001: package org.andromda.maven.plugin.andromdapp;
002:
003: import java.io.BufferedReader;
004: import java.io.File;
005: import java.io.IOException;
006: import java.io.InputStreamReader;
007:
008: import java.lang.reflect.Method;
009:
010: import java.util.ArrayList;
011: import java.util.Arrays;
012: import java.util.Collection;
013: import java.util.Collections;
014: import java.util.HashMap;
015: import java.util.Iterator;
016: import java.util.LinkedHashMap;
017: import java.util.List;
018: import java.util.Map;
019: import java.util.Properties;
020:
021: import org.andromda.maven.plugin.andromdapp.utils.ProjectUtils;
022: import org.andromda.maven.plugin.andromdapp.utils.Projects;
023: import org.apache.commons.lang.ObjectUtils;
024: import org.apache.commons.lang.StringUtils;
025: import org.apache.maven.BuildFailureException;
026: import org.apache.maven.execution.MavenSession;
027: import org.apache.maven.execution.ReactorManager;
028: import org.apache.maven.lifecycle.LifecycleExecutionException;
029: import org.apache.maven.lifecycle.LifecycleExecutor;
030: import org.apache.maven.plugin.AbstractMojo;
031: import org.apache.maven.plugin.MojoExecutionException;
032: import org.apache.maven.project.MavenProject;
033: import org.apache.maven.project.MavenProjectBuilder;
034: import org.apache.maven.project.ProjectBuildingException;
035: import org.codehaus.plexus.util.dag.CycleDetectedException;
036:
037: /**
038: * A Mojo used for executing the build goals from the top level project.
039: *
040: * @goal build
041: * @author Chad Brandon
042: */
043: public class BuildMojo extends AbstractMojo {
044: /**
045: * @parameter expression="${component.org.apache.maven.lifecycle.LifecycleExecutor}"
046: */
047: private LifecycleExecutor lifecycleExecutor;
048:
049: /**
050: * @parameter expression="${session}"
051: */
052: private MavenSession session;
053:
054: /**
055: * @parameter expression="${project.basedir}"
056: */
057: private File baseDirectory;
058:
059: /**
060: * A comma seperated list of modules to execute in the form:
061: * <em>-Dmodules=mda,core,common</em> or if you want to specify the goals
062: * to execute as well:
063: * <em>-Dmodules=mda:[goal1+goal2+goal3],core:[goal1]<em>.
064: *
065: * @parameter expression="${modules}"
066: */
067: private String modules;
068:
069: /**
070: * @parameter expression="${project}"
071: * @required
072: * @readonly
073: */
074: private MavenProject project;
075:
076: /**
077: * If defined starts the build console (i.e. keeps maven loaded and running)
078: *
079: * @parameter expression="${console}"
080: */
081: private String startConsole;
082:
083: /**
084: * The default module goals to execute.
085: *
086: * @parameter
087: */
088: private List goals = new ArrayList(Arrays
089: .asList(new String[] { "install" }));
090:
091: /**
092: * The string used to quite the console;
093: */
094: private static final String EXIT = "exit";
095:
096: /**
097: * Used to contruct Maven project instances from POMs.
098: *
099: * @component
100: */
101: private MavenProjectBuilder projectBuilder;
102:
103: /**
104: * Any execution properties.
105: *
106: * @parameter
107: */
108: private Properties executionProperties = new Properties();
109:
110: /**
111: * Identifies system properties when running in console mode.
112: */
113: private static final String EXECUTION_PROPERTY_TOKEN = "-D";
114:
115: /**
116: * Lists all execution properties when running in console mode.
117: */
118: private static final String LIST_PROPERTIES = "-list";
119:
120: /**
121: * Clears all execution properties.
122: */
123: private static final String CLEAR_PROPERTIES = "-clear";
124:
125: /**
126: * Explicity calls the garbage collector.
127: */
128: private static final String GARBAGE_COLLECT = "-gc";
129:
130: /**
131: * The prefix environment variables must have.
132: *
133: * @parameter expression="env."
134: */
135: private String environmentVariablePrefix;
136:
137: /**
138: * @see org.apache.maven.plugin.Mojo#execute()
139: */
140: public void execute() throws MojoExecutionException {
141: try {
142: final Map environment = this .getEnvironment();
143: if (this .startConsole != null
144: && !this .startConsole.equals(Boolean.FALSE
145: .toString())) {
146: boolean executed = false;
147: this .printLine();
148: while (true) {
149: this .printConsolePrompt();
150: String input = StringUtils.trimToEmpty(this
151: .readLine());
152: if (EXIT.equals(input)) {
153: break;
154: }
155: if (input.startsWith(EXECUTION_PROPERTY_TOKEN)) {
156: input = input.replaceFirst(
157: EXECUTION_PROPERTY_TOKEN, "");
158: int index = input.indexOf("=");
159: String name;
160: String value;
161: if (index <= 0) {
162: name = input.trim();
163: value = "true";
164: } else {
165: name = input.substring(0, index).trim();
166: value = input.substring(index + 1).trim();
167: }
168: if (value
169: .startsWith(this .environmentVariablePrefix)) {
170: value = StringUtils.replace(value,
171: this .environmentVariablePrefix, "");
172: if (environment.containsKey(value)) {
173: value = ObjectUtils.toString(
174: environment.get(value)).trim();
175: }
176: }
177: this .executionProperties.put(name, value);
178: System.setProperty(name, value);
179: this .printExecutionProperties();
180: } else if (LIST_PROPERTIES.equals(input)) {
181: this .printExecutionProperties();
182: } else if (CLEAR_PROPERTIES.equals(input)) {
183: this .executionProperties.clear();
184: this .printExecutionProperties();
185: } else if (GARBAGE_COLLECT.equals(input)) {
186: System.gc();
187: } else {
188: try {
189: executed = this .executeModules(input);
190:
191: // - if nothing was executed, try a goal in the current project
192: if (this .project != null && !executed
193: && input != null
194: && input.trim().length() > 0) {
195: executed = true;
196: final List goals = Arrays.asList(input
197: .split("\\s+"));
198: this .executeModules(StringUtils.join(
199: this .project.getModules()
200: .iterator(), ","),
201: goals, true);
202: }
203: } catch (final Throwable throwable) {
204: throwable.printStackTrace();
205: }
206: if (executed) {
207: this .printLine();
208: }
209: Projects.instance().clear();
210: }
211: }
212: } else {
213: this .executionProperties.putAll(this .session
214: .getExecutionProperties());
215: this .executeModules(this .modules);
216: }
217: } catch (final Throwable throwable) {
218: throw new MojoExecutionException("Error executing modules",
219: throwable);
220: }
221: }
222:
223: private static final String GET_ENVIRONMENT_METHOD = "getenv";
224:
225: /**
226: * Retrieves the environment variables (will only work when running jdk5 or above).
227: *
228: * @return the environment variables.
229: */
230: private Map getEnvironment() {
231: final Map variables = new HashMap();
232: try {
233: final Method method = System.class.getMethod(
234: GET_ENVIRONMENT_METHOD, null);
235: final Object result = method.invoke(System.class, null);
236: if (result instanceof Map) {
237: variables.putAll((Map) result);
238: }
239: } catch (Exception exception) {
240: // - ignore (means we can't retrieve the environment with the current JDK).
241: }
242: return variables;
243: }
244:
245: private void printExecutionProperties() {
246: this .printLine();
247: this
248: .printTextWithLine("| ------------- execution properties ------------- |");
249: for (final Iterator iterator = this .executionProperties
250: .keySet().iterator(); iterator.hasNext();) {
251: final String name = (String) iterator.next();
252: System.out.println(" " + name + " = "
253: + this .executionProperties.getProperty(name));
254: }
255: this
256: .printTextWithLine("| ------------------------------------------------ |");
257: this .printLine();
258: }
259:
260: /**
261: * Prints the prompt for the console
262: */
263: private void printConsolePrompt() {
264: if (this .project != null) {
265: this .printText("");
266: this .printText(this .project.getArtifactId() + " "
267: + this .project.getVersion() + ">");
268: }
269: }
270:
271: /**
272: * Prints text to the console.
273: *
274: * @param text the text to print to the console;
275: */
276: private void printText(final String text) {
277: System.out.print(text);
278: System.out.flush();
279: }
280:
281: /**
282: * Prints text with a new line to the console.
283: *
284: * @param text the text to print to the console.
285: */
286: private void printTextWithLine(final String text) {
287: System.out.println(text);
288: System.out.flush();
289: }
290:
291: /**
292: * Prints a line to standard output.
293: */
294: private void printLine() {
295: System.out.println();
296: System.out.flush();
297: }
298:
299: /**
300: * Reads a line from standard input and returns the value.
301: *
302: * @return the value read from standard input.
303: */
304: private String readLine() {
305: final BufferedReader input = new BufferedReader(
306: new InputStreamReader(System.in));
307: String inputString = null;
308: try {
309: inputString = input.readLine();
310: } catch (final IOException exception) {
311: inputString = null;
312: }
313: return inputString == null || inputString.trim().length() == 0 ? null
314: : inputString;
315: }
316:
317: /**
318: * Creates all project modules and executes them.
319: *
320: * @param modules the comma seperated list of modules to execute.
321: * @return true if any modules were executed, false otherwise.
322: * @throws MojoExecutionException
323: * @throws CycleDetectedException
324: * @throws LifecycleExecutionException
325: * @throws BuildFailureException
326: */
327: private boolean executeModules(final String modules)
328: throws MojoExecutionException {
329: return this .executeModules(modules, null, false);
330: }
331:
332: /**
333: * Creates all project modules and executes them.
334: *
335: * @param modules The list of modules to execute.
336: * @param goals the list of goals to execute (if null, the, goals will be retrieved from project map).
337: * @param sortProjects whether or not projects should be sorted and then executed or whether they should be executed in the
338: * order they're in.
339: * @throws CycleDetectedException
340: * @throws LifecycleExecutionException
341: * @throws MojoExecutionException
342: * @throws BuildFailureException
343: * @return true/false on whether or not any modules were executed
344: */
345: private boolean executeModules(final String modules,
346: final List goals, boolean sortProjects)
347: throws MojoExecutionException {
348: final Map projects = this .collectProjects(modules);
349: boolean executed = !projects.isEmpty();
350:
351: // - only execute if we have some projects
352: if (executed) {
353: if (!sortProjects) {
354: for (final Iterator iterator = projects.keySet()
355: .iterator(); iterator.hasNext();) {
356: final MavenProject project = (MavenProject) iterator
357: .next();
358: List projectGoals;
359: if (goals == null) {
360: projectGoals = (List) projects.get(project);
361: if (projectGoals.isEmpty()) {
362: projectGoals.addAll(this .goals);
363: }
364: } else {
365: projectGoals = goals;
366: }
367: this .executeProjects(Collections
368: .singletonList(project), projectGoals);
369: }
370: } else {
371: this .executeProjects(projects.keySet(), goals);
372: }
373: }
374: return executed;
375: }
376:
377: /**
378: * Executes the given maven <code>project</code>.
379: *
380: * @param project the project to execute.
381: * @param goals the goals to execute on the project.
382: * @throws MojoExecutionException
383: * @throws CycleDetectedException
384: * @throws LifecycleExecutionException
385: * @throws BuildFailureException
386: */
387: private void executeProjects(final Collection projects,
388: final List goals) throws MojoExecutionException {
389: try {
390: if (goals.isEmpty()) {
391: goals.addAll(this .goals);
392: }
393: if (projects.size() > 1) {
394: this .getLog().info("Reactor build order:");
395: }
396: final ReactorManager reactorManager = new ReactorManager(
397: new ArrayList(projects));
398: for (final Iterator iterator = reactorManager
399: .getSortedProjects().iterator(); iterator.hasNext();) {
400: final MavenProject project = (MavenProject) iterator
401: .next();
402: this .getLog().info(" " + project.getName());
403: }
404:
405: final MavenSession projectSession = new MavenSession(
406: this .session.getContainer(), this .session
407: .getSettings(), this .session
408: .getLocalRepository(), this .session
409: .getEventDispatcher(), reactorManager,
410: goals, this .baseDirectory.toString(),
411: this .executionProperties, this .session
412: .getStartTime());
413:
414: projectSession.setUsingPOMsFromFilesystem(true);
415: this .lifecycleExecutor
416: .execute(projectSession, reactorManager,
417: projectSession.getEventDispatcher());
418: } catch (final Throwable throwable) {
419: throw new MojoExecutionException(
420: "An error occured while attempting to execute projects",
421: throwable);
422: }
423: }
424:
425: /**
426: * Collects all project modules to execute.
427: *
428: * @return the Map of collected projects (the key is the project, the value
429: * the goals).
430: * @param The list of modules to execute.
431: * @throws MojoExecutionException
432: */
433: private Map collectProjects(final String modules)
434: throws MojoExecutionException {
435: final Map projects = new LinkedHashMap();
436: final Map poms = getModulePoms(modules);
437:
438: if (!poms.isEmpty()) {
439: for (final Iterator iterator = poms.keySet().iterator(); iterator
440: .hasNext();) {
441: final File pom = (File) iterator.next();
442: try {
443: final MavenProject project = ProjectUtils
444: .getProject(this .projectBuilder,
445: this .session, pom);
446: if (this .getLog().isDebugEnabled()) {
447: this .getLog().debug(
448: "Adding project " + project.getId());
449: }
450: projects.put(project, poms.get(pom));
451: } catch (ProjectBuildingException exception) {
452: throw new MojoExecutionException(
453: "Error loading POM --> '" + pom + "'",
454: exception);
455: }
456: }
457: }
458: return projects;
459: }
460:
461: /**
462: * Gets all POMs for the modules specified.
463: *
464: * @param moduleList the list of modules to execute.
465: * @return the list of module poms
466: */
467: private Map getModulePoms(final String moduleList) {
468: final Map poms = new LinkedHashMap();
469: final String[] modules = moduleList != null ? moduleList
470: .split(",") : null;
471:
472: final String goalPrefix = ":";
473: if (modules != null) {
474: final int numberOfModules = modules.length;
475: for (int ctr = 0; ctr < numberOfModules; ctr++) {
476: String module = modules[ctr].trim();
477: final List goalsList = new ArrayList();
478: if (module.indexOf(goalPrefix) != -1) {
479: final String[] goals = module.replaceAll(
480: ".*(:\\[)|(\\])", "").split("\\+");
481: if (goals != null) {
482: final int numberOfGoals = goals.length;
483: for (int ctr2 = 0; ctr2 < numberOfGoals; ctr2++) {
484: final String goal = goals[ctr2].trim();
485: goalsList.add(goal);
486: }
487: }
488: }
489: module = module.replaceAll(goalPrefix + "\\[.*\\]", "");
490: final File pom = new File(this .baseDirectory, module
491: + "/pom.xml");
492: if (pom.isFile()) {
493: poms.put(pom, goalsList);
494: }
495: }
496: }
497: return poms;
498: }
499: }
|