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