1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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);
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
124 Resource ioc = new DefaultResourceLoader().getResource(getConfigFile());
125 XmlBeanFactory factory = new XmlBeanFactory(ioc);
126
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
143 globalProps.postProcessBeanFactory(factory);
144
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 }