--- /dev/null
+package org.argeo.slc.ant;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.net.InetAddress;\r
+import java.net.UnknownHostException;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+import java.util.UUID;\r
+import java.util.Vector;\r
+\r
+import org.springframework.beans.factory.ListableBeanFactory;\r
+import org.springframework.context.ApplicationContext;\r
+import org.springframework.context.support.AbstractApplicationContext;\r
+import org.springframework.context.support.FileSystemXmlApplicationContext;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.apache.log4j.LogManager;\r
+import org.apache.tools.ant.BuildException;\r
+import org.apache.tools.ant.BuildListener;\r
+import org.apache.tools.ant.Project;\r
+import org.apache.tools.ant.helper.ProjectHelper2;\r
+\r
+import org.argeo.slc.core.process.SlcExecution;\r
+import org.argeo.slc.core.process.SlcExecutionNotifier;\r
+import org.argeo.slc.core.structure.DefaultSRegistry;\r
+import org.argeo.slc.core.structure.SimpleSElement;\r
+import org.argeo.slc.core.structure.StructureRegistry;\r
+import org.argeo.slc.core.structure.tree.TreeSPath;\r
+\r
+/**\r
+ * Custom implementation of an Ant <code>ProjectHelper</code> binding a Spring\r
+ * application context and a structure registry with the Ant project.\r
+ */\r
+public class SlcProjectHelper extends ProjectHelper2 {\r
+ private static Log log;\r
+\r
+ /** The Ant reference to the Spring application context used. */\r
+ public static String REF_ROOT_CONTEXT = "slcApplicationContext";\r
+ /** The Ant reference to the SLC structure registry used. */\r
+ public static String REF_STRUCTURE_REGISTRY = "slcStructureRegistry";\r
+ /** The Ant reference to the <code>TreePath</code> of the current project */\r
+ public static String REF_PROJECT_PATH = "slcProjectPath";\r
+ /**\r
+ * Resource path to the property file listing the SLC specific Ant tasks:\r
+ * /org/argeo/slc/ant/taskdefs.properties\r
+ */\r
+ private static String SLC_TASKDEFS_RESOURCE_PATH = "/org/argeo/slc/ant/taskdefs.properties";\r
+ private static String SLC_TYPEDEFS_RESOURCE_PATH = "/org/argeo/slc/ant/typedefs.properties";\r
+\r
+ protected SlcAntConfig slcAntConfig = null;\r
+\r
+ @Override\r
+ public void parse(Project project, Object source) throws BuildException {\r
+\r
+ if (source instanceof File) {\r
+ File sourceFile = (File) source;\r
+ // Reset basedir property, in order to avoid base dir override when\r
+ // running in Maven\r
+ project.setProperty("basedir", sourceFile.getParentFile()\r
+ .getAbsolutePath());\r
+ }\r
+\r
+ if (slcAntConfig != null) {\r
+ // Config already initialized (probably import), only parse\r
+ super.parse(project, source);\r
+ return;\r
+ }\r
+\r
+ // Initialize config\r
+ slcAntConfig = new SlcAntConfig();\r
+\r
+ if (!slcAntConfig.initProject(project)) {\r
+ // not SLC compatible, do normal Ant\r
+ super.parse(project, source);\r
+ return;\r
+ }\r
+\r
+ if (log == null) {\r
+ // log4j is initialized only now\r
+ log = LogFactory.getLog(SlcProjectHelper.class);\r
+ }\r
+\r
+ if (log.isDebugEnabled())\r
+ log.debug("SLC properties are set, starting initialization for "\r
+ + source + " (projectHelper=" + this + ")");\r
+\r
+ beforeParsing(project);\r
+\r
+ // Calls the underlying implementation to do the actual work\r
+ super.parse(project, source);\r
+\r
+ afterParsing(project);\r
+ }\r
+\r
+ /**\r
+ * Performs operations after config initialization and before Ant file\r
+ * parsing. Performed only once when the main project file is parsed. Should\r
+ * be called by overriding methods.\r
+ */\r
+ protected void beforeParsing(Project project) {\r
+ // Init Spring application context\r
+ initSpringContext(project);\r
+\r
+ // Init structure registry\r
+ DefaultSRegistry registry = new DefaultSRegistry();\r
+ project.addReference(REF_STRUCTURE_REGISTRY, registry);\r
+ }\r
+\r
+ /**\r
+ * Performs operations after parsing of the main file. Called only once (not\r
+ * for imports).\r
+ */\r
+ protected void afterParsing(Project project) {\r
+ // Creates structure root\r
+ registerProjectAndParents(project, slcAntConfig);\r
+ addCustomTaskAndTypes(project);\r
+ }\r
+\r
+ /** Creates the tree-based structure for this project. */\r
+ private void registerProjectAndParents(Project project,\r
+ SlcAntConfig slcAntConfig) {\r
+ StructureRegistry<TreeSPath> registry = (StructureRegistry<TreeSPath>) project\r
+ .getReference(REF_STRUCTURE_REGISTRY);\r
+ File rootDir = new File(project\r
+ .getUserProperty(SlcAntConfig.ROOT_DIR_PROPERTY))\r
+ .getAbsoluteFile();\r
+ File baseDir = project.getBaseDir().getAbsoluteFile();\r
+ List<File> dirs = new Vector<File>();\r
+ File currentDir = baseDir;\r
+ do {\r
+ dirs.add(currentDir);\r
+ currentDir = currentDir.getParentFile();\r
+ if (log.isTraceEnabled())\r
+ log.trace("List " + currentDir);\r
+ } while (!currentDir.equals(rootDir.getParentFile()));\r
+\r
+ // first path is root dir (because of previous algorithm)\r
+ TreeSPath currPath = TreeSPath.createRootPath(rootDir.getName());\r
+ for (int i = dirs.size() - 1; i >= 0; i--) {\r
+ File dir = dirs.get(i);\r
+\r
+ // retrieves description for this path\r
+ final String description;\r
+ if (i == 0) {// project itself\r
+ description = project.getDescription() != null\r
+ && !project.getDescription().equals("") ? project\r
+ .getDescription() : project.getName() != null ? project\r
+ .getName() : slcAntConfig.getDescriptionForDir(dir);\r
+ } else {\r
+ description = slcAntConfig.getDescriptionForDir(dir);\r
+ if (log.isTraceEnabled())\r
+ log.trace("Dir desc " + i + "/" + dirs.size() + ": "\r
+ + description);\r
+ }\r
+ SimpleSElement element = new SimpleSElement(description);\r
+\r
+ // creates and register path\r
+ if (!dir.equals(rootDir)) {// already set\r
+ currPath = currPath.createChild(dir.getName());\r
+ }\r
+ registry.register(currPath, element);\r
+ }\r
+ project.addReference(REF_PROJECT_PATH, currPath);\r
+ }\r
+\r
+ /** Gets the path of a project (root). */\r
+ // private static TreeSPath getProjectPath(Project project) {\r
+ // return (TreeSPath) project.getReference(REF_PROJECT_PATH);\r
+ // }\r
+ /** Initializes the Spring application context. */\r
+ private void initSpringContext(Project project) {\r
+ System.getProperties().putAll((Map<?, ?>) project.getProperties());\r
+ String acPath = project\r
+ .getUserProperty(SlcAntConfig.APPLICATION_CONTEXT_PROPERTY);\r
+ if (log.isDebugEnabled())\r
+ log.debug("Loading Spring application context from " + acPath);\r
+ // FIXME: workaround to the removal of leading '/' by Spring\r
+ // use URL instead?\r
+ AbstractApplicationContext context = new FileSystemXmlApplicationContext(\r
+ '/' + acPath);\r
+ context.registerShutdownHook();\r
+ project.addReference(REF_ROOT_CONTEXT, context);\r
+\r
+ createAndRegisterSlcExecution(project);\r
+ // Add build listeners declared in Spring context\r
+ // Map<String, BuildListener> listeners = context.getBeansOfType(\r
+ // BuildListener.class, false, true);\r
+ // for (BuildListener listener : listeners.values()) {\r
+ // project.addBuildListener(listener);\r
+ // }\r
+ }\r
+\r
+ /** Loads the SLC specific Ant tasks. */\r
+ protected static void addCustomTaskAndTypes(Project project) {\r
+ Properties taskdefs = getDefs(project, SLC_TASKDEFS_RESOURCE_PATH);\r
+ for (Object o : taskdefs.keySet()) {\r
+ String name = o.toString();\r
+ try {\r
+ project.addTaskDefinition(name, Class.forName(taskdefs\r
+ .getProperty(name)));\r
+ } catch (ClassNotFoundException e) {\r
+ log.error("Unknown class for task " + name, e);\r
+ }\r
+ }\r
+ Properties typedefs = getDefs(project, SLC_TYPEDEFS_RESOURCE_PATH);\r
+ for (Object o : typedefs.keySet()) {\r
+ String name = o.toString();\r
+ try {\r
+ project.addDataTypeDefinition(name, Class.forName(typedefs\r
+ .getProperty(name)));\r
+ } catch (ClassNotFoundException e) {\r
+ log.error("Unknown class for type " + name, e);\r
+ }\r
+ }\r
+ }\r
+\r
+ private static Properties getDefs(Project project, String path) {\r
+ Properties defs = new Properties();\r
+ try {\r
+ InputStream in = project.getClass().getResourceAsStream(path);\r
+ defs.load(in);\r
+ in.close();\r
+ } catch (IOException e) {\r
+ throw new SlcAntException("Cannot load task definitions", e);\r
+ }\r
+ return defs;\r
+ }\r
+\r
+ protected static void createAndRegisterSlcExecution(Project project) {\r
+ SlcExecution slcExecution = new SlcExecution();\r
+ slcExecution.setUuid(UUID.randomUUID().toString());\r
+ try {\r
+ slcExecution.setHost(InetAddress.getLocalHost().getHostName());\r
+ } catch (UnknownHostException e) {\r
+ slcExecution.setHost(SlcExecution.UNKOWN_HOST);\r
+ }\r
+\r
+ if (project.getReference(SlcProjectHelper.REF_ROOT_CONTEXT) != null) {\r
+ slcExecution.setType(SlcExecutionBuildListener.SLC_ANT_TYPE);\r
+ } else {\r
+ slcExecution.setType(SlcExecutionBuildListener.ANT_TYPE);\r
+ }\r
+\r
+ slcExecution.setUser(System.getProperty("user.name"));\r
+ slcExecution.setStatus(SlcExecution.STATUS_RUNNING);\r
+ slcExecution.getAttributes().put("ant.file",\r
+ project.getProperty("ant.file"));\r
+\r
+ project.addReference(SlcExecutionBuildListener.REF_SLC_EXECUTION,\r
+ slcExecution);\r
+\r
+ // Add build listeners declared in Spring context\r
+ Map<String, ProjectRelatedBuildListener> listeners = ((ListableBeanFactory) project\r
+ .getReference(REF_ROOT_CONTEXT)).getBeansOfType(\r
+ ProjectRelatedBuildListener.class, false, true);\r
+ for (ProjectRelatedBuildListener listener : listeners.values()) {\r
+ listener.init(project);\r
+ project.addBuildListener(listener);\r
+ }\r
+\r
+ }\r
+}\r