/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Header:$ */ package org.apache.beehive.controls.runtime.assembly; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.Set; import java.util.TreeSet; import java.net.URL; import java.net.URLClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.FileSet; import org.apache.beehive.controls.runtime.generator.apt.ControlClientManifest; import org.apache.beehive.controls.api.assembly.ControlAssemblyException; /** * AssembleTask defines a custom ant task to perform control assembly. *

* The core assembly algorithm is documented and implemented in {@link Assembler}. *

* Required attributes:
* moduleDir: path to the root of J2EE module on which to perform assembly.
* srcOutputDir: path to the dir where control assemblers may output source files. * It may be necessary to run additional build steps in order to process such files (for example, * if an assembler outputs Java source code, that code may need to be compiled).
* contextFactoryClassname: fully qualified classname of a factory class that implements * {@link org.apache.beehive.controls.api.assembly.ControlAssemblyContext.Factory}. Typically this * would depend on the type of module on which assembly is being run (EJB, webapp, etc). Different * contexts will expose different APIs to control assemblers (making different descriptors available, * etc). *

* Supported nested elements:
* classpath: specifies the classpath that will be searched for control interfaces/implementations, * control clients and control assemblers.
* fileset: specifies the control client manifests that should be processed by this assembly call.
*

* An example usage of the AssembleTask in an ant build script (build.xml): *

<taskdef name="assemble" classname="org.apache.beehive.controls.runtime.assembly.AssembleTask" classpathref="controls.dependency.path" onerror="report" /> <assemble moduleDir="${build.beans}" srcOutputDir="${build.beansrc}" contextFactoryClassname="org.apache.beehive.controls.runtime.assembly.EJBAssemblyContext$Factory"> <classpath> <path refid="test.classpath"/> <pathelement location="${build.beans}"/> </classpath> <fileset dir="${build.beans}"> <include name="**\*.controls.properties"/> </fileset> </assemble> */ public class AssembleTask extends Task { public AssembleTask() { // do nothing } public void setContextFactoryClassName(String contextFactoryClassName) { _contextFactoryClassName = contextFactoryClassName; } public void setModuleDir( File moduleDir ) { _moduleDir = moduleDir; } public void setModuleName( String moduleName ) { _moduleName = moduleName; } public void setSrcOutputDir( File srcOutputDir ) { _srcOutputDir = srcOutputDir; } public void setBindingFile(File bindingFile) { _bindingFile = bindingFile; } public FileSet createFileset() { _clientManifestFileSet = new FileSet(); return _clientManifestFileSet; } // used to set classpath as an attribute public void setClasspath(Path classpath) { _classPath = new Path(getProject()); _classPath.append(classpath); } // used to set classpath as a nested element public Path createClasspath() { _classPath = new Path(getProject()); return _classPath; } public void execute() { validateAttributeSettings(); if (_clientManifestFileSet == null) { log("No input fileset specified, nothing to do."); return; } // get list of input files as list of ControlRefs files File filesetDir = _clientManifestFileSet.getDir(getProject()); String[] clientManifests = _clientManifestFileSet. getDirectoryScanner(getProject()).getIncludedFiles(); if (clientManifests.length == 0) { log("Input fileset contained no files, nothing to do."); return; } List manifestFiles = new ArrayList(); for ( String mf : clientManifests ) { File f = new File(filesetDir, mf ); if (!f.exists()) { log("File " + f.getAbsolutePath() + " in input fileset does not exist."); continue; } manifestFiles.add(f); } // REVIEW: nested control usage is handled poorly right now. // Need to refine how we pick up control client manifests, especially // for manifests inside control jars (instead of blindly scanning and // including all manifests inside all jars, should base it on actual nested // control usage as analyzed by starting at non-control clients). try { // Build map of control types to assemble by scanning supplied manifests Map controlTypesToImpls = new HashMap(); Map> controlTypesToClients = new HashMap>(); for ( File mf : manifestFiles ) { ControlClientManifest ccmf = new ControlClientManifest( mf ); String controlClient = ccmf.getControlClient(); List controlTypes = ccmf.getControlTypes(); for ( String ct : controlTypes ) { controlTypesToImpls.put( ct, ccmf.getDefaultImpl( ct ) ); Set clients = controlTypesToClients.get( ct ); if (clients == null) { clients = new TreeSet(); controlTypesToClients.put( ct, clients ); } clients.add( controlClient ); } } // Build classloader to do loading // // TODO: The module dir should probably be in the classpath, since it seems reasonable // for assemblers to want access to the classes in the module. String[] classpaths = _classPath == null ? new String[0] : _classPath.list(); ClassLoader cl = buildClassLoader( classpaths, Assembler.class.getClassLoader() ); Assembler.assemble( _moduleDir, _moduleName, _srcOutputDir, _contextFactoryClassName, controlTypesToImpls, controlTypesToClients, cl ); } catch (Exception e) { e.printStackTrace(); throw new BuildException("Assembly failed.", e); } } private void validateAttributeSettings() throws BuildException { if (_contextFactoryClassName == null) throw new BuildException("The contextFactoryClassName attribute must be set"); if (_moduleDir == null) throw new BuildException("The moduleDir attribute must be set"); if (_srcOutputDir == null) throw new BuildException("The srcOutputDir attribute must be set"); } private ClassLoader buildClassLoader( String[] paths, ClassLoader parentCL) throws ControlAssemblyException { List list = new ArrayList(); for (int i=0; i