// ------------------------------------------------------------------------------- // Copyright (c)2000 Apache Software Foundation // ------------------------------------------------------------------------------- package org.apache.ant; import java.io.*; import java.net.*; import java.util.*; import java.util.zip.*; /** * Manager of tasks and all things related to tasks. Tasks can be found in a * wide number of locations -- and most of these locations require class loading * help. As well, new nodes on the task search path may be added at any time. * When these are added, new tasks should be scanned for. * * @author James Duncan Davidson (duncan@apache.org) */ public class TaskManager { // ----------------------------------------------------------------- // PRIVATE DATA MEMBERS // ----------------------------------------------------------------- /** * FrontEnd that this TaskManager can communicate through. */ private AntFrontEnd frontEnd; /** * Data structure where all the Class definition for all known tasks are * held. */ private Hashtable taskClasses = new Hashtable(); /** * Data structure that holds all the nodes where tasks are picked up from. */ private Vector taskPathNodes = new Vector(); // ----------------------------------------------------------------- // CONSTRUCTORS // ----------------------------------------------------------------- /** * Creates a new TaskManager. */ TaskManager(AntFrontEnd frontEnd) { this.frontEnd = frontEnd; } // ----------------------------------------------------------------- // PUBLIC METHODS // ----------------------------------------------------------------- /** * Adds a node to the task path */ public void addTaskPathNode(File file) throws AntException { taskPathNodes.addElement(file); processTaskPathNode(file); } // ----------------------------------------------------------------- // PACKAGE METHODS // ----------------------------------------------------------------- /** * */ AbstractTask getTaskInstance(String taskName) throws AntException { Class clazz = (Class)taskClasses.get(taskName); try { return (AbstractTask)clazz.newInstance(); } catch (Exception e) { String msg = "Can't instantiate task: " + taskName; AntException ae = new AntException(msg, e); throw ae; } } // ----------------------------------------------------------------- // PRIVATE METHODS // ----------------------------------------------------------------- /** * Returns an enum of the task names that are defined in a given * properties file. */ private Enumeration getTaskNames(Properties props) { Vector v = new Vector(); String s = props.getProperty("tasks"); StringTokenizer tok = new StringTokenizer(s, ",", false); while (tok.hasMoreTokens()) { String taskName = tok.nextToken().trim(); v.addElement(taskName); } return v.elements(); } /** * Processes a directory to get class defintions from it */ private void processDir(File dir) { frontEnd.writeMessage("Scanning " + dir + " for tasks", AntFrontEnd.MSG_LEVEL_LOW); File file = new File(dir, "taskdef.properties"); if (file.exists()) { try { InputStream in = new FileInputStream(file); Properties props = new Properties(); props.load(in); in.close(); Enumeration enum = getTaskNames(props); while (enum.hasMoreElements()) { String taskName = (String)enum.nextElement(); String taskClass = props.getProperty("task." + taskName + ".class"); URLClassLoader loader = new URLClassLoader(new URL[] {dir.toURL()}); try { Class clazz = loader.loadClass(taskClass); frontEnd.writeMessage("Got Task: " + taskName + clazz, AntFrontEnd.MSG_LEVEL_LOW); taskClasses.put(taskName, clazz); } catch (ClassNotFoundException cnfe) { System.out.println("Couldn't load task: " + taskName); System.out.println(cnfe); // XXX error out and stop.... } } } catch (IOException ioe) { System.out.println("Could not work with dir: " + dir); System.out.println(ioe); // XXX error out and stop the build } } } /** * Processes a jar file to get class definitions from it */ private void processJar(File file) throws AntException { frontEnd.writeMessage("Scanning " + file + " for tasks", AntFrontEnd.MSG_LEVEL_LOW); try { ZipFile zipFile = new ZipFile(file); ZipEntry zipEntry = zipFile.getEntry("taskdef.properties"); if (zipEntry != null) { InputStream in = zipFile.getInputStream(zipEntry); Properties props = new Properties(); props.load(in); in.close(); Enumeration enum = getTaskNames(props); while (enum.hasMoreElements()) { String taskName = (String)enum.nextElement(); String taskClass = props.getProperty("task." + taskName + ".class"); if (taskClass == null) { String msg = "No class definition for task " + taskName + "in jar file " + file; throw new AntException(msg); } URLClassLoader loader = new URLClassLoader(new URL[] {file.toURL()}); try { Class clazz = loader.loadClass(taskClass); frontEnd.writeMessage("Got Task: " + taskName + clazz, AntFrontEnd.MSG_LEVEL_LOW); taskClasses.put(taskName, clazz); } catch (ClassNotFoundException cnfe) { System.out.println("Couldn't load task: " + taskName); System.out.println(cnfe); // XXX error out and stop.... } } } // make sure to not leave resources hanging zipFile.close(); } catch (IOException ioe) { System.out.println("Couldn't work with file: " + file); System.out.println(ioe); // XXX need to exception out of here properly to stop things } } /** * Processes a node of the task path searching for task definitions there * and adding them to the list of known tasks */ private void processTaskPathNode(File file) throws AntException { // task path nodes can be any of the following: // * jar file // * directory of jar files // * directory holding class files if(file.isDirectory()) { // first look for all jar files here // second look for a taskdefs.properties here to see if we should // treat the directory as a classpath String[] files = file.list(); for (int i = 0; i < files.length; i++) { if (files[i].endsWith(".jar")) { processJar(new File(file, files[i])); } else if (files[i].equals("taskdef.properties")) { processDir(file); } } } else if (file.getName().endsWith(".jar")) { processJar(file); } } /** * Sets up the taskpath based on the currently running operating * system. In general, the ordering of the taskpath is: user directory, * system directory, and then installation. This allows users or * system admins to override or add tasks. */ private void setUpTaskPath() throws AntException { // 1st, add user's home dir. File f; String userHome = System.getProperty("user.home"); // generic unix f = new File(userHome + ".ant", "tasks"); if (f.exists() && f.isDirectory()) { addTaskPathNode(f); } // macos x f = new File(userHome + "/Library/Ant", "Tasks"); if (f.exists() && f.isDirectory()) { addTaskPathNode(f); } // windows -- todo // 2nd, add system local dir. // generic unix f = new File("/usr/local/ant/tasks"); if (f.exists() && f.isDirectory()) { addTaskPathNode(f); } // macos x f = new File("/Library/Ant/Tasks"); if (f.exists() && f.isDirectory()) { addTaskPathNode(f); } // windows -- todo // 3rd, add installation local dir. //System.out.println("BASE: " + this.getClass().getResource("/")); // XXX ---- not really sure how the best way of getting this info is... // hafta think about it. } }