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.services.codegeneration.genericgenerator;
016:
017: import java.io.File;
018: import java.io.IOException;
019: import java.net.MalformedURLException;
020: import java.net.URL;
021: import java.net.URLConnection;
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.Hashtable;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.StringTokenizer;
028:
029: import org.apache.log4j.Logger;
030:
031: import com.metaboss.enterprise.bs.BSException;
032: import com.metaboss.enterprise.bs.BSIllegalArgumentException;
033: import com.metaboss.enterprise.bs.BSUnexpectedProgramConditionException;
034: import com.metaboss.sdlctools.frameworks.generation.pluggable.Generator;
035: import com.metaboss.sdlctools.services.codegeneration.BSGenericGenerator;
036: import com.metaboss.util.DirectoryUtils;
037: import com.metaboss.util.StringUtils;
038: import com.metaboss.util.ZipUtils;
039:
040: /** Default implementation for the generic generator */
041: public class BSGenericGeneratorImpl implements BSGenericGenerator {
042: private static Logger sLogger = Logger
043: .getLogger(BSGenericGeneratorImpl.class);
044: private Hashtable mEnvironment = null;
045: // Loaded at first attempt. The list of directories to have a look for the dynamic geenrators
046: private static String[] sGenerationPluginsPath = null;
047:
048: public BSGenericGeneratorImpl(Hashtable pEnvironment) {
049: mEnvironment = pEnvironment;
050: }
051:
052: /** Generates any number of any kinds of files for the default model dictated by the generation plan with the given name.
053: * dictated by the generation plan with the given name. Returns the byte array containing the zipped up source.
054: * This version of the method will expect the default model to be the instance of the MetaBoss Meta Model and
055: * will use the element with the specified ref as the root element for generation
056: * @param pGenerationPlanName the logical name of the generation plan to invoke
057: * @param pRootModelElements the collection of RefBaseObjects to use as the root elements when processing the generation plan
058: * @param pGenerationContext simple map of the values comprising generation context
059: * @return byte array containing zip of the generated files or null if nothing has been generated */
060: public byte[] generateZipAsPerPlan(String pGenerationPlanName,
061: Collection pRootModelElements, Map pGenerationContext)
062: throws BSException {
063: try {
064: String lGenerationDirectoryPath = DirectoryUtils
065: .getUniqueTempDirectoryAbsolutePath();
066: try {
067: sLogger.info("Executing '" + pGenerationPlanName
068: + "' generation plan.");
069: URLConnection lPlanConnection = findPlanLocation(pGenerationPlanName);
070: if (lPlanConnection == null)
071: throw new BSIllegalArgumentException(
072: "Generation plugin '"
073: + pGenerationPlanName
074: + "' not found after trying following locations : "
075: + StringUtils
076: .combine(
077: getPossiblePlanLocations(pGenerationPlanName),
078: ","));
079: // Grab the name for the temporary directory where generation will take place
080: long lFileCount = Generator.doGenerationForElements(
081: lGenerationDirectoryPath, lPlanConnection,
082: pRootModelElements, pGenerationContext);
083: if (lFileCount == 0) {
084: sLogger
085: .info("No files has been created as the result of executing '"
086: + pGenerationPlanName
087: + "' generation plan.");
088: return null;
089: } else {
090: if (lFileCount == 1)
091: sLogger
092: .info("Created one file as the result of executing '"
093: + pGenerationPlanName
094: + "' generation plan.");
095: else
096: sLogger.info("Created "
097: + Long.toString(lFileCount)
098: + " files as the result of executing '"
099: + pGenerationPlanName
100: + "' generation plan.");
101: // Now zip up and return the results
102: return ZipUtils
103: .zipUpDirectory(lGenerationDirectoryPath);
104: }
105: } finally {
106: DirectoryUtils.deleteDirectoryIfExists(new File(
107: lGenerationDirectoryPath));
108: }
109: } catch (IOException e) {
110: throw new BSUnexpectedProgramConditionException(
111: "Had problem accessing internally created directory or file.",
112: e);
113: }
114: }
115:
116: /** Generates any number of any kinds of files for the required list of elements dictated by the generation plan with the given name.
117: * dictated by the generation plan with the given name. Returns the byte array containing the zipped up source.
118: * This version of the method will expect the default model to be the instance of the MetaBoss Meta Model and
119: * will use the element with the specified ref as the root element for generation
120: * @param pGenerationPlanName the logical name of the generation plan to invoke
121: * @param pMetaBossModelElementRef the reference of the MetaBoss model element, which must exist in the default model
122: * @param pGenerationContext simple map of the values comprising generation context
123: * @return byte array containing zip of the generated files or null if nothing has been generated */
124: public byte[] generateZipAsPerPlan(String pGenerationPlanName,
125: Map pGenerationContext) throws BSException {
126: try {
127: String lGenerationDirectoryPath = DirectoryUtils
128: .getUniqueTempDirectoryAbsolutePath();
129: try {
130: sLogger.info("Executing '" + pGenerationPlanName
131: + "' generation plan.");
132: URLConnection lPlanConnection = findPlanLocation(pGenerationPlanName);
133: if (lPlanConnection == null)
134: throw new BSIllegalArgumentException(
135: "Generation plugin '"
136: + pGenerationPlanName
137: + "' not found after trying following locations : "
138: + StringUtils
139: .combine(
140: getPossiblePlanLocations(pGenerationPlanName),
141: ","));
142: // Grab the name for the temporary directory where generation will take place
143: long lFileCount = Generator
144: .doGenerationForDefaultModel(
145: lGenerationDirectoryPath,
146: lPlanConnection, pGenerationContext);
147: if (lFileCount == 0) {
148: sLogger
149: .info("No files has been created as the result of executing '"
150: + pGenerationPlanName
151: + "' generation plan.");
152: return null;
153: } else {
154: if (lFileCount == 1)
155: sLogger
156: .info("Created one file as the result of executing '"
157: + pGenerationPlanName
158: + "' generation plan.");
159: else
160: sLogger.info("Created "
161: + Long.toString(lFileCount)
162: + " files as the result of executing '"
163: + pGenerationPlanName
164: + "' generation plan.");
165: // Now zip up and return the results
166: return ZipUtils
167: .zipUpDirectory(lGenerationDirectoryPath);
168: }
169: } finally {
170: DirectoryUtils.deleteDirectoryIfExists(new File(
171: lGenerationDirectoryPath));
172: }
173: } catch (IOException e) {
174: throw new BSUnexpectedProgramConditionException(
175: "Had problem accessing internally created directory or file.",
176: e);
177: }
178: }
179:
180: /* Generates any number of any kinds of files for the default model dictated by the generation plan
181: * with the given name. */
182: public void generateDirectoryAsPerPlan(
183: String pGenerationDirectoryPath,
184: String pGenerationPlanName, Map pGenerationContext)
185: throws BSException {
186: sLogger.info("Generating contents of the "
187: + pGenerationDirectoryPath + " in accordance with '"
188: + pGenerationPlanName + "' generation plan...");
189: URLConnection lPlanConnection = findPlanLocation(pGenerationPlanName);
190: if (lPlanConnection == null)
191: throw new BSIllegalArgumentException(
192: "Generation plugin '"
193: + pGenerationPlanName
194: + "' not found after trying following locations : "
195: + StringUtils
196: .combine(
197: getPossiblePlanLocations(pGenerationPlanName),
198: ","));
199: long lFileCount = Generator.doGenerationForDefaultModel(
200: pGenerationDirectoryPath, lPlanConnection,
201: pGenerationContext);
202: if (lFileCount == 0)
203: sLogger
204: .info("No files has been created as the result of executing '"
205: + pGenerationPlanName
206: + "' generation plan.");
207: else if (lFileCount == 1)
208: sLogger
209: .info("Created one file as the result of executing '"
210: + pGenerationPlanName
211: + "' generation plan.");
212: else
213: sLogger.info("Created " + Long.toString(lFileCount)
214: + " files as the result of executing '"
215: + pGenerationPlanName + "' generation plan.");
216: }
217:
218: /* Generates any number of any kinds of files for the required list of objects dictated by the generation plan with the given name. */
219: public void generateDirectoryAsPerPlan(
220: String pGenerationDirectoryPath,
221: String pGenerationPlanName, Collection pRootModelElements,
222: Map pGenerationContext) throws BSException {
223: sLogger.info("Generating contents of the "
224: + pGenerationDirectoryPath + " in accordance with '"
225: + pGenerationPlanName + "' generation plan...");
226: URLConnection lPlanConnection = findPlanLocation(pGenerationPlanName);
227: if (lPlanConnection == null)
228: throw new BSIllegalArgumentException(
229: "Generation plugin '"
230: + pGenerationPlanName
231: + "' not found after trying following locations : "
232: + StringUtils
233: .combine(
234: getPossiblePlanLocations(pGenerationPlanName),
235: ","));
236: long lFileCount = Generator.doGenerationForElements(
237: pGenerationDirectoryPath, lPlanConnection,
238: pRootModelElements, pGenerationContext);
239: if (lFileCount == 0)
240: sLogger
241: .info("No files has been created as the result of executing '"
242: + pGenerationPlanName
243: + "' generation plan.");
244: else if (lFileCount == 1)
245: sLogger
246: .info("Created one file as the result of executing '"
247: + pGenerationPlanName
248: + "' generation plan.");
249: else
250: sLogger.info("Created " + Long.toString(lFileCount)
251: + " files as the result of executing '"
252: + pGenerationPlanName + "' generation plan.");
253: }
254:
255: /** Generates any number of any kinds of files for the given element from the default model
256: * dictated by the generation plan with the given name. Returns the byte array containing the zipped up source.
257: * This version of the method will expect the default model to be the instance of the MetaBoss Meta Model and
258: * will use the element with the specified ref as the root element for generation
259: * @param pGenerationPlanName the logical name of the generation plan to invoke
260: * @param pMetaBossModelElementRef the reference of the MetaBoss model element, which must exist in the default model
261: * @param pGenerationContext simple map of the values comprising generation context
262: * @return byte array containing zip of the generated files or null if nothing has been generated */
263: public byte[] generateZipAsPerPlanForMetaBossModelElement(
264: String pGenerationPlanName,
265: String pMetaBossModelElementRef,
266: java.util.Map pGenerationContext) throws BSException {
267: try {
268: String lGenerationDirectoryPath = DirectoryUtils
269: .getUniqueTempDirectoryAbsolutePath();
270: try {
271: sLogger.info("Executing '" + pGenerationPlanName
272: + "' generation plan for "
273: + pMetaBossModelElementRef + " ModelElement.");
274: URLConnection lPlanConnection = findPlanLocation(pGenerationPlanName);
275: if (lPlanConnection == null)
276: throw new BSIllegalArgumentException(
277: "Generation plugin '"
278: + pGenerationPlanName
279: + "' not found after trying following locations : "
280: + StringUtils
281: .combine(
282: getPossiblePlanLocations(pGenerationPlanName),
283: ","));
284: // Grab the name for the temporary directory where generation will take place
285: long lFileCount = Generator
286: .doGenerationForDefaultMetaBossModelElement(
287: lGenerationDirectoryPath,
288: lPlanConnection,
289: pMetaBossModelElementRef,
290: pGenerationContext);
291: if (lFileCount == 0) {
292: sLogger
293: .info("No files has been created as the result of executing '"
294: + pGenerationPlanName
295: + "' generation plan for "
296: + pMetaBossModelElementRef
297: + " ModelElement.");
298: return null;
299: } else {
300: if (lFileCount == 1)
301: sLogger
302: .info("Created one file as the result of executing '"
303: + pGenerationPlanName
304: + "' generation plan for "
305: + pMetaBossModelElementRef
306: + " ModelElement.");
307: else
308: sLogger.info("Created "
309: + Long.toString(lFileCount)
310: + " files as the result of executing '"
311: + pGenerationPlanName
312: + "' generation plan for "
313: + pMetaBossModelElementRef
314: + " ModelElement.");
315: // Now zip up and return the results
316: return ZipUtils
317: .zipUpDirectory(lGenerationDirectoryPath);
318: }
319: } finally {
320: DirectoryUtils.deleteDirectoryIfExists(new File(
321: lGenerationDirectoryPath));
322: }
323: } catch (IOException e) {
324: throw new BSUnexpectedProgramConditionException(
325: "Had problem accessing internally created directory or file.",
326: e);
327: }
328: }
329:
330: /** Generates any number of any kinds of files for the given element from the default model
331: * dictated by the generation plan with the given name. The subdirectory with the same name as the specified plan name must exist on the
332: * path specified in com.metaboss.generation.PluginPath property which is usually set in the
333: * codegeneration.properties config file. */
334: public void generateDirectoryAsPerPlanForMetaBossModelElement(
335: String pGenerationDirectoryPath,
336: String pGenerationPlanName,
337: String pMetaBossModelElementRef,
338: java.util.Map pGenerationContext) throws BSException {
339: sLogger.info("Running '" + pGenerationPlanName
340: + "' generation plan with " + pMetaBossModelElementRef
341: + " ModelElement targeting " + pGenerationDirectoryPath
342: + " directory...");
343: URLConnection lPlanConnection = findPlanLocation(pGenerationPlanName);
344:
345: if (lPlanConnection == null)
346: throw new BSIllegalArgumentException(
347: "Generation plugin '"
348: + pGenerationPlanName
349: + "' not found after trying following locations : "
350: + StringUtils
351: .combine(
352: getPossiblePlanLocations(pGenerationPlanName),
353: ","));
354: sLogger.info("Determined location for the '"
355: + pGenerationPlanName + "' generation plan as "
356: + lPlanConnection.getURL().toString());
357: long lFileCount = Generator
358: .doGenerationForDefaultMetaBossModelElement(
359: pGenerationDirectoryPath, lPlanConnection,
360: pMetaBossModelElementRef, pGenerationContext);
361: if (lFileCount == 0)
362: sLogger
363: .info("No files has been created as the result of executing '"
364: + pGenerationPlanName
365: + "' generation plan.");
366: else if (lFileCount == 1)
367: sLogger
368: .info("Created one file as the result of executing '"
369: + pGenerationPlanName
370: + "' generation plan.");
371: else
372: sLogger.info("Created " + Long.toString(lFileCount)
373: + " files as the result of executing '"
374: + pGenerationPlanName + "' generation plan.");
375: }
376:
377: // Helper. Finds the most prioritable plan location out of the available ones
378: private static URLConnection findPlanLocation(
379: String pGenerationPlanName) throws BSException {
380: try {
381: String[] lPossibleLocations = getPossiblePlanLocations(pGenerationPlanName);
382: for (int i = 0; i < lPossibleLocations.length; i++) {
383: URL lURL = new URL(lPossibleLocations[i]);
384: try {
385: sLogger
386: .debug("Trying " + lURL.toString()
387: + " as possible location of the "
388: + pGenerationPlanName
389: + " generation plan.");
390: URLConnection lConnection = lURL.openConnection();
391: if (lConnection != null) {
392: // This really tests if resource is there
393: lConnection.connect();
394: return lConnection;
395: }
396: } catch (IOException e) {
397: if (i < (lPossibleLocations.length - 1))
398: sLogger
399: .debug("URL "
400: + lURL.toString()
401: + " is not accessible. Will try the next one.");
402: else
403: sLogger.debug("URL " + lURL.toString()
404: + " is not accessible.");
405: }
406: }
407: return null;
408: } catch (MalformedURLException e) {
409: throw new BSUnexpectedProgramConditionException(
410: "Internally generated URL appears to be invalid", e);
411: }
412: }
413:
414: // Helper. Generates possible locations for the specified plugin
415: private static String[] getPossiblePlanLocations(
416: String pGenerationPlanName) throws BSException {
417: String[] lPluginsPath = getGenerationPluginsPath();
418: if (lPluginsPath.length == 0)
419: return new String[0];
420: // Plan name is a dot separated (like a java package)
421: String[] lNameParts = pGenerationPlanName.split("\\.");
422: if (lNameParts.length == 0)
423: return new String[0];
424: List lPossiblePlanLocations = new ArrayList();
425: // Plugin directories are ordered in descending order of importance
426: for (int lPluginPathIndex = 0; lPluginPathIndex < lPluginsPath.length; lPluginPathIndex++) {
427: String lPluginPath = lPluginsPath[lPluginPathIndex];
428: // Iterate over the name parts from the back and build the list of the expected names
429: for (int lSplitIndex = lNameParts.length; lSplitIndex >= 0; lSplitIndex--) {
430: // Now try subdirectory location
431: // We will use following URLs
432: // file:/c:/..../x.y.z/generationdef.xml
433: // file:/c:/..../x.y/z/generationdef.xml
434: // file:/c:/..../x/y/z/generationdef.xml
435:
436: String lSubdirectory = StringUtils.combine(lNameParts,
437: ".", 0, lSplitIndex);
438: String lFileBase = StringUtils.combine(lNameParts,
439: File.separator, lSplitIndex, lNameParts.length);
440: // First in the priority order is the file
441: {
442: StringBuffer lPlanLocationURL = new StringBuffer(
443: "file:/");
444: lPlanLocationURL.append(lPluginPath);
445: if (lSubdirectory.length() > 0) {
446: lPlanLocationURL.append(File.separator);
447: lPlanLocationURL.append(lSubdirectory);
448: }
449: lPlanLocationURL.append(File.separator);
450: if (lFileBase.length() > 0) {
451: lPlanLocationURL.append(lFileBase);
452: lPlanLocationURL.append(File.separator);
453: }
454: lPlanLocationURL.append("generationdef.xml");
455: lPossiblePlanLocations.add(lPlanLocationURL
456: .toString());
457: }
458: // Now try in-jar locations everywhere, but not the deepest possible level
459: // Sample of the in-jar url is jar:file:/C:/JavaLib/netbeansmdr-20040212/lib/uml-1.4.jar!/org/omg/uml/resources/01-02-15_Diff.xml
460: // We will use following URLs
461: // jar:file:/c:/..../x.y.z.jar!/generationdef.xml
462: // jar:file:/c:/..../x.y.jar!/z/generationdef.xml
463: // jar:file:/c:/..../x.jar!/y/z/generationdef.xml
464:
465: if (lSplitIndex < lNameParts.length) {
466: StringBuffer lInJarPlanLocationURLStart = new StringBuffer(
467: "jar:file:/");
468: lInJarPlanLocationURLStart.append(lPluginPath);
469: if (lSubdirectory.length() > 0) {
470: lInJarPlanLocationURLStart
471: .append(File.separator);
472: lInJarPlanLocationURLStart
473: .append(lSubdirectory);
474: }
475: lInJarPlanLocationURLStart.append(File.separator);
476: // Iterate over the name parts from the back and build the list of the expected names
477: for (int lInJarSplitIndex = lNameParts.length; lInJarSplitIndex > lSplitIndex; lInJarSplitIndex--) {
478: String lJarFileName = StringUtils.combine(
479: lNameParts, ".", lSplitIndex,
480: lInJarSplitIndex);
481: String lInJarSubdirectory = StringUtils
482: .combine(lNameParts, "/",
483: lInJarSplitIndex,
484: lNameParts.length);
485: StringBuffer lInJarPlanLocationURL = new StringBuffer(
486: lInJarPlanLocationURLStart.toString());
487: lInJarPlanLocationURL.append(lJarFileName);
488: lInJarPlanLocationURL.append(".jar!/");
489: if (lInJarSubdirectory.length() > 0) {
490: lInJarPlanLocationURL
491: .append(lInJarSubdirectory);
492: lInJarPlanLocationURL.append("/");
493: }
494: lInJarPlanLocationURL
495: .append("generationdef.xml");
496: lPossiblePlanLocations
497: .add(lInJarPlanLocationURL.toString());
498: }
499: }
500: }
501: }
502: return (String[]) lPossiblePlanLocations
503: .toArray(new String[lPossiblePlanLocations.size()]);
504: }
505:
506: // Helper. Returns path of the generator plugins. Most prioritable element is at zero index.
507: private static String[] getGenerationPluginsPath() {
508: if (sGenerationPluginsPath == null) {
509: ArrayList lPathList = new ArrayList();
510: // See if we can load the implementation jar from component implementation path
511: // Only able to use dynamic loading if component imlementation path property is set
512: String lGenerationPluginsPath = System
513: .getProperty("com.metaboss.generation.PluginsPath");
514: if (lGenerationPluginsPath != null
515: && lGenerationPluginsPath.length() > 0) {
516: StringTokenizer lPathTokenizer = new StringTokenizer(
517: lGenerationPluginsPath, ";", false);
518: while (lPathTokenizer.hasMoreTokens()) {
519: File lFile = new File(lPathTokenizer.nextToken());
520: lPathList.add(lFile.getAbsolutePath());
521: }
522:
523: }
524: sGenerationPluginsPath = (String[]) lPathList
525: .toArray(new String[lPathList.size()]);
526: if (sGenerationPluginsPath.length == 0)
527: sLogger
528: .debug("Dynamic loading of generators is switched off because component implementation path is unspecified ('com.metaboss.generation.PluginPath' system property is not present or empty).");
529: }
530: return sGenerationPluginsPath;
531: }
532: }
|