001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.sdlctools.applications.anttasks.builder;
016:
017: import java.io.File;
018: import java.io.InputStream;
019: import java.net.URL;
020: import java.net.URLClassLoader;
021: import java.util.ArrayList;
022: import java.util.Arrays;
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.Enumeration;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Properties;
031: import java.util.Set;
032: import java.util.TreeMap;
033:
034: import org.apache.tools.ant.BuildException;
035: import org.apache.tools.ant.types.Path;
036:
037: import com.metaboss.sdlctools.applications.anttasks.MetaBossModelToolTask;
038: import com.metaboss.sdlctools.applications.anttasks.builder.modules.BusinessDomainsImplementationModuleDefinition;
039: import com.metaboss.sdlctools.applications.anttasks.builder.modules.BusinessServicesDistributionModuleDefinition;
040: import com.metaboss.sdlctools.applications.anttasks.builder.modules.BusinessServicesImplementationModuleDefinition;
041: import com.metaboss.sdlctools.applications.anttasks.builder.modules.BusinessServicesInterfaceModuleDefinition;
042: import com.metaboss.sdlctools.applications.anttasks.builder.modules.DataDictionaryModuleDefinition;
043: import com.metaboss.sdlctools.applications.anttasks.builder.modules.WebServicesServerModuleDefinition;
044: import com.metaboss.sdlctools.applications.anttasks.builder.tools.JarPackagerInvocationDefinition;
045: import com.metaboss.util.DirectoryUtils;
046:
047: /**
048: * The MetaBoss Enterprise Modules builder task.
049: * This task can generate, compile and package any number of modules based on the enterprise model.
050: * It implements Model Driven approach to build where the definition contains information
051: * "what to build" rather than "how to build". Apart from that this task of course requires some
052: * more traditionally looking And attributes defining the build environment.
053: * <p>In addition to atttributes supported by {@link com.metaboss.sdlctools.applications.anttasks.MetaBossModelToolTask MetaBossModelToolTask},
054: * this task supports following attributes:
055: * <table border="1" cellpadding="2" cellspacing="0">
056: * <tr>
057: * <th>Attribute Name</th>
058: * <th>Attribute Description</th>
059: * <th>Required</th>
060: * </tr>
061: * <tr>
062: * <td valign="top">version</td>
063: * <td valign="top">Optional string attribute used to specify the version of the build.
064: * If it is specified, the file name of every module produced by this build will contain
065: * given version expression.
066: * </td>
067: * <td valign="top">No</td>
068: * </tr>
069: * <tr>
070: * <td valign="top">debug</td>
071: * <td valign="top">Optional boolean attribute used to specify whether this build is a debug build.
072: * Default value is 'false', which means that produced modules will not contain debug information.</td>
073: * <td valign="top">No</td>
074: * </tr>
075: * <tr>
076: * <td valign="top">turbobuild</td>
077: * <td valign="top">Optional boolean attribute used to specify whether this build should
078: * aim for a speed (turbobuild='true') or low memory usage (turbobuild='false').
079: * Default value is 'true', which means that the build will attempt to run as fast as posible
080: * which uses more memory.</td>
081: * </td>
082: * <td valign="top">No</td>
083: * </tr>
084: * <tr>
085: * <td valign="top">compileclasspath</td>
086: * <td valign="top">The optional path to use as additional classpath during compilation.
087: * This may be necessary when inserted handcoded implementation classes have compile time dependencies
088: * on other third party jar files.
089: * </td>
090: * <td valign="top">No</td>
091: * </tr>
092: * <tr>
093: * <td valign="top">gendir</td>
094: * <td valign="top">The mandatory directory location where intermediate java source files shall be stored.
095: * Directory will be created if it does not exist. Directory will be cleared of any existing files if it does exists.</td>
096: * <td valign="top">Yes</td>
097: * </tr>
098: * <tr>
099: * <td valign="top">classdir</td>
100: * <td valign="top">The mandatory directory location where intermediate java class files shall be stored.
101: * Directory will be created if it does not exist. Directory will be cleared of any existing files if it does exists.</td>
102: * <td valign="top">Yes</td>
103: * </tr>
104: * <tr>
105: * <td valign="top">libdir</td>
106: * <td valign="top">The mandatory directory location where produced java archives files shall be stored.
107: * Directory will be created if it does not exist</td>
108: * <td valign="top">Yes</td>
109: * </tr>
110: * <tr>
111: * <td valign="top">typessrcdir</td>
112: * <td valign="top">The optional directory location where hand coded data types java source resides.
113: * This directory will be used to search for custom data types source (ie. DataTypes which are not
114: * realised by any of the TypeTemplates.</td>
115: * <td valign="top">No</td>
116: * </tr>
117: * <tr>
118: * <td valign="top">implssrcdir</td>
119: * <td valign="top">The optional directory location where hand coded service implementation java source
120: * resides. This directory will be used to search for business services custom source.</td>
121: * <td valign="top">No</td>
122: * </tr>
123: * <tr>
124: * <td valign="top">srcdir</td>
125: * <td valign="top">The optional directory location where hand coded java source resides.
126: * This directory will be used to search for custom data types and custome service implementations.
127: * Note that if sources for data types and implementations reside in different directories, attributes
128: * 'typessrcdir' and 'implssrcdir' should be used.</td>
129: * <td valign="top">No</td>
130: * </tr>
131: * </table>
132: * <p>Subelements of this task are definitions of the build results. Following subelements are supported:
133: * <UL>
134: * <LI>BusinessServicesInterfaceModule - this element requres builder to produce the module with
135: * external public interfaces to the business servcies layer. It always contains service interfaces, structures and
136: * primitive data types. It may optionally contain code for adapters, including custme generated ones.</LI>
137: * </UL></p>
138: * */
139: public class MetaBossBuilderTask extends MetaBossModelToolTask {
140: // Listeners in order of addition
141: private List mListeners = new ArrayList();
142: // Absolute directory where handcoded source pieces reside
143: private File mTypesSrcDir = null; // DataTypes
144: private File mImplsSrcDir = null; // Service implementations
145: // At this level this should point to absolute directory where generated source should be located
146: private File mGenDir = null;
147: // At this level this should point to absolute directory where compiled classes will be located
148: private File mClassDir = null;
149: // At this level this should point to absolute directory where packaged libraries will be located
150: private File mLibDir = null;
151: // The name of the build version. Can be used by modules when forming names of deliverables
152: private String mBuildVersionToken = null;
153: // The flag to indicate the debug builds (false by default)
154: private boolean mDebugBuild = false;
155: // The classapth to be added to compilation
156: private Path mCompileClasspath = null;
157: // The flag to indicate the turbo builds (true by default)
158: // turbo build incresates the speed of the build, but it demands more resources
159: private boolean mTurboBuild = true;
160: // Contains combined map of all contents of all jars on the classpath
161: // The key in the map is the generator id and the value is a set of
162: // model eleemnt refs which are there. This information may be used by
163: // generate and build tools in order not to spend time generating and compiling extra stuff
164: // Each content entry has generator .... model elelement ref
165: private Map mCompileClasspathContentsMap = new TreeMap();
166:
167: // List of modules to build
168: protected List mModules = new ArrayList();
169: private static Properties sPathProperties = null;
170: private Properties mInvocationProperties = new Properties();
171:
172: /** Default constructor */
173: public MetaBossBuilderTask() {
174: // Builder uses existing model
175: super (true);
176: }
177:
178: /** Adds listener to the internal list */
179: public void addListener(MetaBossBuilderTaskListener pListener) {
180: mListeners.add(pListener);
181: }
182:
183: /** The special creator asking to build compile classpath */
184: public Path createCompileClasspath() {
185: if (mCompileClasspath == null)
186: mCompileClasspath = new Path(getProject());
187: return mCompileClasspath.createPath();
188: }
189:
190: /** The special creator asking to build SystemInterfaceModule */
191: public BusinessServicesInterfaceModuleDefinition createBusinessServicesInterfaceModule() {
192: BusinessServicesInterfaceModuleDefinition lBusinessServicesInterfaceModuleDefinition = new BusinessServicesInterfaceModuleDefinition(
193: this );
194: return lBusinessServicesInterfaceModuleDefinition;
195: }
196:
197: /** The special creator asking to build SystemInterfaceModule */
198: public DataDictionaryModuleDefinition createDataDictionaryModule() {
199: DataDictionaryModuleDefinition lDataDictionaryModuleDefinition = new DataDictionaryModuleDefinition(
200: this );
201: return lDataDictionaryModuleDefinition;
202: }
203:
204: /** The special creator asking to build SystemInterfaceModule */
205: public BusinessServicesDistributionModuleDefinition createBusinessServicesDistributionModule() {
206: BusinessServicesDistributionModuleDefinition lBusinessServicesDistributionModuleDefinition = new BusinessServicesDistributionModuleDefinition(
207: this );
208: return lBusinessServicesDistributionModuleDefinition;
209: }
210:
211: /** The special creator asking to build BusinessServicesImplementationModule */
212: public BusinessServicesImplementationModuleDefinition createBusinessServicesImplementationModule() {
213: BusinessServicesImplementationModuleDefinition lBusinessServicesImplementationModuleDefinition = new BusinessServicesImplementationModuleDefinition(
214: this );
215: return lBusinessServicesImplementationModuleDefinition;
216: }
217:
218: /** The special creator asking to build WebServicesServerModule */
219: public WebServicesServerModuleDefinition createWebServicesServerModule() {
220: WebServicesServerModuleDefinition lWebServicesServerModuleDefinition = new WebServicesServerModuleDefinition(
221: this );
222: return lWebServicesServerModuleDefinition;
223: }
224:
225: /** The special creator asking to build BusinessDomainsImplementationModuleDefinition */
226: public BusinessDomainsImplementationModuleDefinition createBusinessDomainsImplementationModule() {
227: BusinessDomainsImplementationModuleDefinition lBusinessDomainsImplementationModuleDefinition = new BusinessDomainsImplementationModuleDefinition(
228: this );
229: return lBusinessDomainsImplementationModuleDefinition;
230: }
231:
232: // The setter for the optional "version" attribute
233: public void setVersion(String pVersionToken) throws BuildException {
234: mBuildVersionToken = pVersionToken;
235: }
236:
237: // The getter for the optional "version" attribute
238: public String getVersionToken() throws BuildException {
239: return mBuildVersionToken;
240: }
241:
242: /** The setter for the "debug" attribute */
243: public void setDebug(boolean pIsDebug) {
244: mDebugBuild = pIsDebug;
245: }
246:
247: /** The getter for the "debug" attribute */
248: public boolean isDebugBuild() {
249: return mDebugBuild;
250: }
251:
252: /** The setter for the "turbobuild" attribute */
253: public void setTurboBuild(boolean pTurboBuild) {
254: mTurboBuild = pTurboBuild;
255: }
256:
257: /** The getter for the "turbobuild" attribute */
258: public boolean isTurboBuild() {
259: return mTurboBuild;
260: }
261:
262: // The setter for the "compileclasspath" attribute
263: public void setCompileClasspath(Path pCompileClasspath) {
264: if (mCompileClasspath == null)
265: mCompileClasspath = new Path(getProject());
266: mCompileClasspath.add(pCompileClasspath);
267: }
268:
269: // The getter for the optional compile classapth attribute
270: public Path getCompileClasspath() {
271: return mCompileClasspath;
272: }
273:
274: // The setter for the "classdir" attribute
275: public void setClassdir(File pClassDir) {
276: mClassDir = pClassDir;
277: }
278:
279: // The getter for the "classdir" attribute
280: public File getClassDir() throws BuildException {
281: if (mClassDir == null)
282: throw new BuildException(
283: "Missing 'classdir' attribute, which is mandatory for <"
284: + getTaskName() + "> task.");
285: return mClassDir;
286: }
287:
288: /** The setter for the "gendir" attribute */
289: public void setGenDir(File pGenDir) {
290: mGenDir = pGenDir.getAbsoluteFile();
291: }
292:
293: /** The getter for the 'gendir' attribute */
294: public File getGenDir() {
295: if (mGenDir == null)
296: throw new BuildException(
297: "Missing 'gendir' attribute, which is mandatory for <"
298: + getTaskName() + "> task.");
299: return mGenDir;
300: }
301:
302: /** The setter for the "libdir" attribute */
303: public void setLibDir(File pLibDir) {
304: mLibDir = pLibDir.getAbsoluteFile();
305: }
306:
307: /** The getter for the 'libdir' attribute */
308: public File getLibDir() {
309: if (mLibDir == null)
310: throw new BuildException(
311: "Missing 'libdir' attribute, which is mandatory for <"
312: + getTaskName() + "> task.");
313: return mLibDir;
314: }
315:
316: // The setter for al sourcedir attributes together. This will set "typessrcdir" and
317: // "implssrcdir" attributes to the same value
318: public void setSrcDir(File pSrcDir) {
319: mTypesSrcDir = pSrcDir;
320: mImplsSrcDir = pSrcDir;
321: }
322:
323: // The setter for optional "typessrcdir" attribute
324: public void setTypesSrcDir(File pSrcDir) {
325: mTypesSrcDir = pSrcDir;
326: }
327:
328: // The setter for optional "implssrcdir" attribute
329: public void setImplsSrcDir(File pSrcDir) {
330: mImplsSrcDir = pSrcDir;
331: }
332:
333: // The getter for optional "typessrcdir" attribute
334: public File getTypesSrcDir() throws BuildException {
335: return mTypesSrcDir;
336: }
337:
338: // The getter for optional "implssrcdir" attribute
339: public File getImplsSrcDir() throws BuildException {
340: return mImplsSrcDir;
341: }
342:
343: /** Returns read-only collection of the modules contained in this builder */
344: public Collection getModules() {
345: return Collections.unmodifiableCollection(mModules);
346: }
347:
348: // The method executing the tool
349: public void runTool() throws Exception {
350: // Gather the metaboss contents from the jars on the classpath
351: initialiseContentsInformation();
352:
353: // Complete initialisation of module definitions before using them
354: {
355: for (Iterator lModulesIterator = mModules.iterator(); lModulesIterator
356: .hasNext();) {
357: ModuleDefinition lModuleDefinition = (ModuleDefinition) lModulesIterator
358: .next();
359: if (getRootModelElement() != null)
360: lModuleDefinition
361: .setDefaultRootModelElement(getRootModelElement());
362: lModuleDefinition.completeInitialisation();
363: }
364: }
365:
366: // Ensure that the temporary directories exist and that they are empty
367: DirectoryUtils.ensureNewCleanDirectory(getGenDir()
368: .getAbsolutePath());
369: DirectoryUtils.ensureNewCleanDirectory(getClassDir()
370: .getAbsolutePath());
371:
372: // Notify listeners
373: for (Iterator lListenersIterator = mListeners.iterator(); lListenersIterator
374: .hasNext();)
375: ((MetaBossBuilderTaskListener) lListenersIterator.next())
376: .onBuildAboutToStart();
377: // Perform generation
378: {
379: // Build the list of tool invocations to be done
380: List lToBeDoneSteps = new ArrayList();
381: for (Iterator lModulesIterator = mModules.iterator(); lModulesIterator
382: .hasNext();) {
383: ModuleDefinition lModuleDefinition = (ModuleDefinition) lModulesIterator
384: .next();
385: lToBeDoneSteps.addAll(Arrays.asList(lModuleDefinition
386: .getGenerationPlan()));
387: lToBeDoneSteps.addAll(Arrays.asList(lModuleDefinition
388: .getCompilationPlan()));
389: lToBeDoneSteps.addAll(Arrays.asList(lModuleDefinition
390: .getPackagingPlan()));
391: }
392: // Invoke all tools in the row. Look after opportunity not to run same tool invocation for the second time
393: // as well as for then opportunity to combine invocation
394: List lDoneSteps = new ArrayList();
395: int lNumberOfCompletedSteps = 0;
396: int lTotalNumberOfSteps = lToBeDoneSteps.size();
397: ToolInvocationDefinition lCurrentInvocationStep = null;
398: for (Iterator lToolInvocationsIterator = lToBeDoneSteps
399: .iterator(); lToolInvocationsIterator.hasNext();) {
400: ToolInvocationDefinition lInvocationStep = (ToolInvocationDefinition) lToolInvocationsIterator
401: .next();
402:
403: // Notify listeners
404: for (Iterator lListenersIterator = mListeners
405: .iterator(); lListenersIterator.hasNext();)
406: ((MetaBossBuilderTaskListener) lListenersIterator
407: .next()).onBuildProgress(
408: lNumberOfCompletedSteps++,
409: lTotalNumberOfSteps);
410: // See if we have an opportunity to skip execution because it was alredy invoked before
411: if (!lInvocationStep.isExecutingAllOccurrences()) {
412: // Ensure that we have not processed equal object already
413: // Note that we have had some strange troubles with collections contains - so we will do it explicitly
414: boolean lSkipStep = false;
415: for (Iterator lDoneStepsIterator = lDoneSteps
416: .iterator(); lDoneStepsIterator.hasNext();) {
417: if ((lSkipStep = lInvocationStep
418: .equals(lDoneStepsIterator.next())) == true)
419: break;
420: }
421: if (lSkipStep)
422: continue; // Can skip this step
423: }
424: // See if we should and can combine this invocation with the current one
425: if (isTurboBuild()) {
426: if (lCurrentInvocationStep == null) {
427: // This is the first time through - just rememeber the strep
428: lCurrentInvocationStep = lInvocationStep;
429: } else if (lCurrentInvocationStep
430: .canCombine(lInvocationStep)) {
431: // Combine current step with this one and do not do it yet
432: lCurrentInvocationStep = lCurrentInvocationStep
433: .combine(lInvocationStep);
434: } else {
435: // There is a step but we are unable to combine them. Excecute the current one and save the new one
436: lCurrentInvocationStep.invoke();
437: lCurrentInvocationStep = lInvocationStep;
438: }
439: // Since we defer execution - log it at the end
440: // This way if we have executed previous steps - it will not be confusing where the error has occurred
441: getLogger().info(
442: lInvocationStep.getToolInvocationName());
443: } else {
444: // Log prior to excution, so it will not be confusing where the error has occurred
445: getLogger().info(
446: lInvocationStep.getToolInvocationName());
447: // Just invoke the step
448: lInvocationStep.invoke();
449: }
450: // Register this step as done
451: lDoneSteps.add(lInvocationStep);
452: }
453: // Excecute the last step if there is one
454: if (lCurrentInvocationStep != null) {
455: lCurrentInvocationStep.invoke();
456: }
457: }
458: // Notify listeners
459: for (Iterator lListenersIterator = mListeners.iterator(); lListenersIterator
460: .hasNext();)
461: ((MetaBossBuilderTaskListener) lListenersIterator.next())
462: .onBuildComplete();
463: }
464:
465: // Returns true if the code produced by the given generator from the given entry is present on the classpath
466: public boolean isArtefactPresentOnCompileClasspath(
467: String pGeneratorId, String pModelElementRef) {
468: Set lModelElementsSet = (Set) mCompileClasspathContentsMap
469: .get(pGeneratorId);
470: if (lModelElementsSet != null)
471: return lModelElementsSet.contains(pModelElementRef);
472: return false;
473: }
474:
475: // Gather the metaboss contents from the jars on the classpath
476: private void initialiseContentsInformation() throws BuildException {
477: try {
478: if (mCompileClasspath == null)
479: return; // No compile classpath specified
480: getLogger()
481: .verbose(
482: "Analysing manifests of all archives on the compile classpath");
483: String[] lPaths = mCompileClasspath.list();
484: URL[] lURLs = new URL[lPaths.length];
485: for (int i = 0; i < lPaths.length; i++)
486: lURLs[i] = new File(lPaths[i]).getAbsoluteFile()
487: .toURL();
488:
489: URLClassLoader lClassLoader = new URLClassLoader(lURLs);
490: Enumeration lManifestResources = lClassLoader
491: .getResources(JarPackagerInvocationDefinition.CONTENTS_PROPERTIES_FILE_NAME);
492: while (lManifestResources.hasMoreElements()) {
493: URL lResource = (URL) lManifestResources.nextElement();
494: getLogger()
495: .verbose(
496: "Found "
497: + JarPackagerInvocationDefinition.CONTENTS_PROPERTIES_FILE_NAME
498: + " in "
499: + lResource.toExternalForm());
500: InputStream lResourceInputStream = lResource
501: .openStream();
502: Properties lContentsProperties = new Properties();
503: lContentsProperties.load(lResourceInputStream);
504: lResourceInputStream.close();
505: // Read properties and put information in the contents
506: for (Iterator lIter = lContentsProperties.entrySet()
507: .iterator(); lIter.hasNext();) {
508: Map.Entry lContentEntry = (Map.Entry) lIter.next();
509: String lKey = (String) lContentEntry.getKey();
510: // Strip the last number from the key to get generator id
511: int lLastDotIndex = lKey.lastIndexOf(".");
512: if (lLastDotIndex <= 0)
513: throw new BuildException(
514: "Unexpected format of the MetaBoss contents file entry. File:"
515: + lResource.toExternalForm());
516: String lGeneratorId = lKey.substring(0,
517: lLastDotIndex);
518: String lModelElementRef = (String) lContentEntry
519: .getValue();
520: // Put information in the contents
521: Set lModelElementsSet = (Set) mCompileClasspathContentsMap
522: .get(lGeneratorId);
523: if (lModelElementsSet == null)
524: mCompileClasspathContentsMap.put(lGeneratorId,
525: lModelElementsSet = new HashSet());
526: lModelElementsSet.add(lModelElementRef);
527: }
528: }
529: getLogger()
530: .verbose(
531: "Found "
532: + mCompileClasspathContentsMap
533: .size()
534: + " MetaBoss content entries in jars on the compile classpath.");
535: } catch (java.net.MalformedURLException e) {
536: throw new BuildException(
537: "Caught unexpected exception while analysing compile classpath."
538: + e.getMessage());
539: } catch (java.io.IOException e) {
540: throw new BuildException(
541: "Caught unexpected exception while analysing compile classpath."
542: + e.getMessage());
543: }
544: }
545: }
|