View Javadoc
1   package org.andromda.maven.plugin.andromdapp;
2   
3   import java.io.BufferedReader;
4   import java.io.File;
5   import java.io.IOException;
6   import java.io.InputStreamReader;
7   import java.lang.reflect.Method;
8   import java.util.ArrayList;
9   import java.util.Arrays;
10  import java.util.Collection;
11  import java.util.Collections;
12  import java.util.HashMap;
13  import java.util.Iterator;
14  import java.util.LinkedHashMap;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Properties;
18  import org.andromda.maven.plugin.andromdapp.utils.ProjectUtils;
19  import org.andromda.maven.plugin.andromdapp.utils.Projects;
20  import org.apache.commons.lang.ObjectUtils;
21  import org.apache.commons.lang.StringUtils;
22  import org.apache.maven.BuildFailureException;
23  import org.apache.maven.execution.MavenSession;
24  import org.apache.maven.execution.ReactorManager;
25  import org.apache.maven.lifecycle.LifecycleExecutionException;
26  import org.apache.maven.lifecycle.LifecycleExecutor;
27  import org.apache.maven.plugin.AbstractMojo;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.project.MavenProject;
30  import org.apache.maven.project.MavenProjectBuilder;
31  import org.apache.maven.project.ProjectBuildingException;
32  import org.codehaus.plexus.util.dag.CycleDetectedException;
33  
34  /**
35   * A Mojo used for executing the build goals from the top level project.
36   *
37   * @goal build
38   * @author Chad Brandon
39   */
40  public class BuildMojo
41      extends AbstractMojo
42  {
43      /**
44       * @component role="org.apache.maven.lifecycle.LifecycleExecutor"
45       */
46      private LifecycleExecutor lifecycleExecutor;
47  
48      /**
49       * @parameter expression="${session}"
50       */
51      private MavenSession session;
52  
53      /**
54       * @parameter expression="${project.basedir}"
55       */
56      private File baseDirectory;
57  
58      /**
59       * A comma separated list of modules to execute in the form:
60       * <em>-Dmodules=mda,core,common</em> or if you want to specify the goals
61       * to execute as well:
62       * <em>-Dmodules=mda:[goal1+goal2+goal3],core:[goal1]<em>.
63       *
64       * @parameter expression="${modules}"
65       */
66      private String modules;
67  
68      /**
69       * @parameter expression="${project}"
70       * @required
71       * @readonly
72       */
73      private MavenProject project;
74  
75      /**
76       * If defined starts the build console (i.e. keeps maven loaded and running)
77       *
78       * @parameter expression="${console}"
79       */
80      private String startConsole;
81  
82      /**
83       * The default module goals to execute.
84       *
85       * @parameter
86       */
87      private List<String> goals = new ArrayList<String>(Arrays.asList("install"));
88  
89      /**
90       * The string used to quite the console;
91       */
92      private static final String EXIT = "exit";
93  
94      /**
95       * Used to construct Maven project instances from POMs.
96       *
97       * @component
98       */
99      private MavenProjectBuilder projectBuilder;
100 
101     /**
102      * Any execution properties.
103      *
104      * @parameter
105      */
106     private Properties executionProperties = new Properties();
107 
108     /**
109      * Identifies system properties when running in console mode.
110      */
111     private static final String EXECUTION_PROPERTY_TOKEN = "-D";
112 
113     /**
114      * Lists all execution properties when running in console mode.
115      */
116     private static final String LIST_PROPERTIES = "-list";
117 
118     /**
119      * Clears all execution properties.
120      */
121     private static final String CLEAR_PROPERTIES = "-clear";
122 
123     /**
124      * Explicitly calls the garbage collector.
125      */
126     private static final String GARBAGE_COLLECT = "-gc";
127 
128     /**
129      * The prefix environment variables must have.
130      *
131      * @parameter expression="env."
132      */
133     private String environmentVariablePrefix;
134 
135     /**
136      * @see org.apache.maven.plugin.Mojo#execute()
137      */
138     public void execute()
139         throws MojoExecutionException
140     {
141         try
142         {
143             final Map environment = this.getEnvironment();
144             if (this.startConsole != null && !this.startConsole.equals(Boolean.FALSE.toString()))
145             {
146                 boolean executed = false;
147                 this.printLine();
148                 while (true)
149                 {
150                     this.printConsolePrompt();
151                     String input = StringUtils.trimToEmpty(this.readLine());
152                     if (EXIT.equals(input))
153                     {
154                         break;
155                     }
156                     if (input.startsWith(EXECUTION_PROPERTY_TOKEN))
157                     {
158                         input = input.replaceFirst(
159                                 EXECUTION_PROPERTY_TOKEN,
160                                 "");
161                         int index = input.indexOf('=');
162                         String name;
163                         String value;
164                         if (index <= 0)
165                         {
166                             name = input.trim();
167                             value = "true";
168                         }
169                         else
170                         {
171                             name = input.substring(
172                                     0,
173                                     index).trim();
174                             value = input.substring(index + 1).trim();
175                         }
176                         if (value.startsWith(this.environmentVariablePrefix))
177                         {
178                             value = StringUtils.replace(
179                                     value,
180                                     this.environmentVariablePrefix,
181                                     "");
182                             if (environment.containsKey(value))
183                             {
184                                 value = ObjectUtils.toString(environment.get(value)).trim();
185                             }
186                         }
187                         this.executionProperties.put(
188                             name,
189                             value);
190                         System.setProperty(
191                             name,
192                             value);
193                         this.printExecutionProperties();
194                     }
195                     else if (LIST_PROPERTIES.equals(input))
196                     {
197                         this.printExecutionProperties();
198                     }
199                     else if (CLEAR_PROPERTIES.equals(input))
200                     {
201                         this.executionProperties.clear();
202                         this.printExecutionProperties();
203                     }
204                     else if (GARBAGE_COLLECT.equals(input))
205                     {
206                         // TODO FindBugs: Except for specific use in benchmarking, this is very dubious.
207                         System.gc();
208                     }
209                     else
210                     {
211                         try
212                         {
213                             executed = this.executeModules(input);
214 
215                             // - if nothing was executed, try a goal in the current project
216                             if (this.project != null && !executed && input.trim().length() > 0)
217                             {
218                                 executed = true;
219                                 final List<String> goals = Arrays.asList(input.split("\\s+"));
220                                 this.executeModules(
221                                     StringUtils.join(
222                                         this.project.getModules().iterator(),
223                                         ","),
224                                     goals,
225                                     true);
226                             }
227                         }
228                         catch (final Throwable throwable)
229                         {
230                             throwable.printStackTrace();
231                         }
232                         if (executed)
233                         {
234                             this.printLine();
235                         }
236                         Projects.instance().clear();
237                     }
238                 }
239             }
240             else
241             {
242                 this.executionProperties.putAll(this.session.getExecutionProperties());
243                 this.executeModules(this.modules);
244             }
245         }
246         catch (final Throwable throwable)
247         {
248             throw new MojoExecutionException("Error executing modules", throwable);
249         }
250     }
251 
252     private static final String GET_ENVIRONMENT_METHOD = "getenv";
253 
254     /**
255      * Retrieves the environment variables (will only work when running jdk5 or above).
256      *
257      * @return the environment variables.
258      */
259     private Map<String, String> getEnvironment()
260     {
261         final Map<String, String> variables = new HashMap<String, String>();
262         try
263         {
264             final Method method = System.class.getMethod(
265                     GET_ENVIRONMENT_METHOD);
266             final Object result = method.invoke(
267                     System.class);
268             if (result instanceof Map)
269             {
270                 variables.putAll((Map<String, String>)result);
271             }
272         }
273         catch (Exception exception)
274         {
275             // - ignore (means we can't retrieve the environment with the current JDK).
276         }
277         return variables;
278     }
279 
280     private void printExecutionProperties()
281     {
282         this.printLine();
283         this.printTextWithLine("| ------------- execution properties ------------- |");
284         for (final Iterator<Object> iterator = this.executionProperties.keySet().iterator(); iterator.hasNext();)
285         {
286             final String name = (String)iterator.next();
287             System.out.println("    " + name + " = " + this.executionProperties.getProperty(name));
288         }
289         this.printTextWithLine("| ------------------------------------------------ |");
290         this.printLine();
291     }
292 
293     /**
294      * Prints the prompt for the console
295      */
296     private void printConsolePrompt()
297     {
298         if (this.project != null)
299         {
300             this.printText("");
301             this.printText(this.project.getArtifactId() + ' ' + this.project.getVersion() + '>');
302         }
303     }
304 
305     /**
306      * Prints text to the console.
307      *
308      * @param text the text to print to the console;
309      */
310     private void printText(final String text)
311     {
312         System.out.print(text);
313         System.out.flush();
314     }
315 
316     /**
317      * Prints text with a new line to the console.
318      *
319      * @param text the text to print to the console.
320      */
321     private void printTextWithLine(final String text)
322     {
323         System.out.println(text);
324         System.out.flush();
325     }
326 
327     /**
328      * Prints a line to standard output.
329      */
330     private void printLine()
331     {
332         System.out.println();
333         System.out.flush();
334     }
335 
336     /**
337      * Reads a line from standard input and returns the value.
338      *
339      * @return the value read from standard input.
340      */
341     @SuppressWarnings("null")
342     private String readLine()
343     {
344         final BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
345         String inputString = null;
346         try
347         {
348             inputString = input.readLine();
349         }
350         catch (final IOException exception)
351         {
352             inputString = null;
353         }
354         return StringUtils.trimToNull(inputString);
355     }
356 
357     /**
358      * Creates all project modules and executes them.
359      *
360      * @param modules the comma separated list of modules to execute.
361      * @return true if any modules were executed, false otherwise.
362      * @throws MojoExecutionException
363      * @throws CycleDetectedException
364      * @throws LifecycleExecutionException
365      * @throws BuildFailureException
366      */
367     private boolean executeModules(final String modules)
368         throws MojoExecutionException
369     {
370         return this.executeModules(
371             modules,
372             null,
373             false);
374     }
375 
376     /**
377      * Creates all project modules and executes them.
378      *
379      * @param modules The list of modules to execute.
380      * @param goals the list of goals to execute (if null, the, goals will be retrieved from project map).
381      * @param sortProjects whether or not projects should be sorted and then executed or whether they should be executed in the
382      *                     order they're in.
383      * @throws CycleDetectedException
384      * @throws LifecycleExecutionException
385      * @throws MojoExecutionException
386      * @throws BuildFailureException
387      * @return true/false on whether or not any modules were executed
388      */
389     private boolean executeModules(
390         final String modules,
391         final List<String> goals,
392         boolean sortProjects)
393         throws MojoExecutionException
394     {
395         final Map<MavenProject, List<String>> projects = this.collectProjects(modules);
396         boolean executed = !projects.isEmpty();
397 
398         // - only execute if we have some projects
399         if (executed)
400         {
401             if (!sortProjects)
402             {
403                 for (final MavenProject project : projects.keySet())
404                 {
405                     List<String> projectGoals;
406                     if (goals == null)
407                     {
408                         projectGoals = projects.get(project);
409                         if (projectGoals.isEmpty())
410                         {
411                             projectGoals.addAll(this.goals);
412                         }
413                     }
414                     else
415                     {
416                         projectGoals = goals;
417                     }
418                     this.executeProjects(
419                         Collections.singletonList(project),
420                         projectGoals);
421                 }
422             }
423             else
424             {
425                 this.executeProjects(
426                     projects.keySet(),
427                     goals);
428             }
429         }
430         return executed;
431     }
432 
433     /**
434      * Executes the given maven <code>project</code>.
435      *
436      * @param projects the projects to execute.
437      * @param goals the goals to execute on the project.
438      * @throws MojoExecutionException
439      * @throws CycleDetectedException
440      * @throws LifecycleExecutionException
441      * @throws BuildFailureException
442      */
443     private void executeProjects(
444         final Collection<MavenProject> projects,
445         final List<String> goals)
446         throws MojoExecutionException
447     {
448         try
449         {
450             if (goals.isEmpty())
451             {
452                 goals.addAll(this.goals);
453             }
454             if (projects.size() > 1)
455             {
456                 this.getLog().info("Reactor build order:");
457             }
458             final ReactorManager reactorManager = new ReactorManager(new ArrayList<MavenProject>(projects));
459             for (final MavenProject project : reactorManager.getSortedProjects())
460             {
461                 this.getLog().info("  " + project.getName());
462             }
463 
464             final MavenSession projectSession =
465                 new MavenSession(
466                     this.session.getContainer(),
467                     this.session.getSettings(),
468                     this.session.getLocalRepository(),
469                     this.session.getEventDispatcher(),
470                     reactorManager,
471                     goals,
472                     this.baseDirectory.toString(),
473                     this.executionProperties,
474                     this.session.getStartTime());
475 
476             projectSession.setUsingPOMsFromFilesystem(true);
477             this.lifecycleExecutor.execute(
478                 projectSession,
479                 reactorManager,
480                 projectSession.getEventDispatcher());
481         }
482         catch (final Throwable throwable)
483         {
484             throw new MojoExecutionException("An error occurred while attempting to execute projects", throwable);
485         }
486     }
487 
488     /**
489      * Collects all project modules to execute.
490      *
491      * @param modules The list of modules to execute.
492      * @return the Map of collected projects (the key is the project, the value
493      *         the goals).
494      * @throws MojoExecutionException
495      */
496     private Map<MavenProject, List<String>> collectProjects(final String modules)
497         throws MojoExecutionException
498     {
499         final Map<MavenProject, List<String>> projects = new LinkedHashMap<MavenProject, List<String>>();
500         final Map<File, List<String>> poms = getModulePoms(modules);
501 
502         if (!poms.isEmpty())
503         {
504             for (final File pom : poms.keySet())
505             {
506                 try
507                 {
508                     final MavenProject project = ProjectUtils.getProject(
509                             this.projectBuilder,
510                             this.session,
511                             pom,
512                             this.getLog());
513                     if (project != null)
514                     {
515                         if (this.getLog().isDebugEnabled())
516                         {
517                             this.getLog().debug("Adding project " + project.getId());
518                         }
519                         projects.put(
520                             project,
521                             poms.get(pom));
522                     }
523                     else
524                     {
525                         if (this.getLog().isWarnEnabled())
526                         {
527                             this.getLog().warn("Could not load project from pom: " + pom + " - ignoring");
528                         }
529                     }
530                 }
531                 catch (ProjectBuildingException exception)
532                 {
533                     throw new MojoExecutionException("Error loading POM --> '" + pom + '\'', exception);
534                 }
535             }
536         }
537         return projects;
538     }
539 
540     /**
541      * Gets all POMs for the modules specified.
542      *
543      * @param moduleList the list of modules to execute.
544      * @return the list of module poms
545      */
546     private Map<File, List<String>> getModulePoms(final String moduleList)
547     {
548         final Map<File, List<String>> poms = new LinkedHashMap<File, List<String>>();
549         final String[] modules = moduleList != null ? moduleList.split(",") : null;
550 
551         final String goalPrefix = ":";
552         if (modules != null)
553         {
554             final int numberOfModules = modules.length;
555             for (int ctr = 0; ctr < numberOfModules; ctr++)
556             {
557                 String module = modules[ctr].trim();
558                 final List<String> goalsList = new ArrayList<String>();
559                 if (module.contains(goalPrefix))
560                 {
561                     final String[] goals = module.replaceAll(
562                             ".*(:\\[)|(\\])",
563                             "").split("\\+");
564                     if (goals != null)
565                     {
566                         final int numberOfGoals = goals.length;
567                         for (int ctr2 = 0; ctr2 < numberOfGoals; ctr2++)
568                         {
569                             final String goal = goals[ctr2].trim();
570                             goalsList.add(goal);
571                         }
572                     }
573                 }
574                 module = module.replaceAll(
575                         goalPrefix + "\\[.*\\]",
576                         "");
577                 final File pom = new File(this.baseDirectory, module + "/pom.xml");
578                 if (pom.isFile())
579                 {
580                     poms.put(
581                         pom,
582                         goalsList);
583                 }
584             }
585         }
586         return poms;
587     }
588 }