View Javadoc

1   /*
2    * Copyright 2006 Outsource Cafe, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the 'License')
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an 'AS IS' BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.javagen.agile.core.ioc.spring;
17  
18  import java.util.Properties;
19  
20  import org.javagen.agile.core.GeneratorPipeline;
21  import org.javagen.agile.core.model.Model;
22  import org.springframework.beans.factory.BeanFactory;
23  import org.springframework.beans.factory.xml.XmlBeanFactory;
24  import org.springframework.core.io.DefaultResourceLoader;
25  import org.springframework.core.io.Resource;
26  
27  /***
28   * Loads XML configuration and executes code generation pipeline using Spring framework IoC BeanFactory.
29   * <p>
30   * This is an extremely flexible way to configure the code generator, avoiding a one-size-fits-all approach.
31   * <p>
32   * Your <code>Main</code> class should extended this class:
33   * <pre>
34   * public class Main extends SpringMain {
35   *   public static void main(String[] args) {
36   *     SpringMain main = new SpringMain();
37   *     main.setArgs(args);
38   *     main.runPipeline();
39   *   }
40   * }
41   * </pre>
42   * This is the only Spring dependency in JavaGen Agile.  If you don't like having to use Spring XML files to configure
43   * the generator then by all means write a non-Spring Main and contribute your code for inclusion in the framework!
44   * <p>
45   * @author Richard Easterling
46   */
47  public abstract class SpringMain {
48  
49      //default IDs for beans needed by SpringMain
50      public static final String DEFAULT_CONFIGURER_BEAN_ID = "propertyConfigurer";
51      public static final String DEFAULT_PIPELINE_BEAN_ID = "pipeline";
52      public static final String DEFAULT_TEMPLATE_GENERATOR_BEAN_ID = "templateGenerator";
53      public static final String DEFAULT_ROOT_BEAN_NAME_ID = "baseModel";
54      
55      public static final String DEFAULT_CONFIG_FILE = "classpath:GenSpringConfig.xml";
56  
57      protected String configFile = DEFAULT_CONFIG_FILE;
58      protected String propertyFile = null;
59      protected Properties properties = null;
60      protected BeanFactory beanFactory = null;
61      protected String startModelBeanId = null;
62      
63      /***
64       * Scans arguments for config and/or property file.
65       * <p>
66       * @param args takes an optional Spring XML configuration file location and/or a properties file.
67       * Files can be any valid URL or classpath location.  File types are recognized by their extension,
68       * names and order are not important. Here are some valid examples:
69       * <ul>
70       * <li>file:src/gen/GenSpringConfig.xml</li>
71       * <li>classpath:GenSpringConfig.xml</li>
72       * <li>file:src/gen/gen.properties</li>
73       * <li>classpath:gen.properties</li>
74       * </ul>
75       */
76      public void setArgs(String[] args) {
77          if (args!=null) {
78              for(String arg: args) {
79                  if (arg==null)
80                      continue;
81                  if (arg.endsWith(".xml"))
82                      configFile = arg;
83                  else if (arg.endsWith(".properties"))
84                      propertyFile = arg;
85              }
86          }
87      }
88      
89      /***
90       * Uses underlying IoC container to access bean instance by ID.  
91       * Configuration should be completed before calling this method.
92       */
93      public Object getBean(String beanId) {
94          return getBeanFactory().getBean(beanId);
95      }
96          
97      /***
98       * Pulls <code>pipeline</code> bean out of <code>factory</code> 
99       * and calls the <code>gen</code> method.
100      * @param factory - configured BeanFactory instance
101      */
102     public Model runPipeline() {
103         GeneratorPipeline pipeline = (GeneratorPipeline)getBean(getPipelineBeanId());
104         Model input = startModelBeanId==null ? null : (Model)getBean(startModelBeanId);
105         Model result = pipeline.gen(input); //run code generator
106         return result;
107     }
108     
109     /***
110      * Load config and properties.
111      * <p>
112      * Properties inputs have the following precedence from lowest to highest:
113      * <ol>
114      * <li>Properties specified in the <code>GlobalPropertyHolder</code> bean within the SpringConfig.xml file.</li>
115      * <li>Properties specified in the external <code>propertyFile</code>. Note: this can be specified as a <code>GlobalPropertyHolder</code> property and overridden here.</li>
116      * <li>Properties specified in the <code>properties</code> instance.  Note: these are usually set by the invoking tool (i.e. Maven).</li>
117      * </ol>
118      *
119      * @see http://static.springframework.org/spring/docs/2.0.x/reference/beans.html#beans-factory-extension-factory-postprocessors
120      */
121     protected BeanFactory getBeanFactory() {
122         if (beanFactory==null) {   
123             //load SpringConfig.xml and any properties
124             Resource ioc = new DefaultResourceLoader().getResource(getConfigFile());
125             XmlBeanFactory factory = new XmlBeanFactory(ioc);
126             //consolidate all the property inputs
127             GlobalPropertyHolder globalProps = (GlobalPropertyHolder)factory.getBean(getConfigurerBeanId());
128             if (properties!=null || propertyFile!=null) {
129                 if (propertyFile!=null) {
130                     Resource propResource = new DefaultResourceLoader().getResource(propertyFile);
131                     globalProps.setLocation(propResource);
132                 }
133                 if (properties!=null) {
134                     if (globalProps.getOverrideProperties()==null) {
135                         globalProps.setOverrideProperties(properties);
136                     } else {
137                         globalProps.getOverrideProperties().putAll(properties);
138                     }
139                 }
140             }
141             
142             //manually replace ${...} property place-holders within Spring context before loading any beans
143             globalProps.postProcessBeanFactory(factory);
144             //now inject these properties into globalPropertiesModel instance
145             globalProps.injectPropertiesIntoNode();
146             beanFactory = factory;
147         }
148         return beanFactory;
149     }
150 
151     /***
152      * Any type of configuration can be achieved using <code>XXXSpringConfig.xml</code> including:
153      * <ul>
154      * <li>Pipeline components and order of execution</li>
155      * <li>Template names, locations and types and their artifact naming patterns and relative output locations</li>
156      * <li>Per-template overwrite predicates</li>
157      * <li>Binding templates to model instances</li>
158      * <li>Root structure of model tree including global properties and parent containers for generated model instances</li>
159      * <li>Default and artifact-specific naming patterns using model and emitter contexts</li>
160      * <li>Reading, writing and merging of metadata</li>
161      * <li>Code naming and conversion implementations</li>
162      * <li>Anything that <code>gen.properties</code> can specify</li>
163      * </ul>
164      * 
165      * @param configFile Spring xml context file.
166      */
167     public String getConfigFile() { 
168         return configFile; 
169     }
170     
171     public void setConfigFile(String configFile) {
172         this.configFile = configFile;
173     }
174 
175     /***
176      * Overrides default properties set in <code>configFile</code>.  
177      * 
178      * @param properties
179      */
180     public void setProperties(Properties properties) {
181         this.properties = properties;
182     }
183 
184     /***
185      * The properties file is meant to be the first (ie easiest) 
186      * level of generator configuration specifying:
187      * <ul>
188      * <li>Generated project name and directory location</li>
189      * <li>Source folder layout</li>
190      * <li>Output types</li>
191      * </ul>
192      */
193     public void setPropertyFile(String propertyFile) {
194         this.propertyFile = propertyFile;
195     }
196 
197     public String getConfigurerBeanId() { return DEFAULT_CONFIGURER_BEAN_ID; }
198     
199     public String getPipelineBeanId() { return DEFAULT_PIPELINE_BEAN_ID; }
200     
201     /***
202      * The bean id of the model instance to use as input to the pipeline.
203      * <p>
204      * Note: normally the startModel is set directly on the pipeline in the SpringConfig.xml file.
205      */
206     public String getStartModelBeanId() { return startModelBeanId; }
207     public void setStartModelBeanId(String parentNodeBeanId) { this.startModelBeanId = parentNodeBeanId; }
208     
209 }