]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.agent/src/main/java/org/argeo/slc/ant/AntSlcApplication.java
525c276af4004c3853331c4401be6bd9d069ffc0
[gpl/argeo-slc.git] / org.argeo.slc.agent / src / main / java / org / argeo / slc / ant / AntSlcApplication.java
1 package org.argeo.slc.ant;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Properties;
9 import java.util.StringTokenizer;
10 import java.util.Vector;
11
12 import org.apache.commons.io.IOUtils;
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15 import org.apache.log4j.Appender;
16 import org.apache.log4j.LogManager;
17 import org.apache.log4j.MDC;
18 import org.apache.tools.ant.BuildListener;
19 import org.apache.tools.ant.Project;
20 import org.apache.tools.ant.ProjectHelper;
21 import org.apache.tools.ant.helper.ProjectHelper2;
22 import org.apache.tools.ant.listener.CommonsLoggingListener;
23 import org.argeo.slc.core.SlcException;
24 import org.argeo.slc.core.process.SlcExecution;
25 import org.argeo.slc.core.structure.SimpleSElement;
26 import org.argeo.slc.core.structure.StructureRegistry;
27 import org.argeo.slc.core.structure.tree.TreeSPath;
28 import org.argeo.slc.core.structure.tree.TreeSRegistry;
29 import org.argeo.slc.logging.Log4jUtils;
30 import org.argeo.slc.runtime.SlcExecutionOutput;
31 import org.springframework.beans.factory.BeanFactoryUtils;
32 import org.springframework.beans.factory.ListableBeanFactory;
33 import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
34 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
35 import org.springframework.context.ConfigurableApplicationContext;
36 import org.springframework.context.support.GenericApplicationContext;
37 import org.springframework.core.io.DefaultResourceLoader;
38 import org.springframework.core.io.Resource;
39 import org.springframework.core.io.ResourceLoader;
40 import org.springframework.util.SystemPropertyUtils;
41
42 public class AntSlcApplication {
43 private final static String DEFAULT_APP_LOG4J_PROPERTIES = "org/argeo/slc/ant/defaultAppLog4j.properties";
44
45 private final static Log log = LogFactory.getLog(AntSlcApplication.class);
46
47 private Resource contextLocation;
48 private ConfigurableApplicationContext parentContext;
49
50 private Resource rootDir;
51 private Resource confDir;
52 private File workDir;
53
54 public void execute(SlcExecution slcExecution, Properties properties,
55 Map<String, Object> references,
56 SlcExecutionOutput<AntExecutionContext> executionOutput) {
57
58 // Properties and application logging initialization
59 initSystemProperties(properties);
60 Log4jUtils.initLog4j("classpath:" + DEFAULT_APP_LOG4J_PROPERTIES);
61
62 log.info("\n###\n### Start SLC execution " + slcExecution.getUuid()
63 + "\n###\n");
64 if (log.isDebugEnabled()) {
65 log.debug("rootDir=" + rootDir);
66 log.debug("confDir=" + confDir);
67 log.debug("workDir=" + workDir);
68 }
69
70 // Ant coordinates
71 String scriptRelativePath = findAntScript(slcExecution);
72 List<String> targets = findAntTargets(slcExecution);
73
74 // Spring initialization
75 ConfigurableApplicationContext ctx = createExecutionContext(slcExecution);
76
77 // Ant project initialization
78 Project project = new Project();
79 AntExecutionContext executionContext = new AntExecutionContext(project);
80 project.addReference(AntConstants.REF_ROOT_CONTEXT, ctx);
81 project.addReference(AntConstants.REF_SLC_EXECUTION, slcExecution);
82
83 try {
84 initProject(project, properties, references);
85 parseProject(project, scriptRelativePath);
86
87 // Execute project
88 initStructure(project, scriptRelativePath);
89 runProject(project, targets);
90
91 if (executionOutput != null)
92 executionOutput.postExecution(executionContext);
93 } finally {
94 ctx.close();
95 }
96 }
97
98 protected void initSystemProperties(Properties userProperties) {
99 // Set user properties as system properties so that Spring can access
100 // them
101 if (userProperties != null) {
102 for (Object key : userProperties.keySet()) {
103 System.setProperty(key.toString(), userProperties
104 .getProperty(key.toString()));
105 }
106 }
107
108 if (System.getProperty(AntConstants.DEFAULT_TEST_RUN_PROPERTY) == null) {
109 System.setProperty(AntConstants.DEFAULT_TEST_RUN_PROPERTY,
110 "defaultTestRun");
111 }
112
113 try {
114 if (rootDir != null)
115 setSystemPropertyForRes(AntConstants.ROOT_DIR_PROPERTY, rootDir);
116 if (confDir != null)
117 setSystemPropertyForRes(AntConstants.CONF_DIR_PROPERTY, confDir);
118 if (workDir != null)
119 System.setProperty(AntConstants.WORK_DIR_PROPERTY, workDir
120 .getCanonicalPath());
121
122 // Additional properties in slc.properties file. Already set sytem
123 // properties (such as the various directories) can be resolved in
124 // placeholders.
125 if (confDir != null) {
126 Resource slcPropertiesRes = confDir
127 .createRelative("slc.properties");
128 if (slcPropertiesRes.exists()) {
129 Properties slcProperties = new Properties();
130 InputStream in = slcPropertiesRes.getInputStream();
131 try {
132 slcProperties.load(in);
133 } finally {
134 IOUtils.closeQuietly(in);
135 }
136
137 for (String key : slcProperties.stringPropertyNames()) {
138 if (!System.getProperties().containsKey(key)) {
139 String value = SystemPropertyUtils
140 .resolvePlaceholders(slcProperties
141 .getProperty(key));
142 System.setProperty(key, value);
143 }
144 }
145 }
146 }
147 } catch (Exception e) {
148 throw new SlcException("Cannot init system properties.", e);
149 }
150 }
151
152 /**
153 * Set property as an absolute file path if the resource can be located on
154 * the file system, or as an url.
155 */
156 private void setSystemPropertyForRes(String key, Resource res)
157 throws IOException {
158 String value = null;
159 try {
160 value = res.getFile().getCanonicalPath();
161 } catch (IOException e) {
162 value = res.getURL().toString();
163 }
164 System.setProperty(key, value);
165 }
166
167 protected ConfigurableApplicationContext createExecutionContext(
168 SlcExecution slcExecution) {
169 try {
170
171 // Find runtime definition
172 Resource runtimeRes = null;
173 String runtimeStr = slcExecution.getAttributes().get(
174 AntConstants.EXECATTR_RUNTIME);
175 if (runtimeStr == null)
176 runtimeStr = "default";
177
178 ResourceLoader rl = new DefaultResourceLoader(getClass()
179 .getClassLoader());
180 try {// tries absolute reference
181 runtimeRes = rl.getResource(runtimeStr);
182 } catch (Exception e) {
183 // silent
184 }
185 if (runtimeRes == null || !runtimeRes.exists()) {
186 if (confDir != null)
187 runtimeRes = confDir.createRelative("runtime/" + runtimeStr
188 + ".xml");
189 }
190
191 // Find runtime independent application context definition
192 if (confDir != null && contextLocation == null) {
193 contextLocation = confDir
194 .createRelative("applicationContext.xml");
195 }
196
197 GenericApplicationContext ctx = new GenericApplicationContext(
198 parentContext);
199 ctx.setDisplayName("SLC Execution #" + slcExecution.getUuid());
200
201 XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
202 if (runtimeRes != null && runtimeRes.exists())
203 xmlReader.loadBeanDefinitions(runtimeRes);
204 else
205 log.warn("No runtime context defined");
206
207 if (contextLocation != null && contextLocation.exists())
208 xmlReader.loadBeanDefinitions(contextLocation);
209 else
210 log.warn("No runtime independent application context defined");
211
212 // Add property place holder
213 PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
214 ppc.setIgnoreUnresolvablePlaceholders(true);
215 ctx.addBeanFactoryPostProcessor(ppc);
216
217 ctx.refresh();
218 return ctx;
219 } catch (Exception e) {
220 throw new SlcException(
221 "Cannot create SLC execution application context.", e);
222 }
223 }
224
225 protected String findAntScript(SlcExecution slcExecution) {
226 String scriptStr = slcExecution.getAttributes().get(
227 AntConstants.EXECATTR_ANT_FILE);
228 if (scriptStr == null)
229 throw new SlcException("No Ant script provided");
230
231 return scriptStr;
232 }
233
234 protected List<String> findAntTargets(SlcExecution slcExecution) {
235 String targetList = slcExecution.getAttributes().get(
236 AntConstants.EXECATTR_ANT_TARGETS);
237 List<String> targets = new Vector<String>();
238 if (targetList != null) {
239 StringTokenizer stTargets = new StringTokenizer(targetList, ",");
240 while (stTargets.hasMoreTokens()) {
241 targets.add(stTargets.nextToken());
242 }
243 }
244 return targets;
245 }
246
247 protected void initProject(Project project, Properties properties,
248 Map<String, Object> references) {
249 if (properties != null) {
250 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
251 project.setUserProperty(entry.getKey().toString(), entry
252 .getValue().toString());
253 }
254 }
255
256 if (references != null) {
257 for (Map.Entry<String, Object> entry : references.entrySet()) {
258 project.addReference(entry.getKey(), entry.getValue());
259 }
260 }
261
262 project.addBuildListener(new CommonsLoggingListener());
263
264 ListableBeanFactory context = (ListableBeanFactory) project
265 .getReference(AntConstants.REF_ROOT_CONTEXT);
266 // Register build listeners
267 Map<String, BuildListener> listeners = BeanFactoryUtils
268 .beansOfTypeIncludingAncestors(context, BuildListener.class,
269 false, false);
270 for (BuildListener listener : listeners.values()) {
271 project.addBuildListener(listener);
272 }
273
274 // Register log4j appenders from context
275 MDC.put(AntConstants.MDC_ANT_PROJECT, project);
276 Map<String, Appender> appenders = context.getBeansOfType(
277 Appender.class, false, true);
278 for (Appender appender : appenders.values()) {
279 LogManager.getRootLogger().addAppender(appender);
280 }
281
282 project.init();
283 addCustomTaskAndTypes(project);
284 }
285
286 /** Loads the SLC specific Ant tasks. */
287 protected void addCustomTaskAndTypes(Project project) {
288 Properties taskdefs = getDefs(project,
289 AntConstants.SLC_TASKDEFS_RESOURCE_PATH);
290 for (Object o : taskdefs.keySet()) {
291 String name = o.toString();
292 try {
293 project.addTaskDefinition(name, Class.forName(taskdefs
294 .getProperty(name)));
295 } catch (ClassNotFoundException e) {
296 log.error("Unknown class for task " + name, e);
297 }
298 }
299 Properties typedefs = getDefs(project,
300 AntConstants.SLC_TYPEDEFS_RESOURCE_PATH);
301 for (Object o : typedefs.keySet()) {
302 String name = o.toString();
303 try {
304 project.addDataTypeDefinition(name, Class.forName(typedefs
305 .getProperty(name)));
306 } catch (ClassNotFoundException e) {
307 log.error("Unknown class for type " + name, e);
308 }
309 }
310 }
311
312 private Properties getDefs(Project project, String path) {
313 Properties defs = new Properties();
314 try {
315 InputStream in = project.getClass().getResourceAsStream(path);
316 defs.load(in);
317 in.close();
318 } catch (IOException e) {
319 throw new SlcException("Cannot load task definitions", e);
320 }
321 return defs;
322 }
323
324 protected void initStructure(Project project, String scriptRelativePath) {
325 // Init structure registry
326 StructureRegistry<TreeSPath> registry = new TreeSRegistry();
327 project.addReference(AntConstants.REF_STRUCTURE_REGISTRY, registry);
328
329 // Lowest levels
330 StringTokenizer st = new StringTokenizer(scriptRelativePath, "/");
331 TreeSPath currPath = null;
332 while (st.hasMoreTokens()) {
333 String name = st.nextToken();
334 if (currPath == null) {
335 currPath = TreeSPath.createRootPath(name);
336 } else {
337 if (st.hasMoreTokens())// don't register project file
338 currPath = currPath.createChild(name);
339 }
340 registry.register(currPath, new SimpleSElement(name));
341 }
342
343 // Project level
344 String projectName = project.getName() != null
345 && !project.getName().equals("") ? project.getName()
346 : "project";
347 TreeSPath projectPath = currPath.createChild(projectName);
348
349 String projectDesc = project.getDescription() != null
350 && !project.getDescription().equals("") ? project
351 .getDescription() : projectPath.getName();
352
353 registry.register(projectPath, new SimpleSElement(projectDesc));
354 project.addReference(AntConstants.REF_PROJECT_PATH, projectPath);
355
356 if (log.isDebugEnabled())
357 log.debug("Project path: " + projectPath);
358 }
359
360 protected void parseProject(Project project, String scriptRelativePath) {
361 try {
362 Resource script = rootDir.createRelative(scriptRelativePath);
363 File baseDir = null;
364 try {
365 File scriptFile = script.getFile();
366 baseDir = scriptFile.getParentFile();
367 } catch (IOException e) {// resource is not a file
368 baseDir = new File(System.getProperty("user.dir"));
369 }
370 project.setBaseDir(baseDir);
371 // Reset basedir property, in order to avoid base dir override when
372 // running in Maven
373 project.setProperty("basedir", baseDir.getAbsolutePath());
374
375 ProjectHelper2 projectHelper = new ProjectHelper2();
376 project.addReference(ProjectHelper.PROJECTHELPER_REFERENCE,
377 projectHelper);
378 projectHelper.parse(project, script.getURL());
379 } catch (Exception e) {
380 throw new SlcException("Could not parse project for script "
381 + scriptRelativePath, e);
382 }
383
384 }
385
386 protected void runProject(Project p, List<String> targets) {
387 p.fireBuildStarted();
388 Throwable exception = null;
389 try {
390 if (targets.size() == 0) {// no target defined
391 p.executeTarget(p.getDefaultTarget());
392 } else {
393 p.executeTargets(new Vector<String>(targets));
394 }
395 } catch (Throwable e) {
396 exception = e;
397 throw new SlcException("SLC Ant execution failed", exception);
398 } finally {
399 p.fireBuildFinished(exception);
400 }
401 }
402
403 public void setContextLocation(Resource contextLocation) {
404 this.contextLocation = contextLocation;
405 }
406
407 public void setRootDir(Resource rootDir) {
408 this.rootDir = rootDir;
409 }
410
411 public void setConfDir(Resource confDir) {
412 this.confDir = confDir;
413 }
414
415 public void setWorkDir(File workDir) {
416 this.workDir = workDir;
417 }
418
419 public void setParentContext(ConfigurableApplicationContext runtimeContext) {
420 this.parentContext = runtimeContext;
421 }
422
423 }