001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049: /*
050: * AppBuilder.java
051: *
052: * Created on March 30, 2003, 1:57 PM
053: */
054:
055: package org.jaffa.tools.patternmetaengine;
056:
057: import java.io.File;
058: import java.io.FileInputStream;
059: import java.io.FileNotFoundException;
060: import java.io.FileOutputStream;
061: import java.io.IOException;
062: import java.net.MalformedURLException;
063: import java.net.URL;
064: import java.util.ArrayList;
065: import java.util.Iterator;
066: import java.util.List;
067: import java.util.regex.Matcher;
068: import java.util.regex.Pattern;
069: import java.util.regex.PatternSyntaxException;
070: import javax.xml.bind.JAXBContext;
071: import javax.xml.bind.UnmarshalException;
072: import javax.xml.bind.Unmarshaller;
073: import org.apache.log4j.Logger;
074: import org.jaffa.patterns.library.domain_creator_1_1.domain.Root;
075: import org.jaffa.tools.patternmetaengine.domain.ApplicationBuilder;
076: import org.jaffa.tools.patternmetaengine.domain.Module;
077: import org.jaffa.util.ListProperties;
078: import org.jaffa.util.StringHelper;
079: import org.jaffa.util.URLHelper;
080: import org.jaffa.util.XmlHelper;
081:
082: /** This is the core utility that will generate a complete CRUD application from
083: * a domain object model.
084: * <ol>
085: * <li>. The process is as follows, all domain object are read in from the list of
086: * domain object paths.
087: * <li>. The app builder descriptor list the modules that should be generated.
088: * <li>. The first pass at building the components will build each component type
089: * for all modules.
090: * <li>. Currently this means all finders, the viewers, then maintenace, then lookups.
091: * (This order is controlled by the static variables, so it can be changed later)
092: * <li>. A second pass though all the components will be made in the above sequence.
093: * This second pass is used to link components together that were generated in the
094: * inital pass.
095: * <li>. All component meta data files will then be written out.
096: * </ol>
097: * Note: During both passes for each module, the label will be updated, if required
098: *
099: * @author PaulE
100: * @version 1.0
101: */
102: public class AppBuilder {
103:
104: /** Set up Logging for Log4J */
105: private static Logger log = Logger.getLogger(AppBuilder.class);
106:
107: // Set up a bunch of statics as to what patterns get generated
108: // The numeric value indicates the order it is generated
109: private static final int FINDER = 0;
110: private static final int VIEWER = 1;
111: private static final int MAINT = 2;
112: private static final int LOOKUP = 3;
113: // This array should match the index keys above
114: private static final String[] COMPONENT_TYPES = new String[] {
115: "Finder", "Viewer", "Maintenance", "Lookup" };
116:
117: private DomainObjectHelper m_doList = null;
118: private ApplicationBuilder m_appProps = null;
119:
120: private ComponentRegistry m_components = new ComponentRegistry();
121:
122: /** Creates a new instance of AppBuilder
123: * @param name The name of the config file that describes how to build the application
124: */
125: public AppBuilder(String name) {
126: m_doList = new DomainObjectHelper();
127: m_appProps = loadAppFile(name);
128: if (m_appProps == null)
129: throw new RuntimeException("Can't Start AppBuilder!");
130: }
131:
132: /** Invokes the start of the pattern generation process
133: */
134: public void process() {
135: boolean error = false;
136: boolean abortOnError = true;
137:
138: log.debug("Load all domain objects...");
139: for (Iterator it = m_appProps.getDomainObjectPath().iterator(); it
140: .hasNext();) {
141: String path = (String) it.next();
142: log.info("Loading Object from..." + path);
143: m_doList.loadObjects(path);
144: }
145: m_doList.doIgnoreFilter();
146:
147: //------------------------------------
148: // Loop through the different build phases
149: //------------------------------------
150: for (int phase = 1; phase < 3
151: && (!error || (error && !abortOnError)); phase++) {
152: //log.debug("Starting Phase ... " + phase);
153:
154: //------------------------------------
155: // Loop through the different component types
156: //------------------------------------
157: for (int compType = 0; compType < COMPONENT_TYPES.length
158: && (!error || (error && !abortOnError)); compType++) {
159: String compTypeName = COMPONENT_TYPES[compType];
160: //log.debug("Starting Component Type ... " + compTypeName + " [" + compType + "]" );
161:
162: //------------------------------------
163: // Loop through the modules, and for each module build the domain objects
164: //------------------------------------
165: for (Iterator it = m_appProps.getModule().iterator(); it
166: .hasNext()
167: && (!error || (error && !abortOnError));) {
168: Module module = (Module) it.next();
169: //log.info("Staring Module ... " + module.getName());
170:
171: //------------------------------------
172: // Work Out Module labels Properties, and pre-load if merging
173: ListProperties moduleLabels = new ListProperties();
174:
175: StringBuffer labelFileName = new StringBuffer();
176: labelFileName.append(m_appProps.getOutputRoot());
177: labelFileName.append(m_appProps.getOutputJava());
178: labelFileName.append(File.separator);
179: labelFileName.append(StringHelper.replace(
180: m_appProps.getPackagePrefix(), ".",
181: File.separator));
182: labelFileName.append(File.separator);
183: if (m_appProps.isFullPackageNames()) {
184: labelFileName.append("applications");
185: labelFileName.append(File.separator);
186: }
187: labelFileName.append(m_appProps
188: .getApplicationName());
189: labelFileName.append(File.separator);
190: if (m_appProps.isFullPackageNames()) {
191: labelFileName.append("modules");
192: labelFileName.append(File.separator);
193: }
194: labelFileName.append(module.getName());
195: labelFileName.append(File.separator);
196: labelFileName
197: .append("ApplicationResources.pfragment");
198:
199: if (module.getLabels() == null)
200: module.setLabels("ignore");
201: if (module.getLabels().equalsIgnoreCase("merge")) {
202: File f = new File(labelFileName.toString());
203: if (f.exists()) {
204: log.debug("Merging Module Labels From "
205: + labelFileName.toString());
206: try {
207: moduleLabels
208: .load(new FileInputStream(f));
209: } catch (FileNotFoundException e) {
210: // Should not happen, we check for this earlier
211: } catch (IOException e) {
212: log
213: .error(
214: "Failed to load Labels to Merge",
215: e);
216: }
217: }
218: }
219:
220: //------------------------------------------------
221: // Based on component type pick the iterator for domain matching
222: Iterator itDomainPat = null;
223: switch (compType) {
224: case FINDER:
225: itDomainPat = module.getFindersDomainPattern()
226: .iterator();
227: break;
228: case VIEWER:
229: itDomainPat = module.getViewersDomainPattern()
230: .iterator();
231: break;
232: case MAINT:
233: itDomainPat = module
234: .getMaintenanceDomainPattern()
235: .iterator();
236: break;
237: case LOOKUP:
238: // create a lookup for each finder
239: itDomainPat = module.getFindersDomainPattern()
240: .iterator();
241: break;
242: default:
243: log.fatal("Unknown Component Type - "
244: + compType);
245: return;
246: }
247:
248: //------------------------------------
249: // for this module pattern match the finders...
250: for (; itDomainPat.hasNext()
251: && (!error || (error && !abortOnError));) {
252: String domainPattern = (String) itDomainPat
253: .next();
254: List domainList = matchDomain(domainPattern);
255: if (domainList != null || !domainList.isEmpty())
256: for (Iterator it3 = domainList.iterator(); it3
257: .hasNext()
258: && (!error || (error && !abortOnError));) {
259: Root domain = (Root) it3.next();
260:
261: log.debug("Processing: Phase=" + phase
262: + ", Component=" + compTypeName
263: + ", Module="
264: + module.getName()
265: + ", DomainObject="
266: + domain.getDomainObject());
267:
268: //---------------------------------------------
269: // Phase 1 pass through the builders....
270: // Create initial versions and register
271: //---------------------------------------------
272: if (phase == 1) {
273: IBuilder comp = null;
274:
275: // Create the different components passing in different data
276: switch (compType) {
277: case FINDER:
278: // Create the Finder
279: comp = new BuildObjectFinder(
280: m_appProps,
281: m_components, module,
282: domain, moduleLabels);
283: break;
284: case VIEWER:
285: // Create the Viewer
286: comp = new BuildObjectViewer(
287: m_appProps,
288: m_components, module,
289: domain, m_doList,
290: moduleLabels);
291: break;
292: case MAINT:
293: // Create the Maintenance
294: comp = new BuildObjectMaintenance(
295: m_appProps,
296: m_components, module,
297: domain, m_doList,
298: moduleLabels);
299: break;
300: case LOOKUP:
301: // Create the Lookup
302: comp = new BuildObjectLookup(
303: m_appProps,
304: m_components, module,
305: domain, moduleLabels);
306: break;
307: default:
308: log
309: .fatal("Unknown Component Type - "
310: + compType);
311: return;
312: }
313: try {
314: // Run the inital component build phase
315: comp.buildPhase1(m_appProps
316: .isFullPackageNames());
317: // add built component to registry
318: m_components.addComponent(comp);
319: } catch (Exception e) {
320: error = true;
321: log
322: .error(
323: "Build Phase 1 For Component '"
324: + compTypeName
325: + "', Domain Object "
326: + domain
327: .getDomainObject(),
328: e);
329: }
330: }
331:
332: //---------------------------------------------
333: // Phase 2 pass through the builders....
334: // Create all inter component links where needed
335: //---------------------------------------------
336: else if (phase == 2) {
337: // get the correct builder from the registry
338: IBuilder comp = m_components
339: .findComponent(
340: domain
341: .getDomainPackage()
342: + "."
343: + domain
344: .getDomainObject(),
345: compTypeName);
346: if (comp == null) {
347: log
348: .fatal("Can't Find Component For Phase 2 processing!");
349: log
350: .fatal("... ["
351: + compTypeName
352: + "] - "
353: + domain
354: .getDomainPackage()
355: + "."
356: + domain
357: .getDomainObject());
358: error = true;
359: } else {
360: try {
361: // Run the second build phase, to link components
362: comp
363: .buildPhase2(m_appProps
364: .isFullPackageNames());
365: } catch (Exception e) {
366: error = true;
367: log
368: .error(
369: "Build Phase 2 For Component '"
370: + compTypeName
371: + "', Domain Object "
372: + domain
373: .getDomainObject(),
374: e);
375: }
376: }
377: }
378:
379: } // end domain loop
380:
381: } // end domain pattern loop
382:
383: // Write out Module Labels if Required...This is only done in phase 1,
384: // otherwise the 'replace' option when used in phase 2 erases the file
385: // created in phase 1!
386: if (!module.getLabels().equalsIgnoreCase("ignore")
387: && phase == 1) {
388: String header = "Labels For Module "
389: + module.getName()
390: + ". Auto-Generated by the JAFFA AppBuilder";
391: log.debug("Saving " + header);
392: File f = new File(labelFileName.toString());
393: if (!f.exists()) {
394: if (!f.getParentFile().exists())
395: f.getParentFile().mkdirs();
396: }
397: try {
398: moduleLabels.sort();
399: moduleLabels.store(new FileOutputStream(f),
400: header);
401: } catch (IOException e) {
402: log.error(
403: "Failed to write out labels to - "
404: + labelFileName, e);
405: error = true;
406: }
407:
408: // If the labels have just been replaced, change the setting to
409: // merge for future iterations.
410: if (module.getLabels().equals("replace"))
411: module.setLabels("merge");
412: }
413:
414: } // end module loop
415:
416: } // end component type loop
417:
418: } // end phase loop
419:
420: //----------------------------------------
421: // Save out all components
422: //----------------------------------------
423: for (Iterator itSave = m_components.iterator(); itSave
424: .hasNext()
425: && (!error || (error && !abortOnError));) {
426: IBuilder comp = (IBuilder) itSave.next();
427: if (comp.save()) {
428: log.debug("Saved Component - "
429: + comp.getComponentName());
430: } else {
431: error = true;
432: log.error("Unable to save component meta for "
433: + comp.getComponentName());
434: }
435: } // end save loop
436: }
437:
438: private List matchDomain(String pattern) {
439: ArrayList list = new ArrayList();
440: for (Iterator it = m_doList.iterator(); it.hasNext();) {
441: Root domain = (Root) it.next();
442: String name = domain.getDomainPackage() + "."
443: + domain.getDomainObject();
444:
445: // compare the string
446: try {
447: if (Pattern.matches(pattern, name))
448: list.add(domain);
449: } catch (PatternSyntaxException e) {
450: log.error("Invalid Pattern '" + pattern
451: + "', Skipping Definition");
452: break;
453: }
454: }
455: return list;
456: }
457:
458: private ApplicationBuilder loadAppFile(String name) {
459: log.info("Building Application From Definition - " + name);
460: try {
461: // create a JAXBContext capable of handling classes generated into the package
462: JAXBContext jc = JAXBContext
463: .newInstance("org.jaffa.tools.patternmetaengine.domain");
464: // create an Unmarshaller
465: Unmarshaller u = jc.createUnmarshaller();
466: // enable validation
467: u.setValidating(true);
468: // unmarshal a document into a tree of Java content objects composed of classes from the package.
469: return (ApplicationBuilder) u.unmarshal(XmlHelper
470: .stripDoctypeDeclaration((new File(name)).toURL()));
471:
472: } catch (MalformedURLException e) {
473: log.error("Can't load file - " + name + ", Bad URL");
474: } catch (UnmarshalException e) {
475: log.error("Invalid XML. File - " + name, e);
476: } catch (Exception e) {
477: log.error("Unknown Error! Can't load file - " + name, e);
478: }
479: return null;
480: }
481:
482: /** Command line entry point for the utility
483: *
484: * @param args The command line arguments.
485: * <br> Must supply the following ...
486: * <br> [0] = Location of AppBuilder XML Descriptor
487: */
488: public static void main(String[] args) {
489: // Set up logging for debug output
490: try {
491: URL u = URLHelper
492: .newExtendedURL("resources/log4j-config.xml");
493: System.out.println("Set Up Log4J from "
494: + u.toExternalForm());
495: org.apache.log4j.xml.DOMConfigurator.configure(u);
496: System.out.println("Used File To Set Up Log4J");
497: } catch (Exception e) {
498: org.apache.log4j.BasicConfigurator.configure();
499: System.out.println("Default Log4J Set Up ");
500: }
501:
502: if (args == null || args.length != 1) {
503: System.out.println("Must supply the following parameters");
504: System.out
505: .println(" [0] = Location of AppBuilder XML Descriptor");
506: System.exit(-1);
507: }
508:
509: System.out.println("XML Descriptor = " + args[0]);
510:
511: AppBuilder app = new AppBuilder(args[0]);
512:
513: app.process();
514:
515: System.exit(0);
516: }
517:
518: }
|