View Javadoc

1   package org.apache.maven.jelly.tags.maven;
2   
3   /* ====================================================================
4    *   Licensed to the Apache Software Foundation (ASF) under one or more
5    *   contributor license agreements.  See the NOTICE file distributed with
6    *   this work for additional information regarding copyright ownership.
7    *   The ASF licenses this file to You under the Apache License, Version 2.0
8    *   (the "License"); you may not use this file except in compliance with
9    *   the License.  You may obtain a copy of the License at
10   *
11   *       http://www.apache.org/licenses/LICENSE-2.0
12   *
13   *   Unless required by applicable law or agreed to in writing, software
14   *   distributed under the License is distributed on an "AS IS" BASIS,
15   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *   See the License for the specific language governing permissions and
17   *   limitations under the License.
18   * ====================================================================
19   */
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import org.apache.commons.jelly.JellyTagException;
28  import org.apache.commons.jelly.MissingAttributeException;
29  import org.apache.commons.jelly.XMLOutput;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.maven.MavenConstants;
33  import org.apache.maven.MavenException;
34  import org.apache.maven.MavenUtils;
35  import org.apache.maven.project.Project;
36  
37  /**
38   * Reactor tag that processes a set of project descriptors taking into
39   * consideration the inter-project dependencies.
40   * <p/>
41   * Used for building a set of projects in the correct order to satisfy any
42   * dependencies among the projects being processed.
43   *
44   * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
45   * @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
46   * @author <a href="mailto:evenisse@ifrance.com">Emmanuel Venisse</a>
47   * @version $Id: ReactorTag.java 517014 2007-03-11 21:15:50Z ltheussl $
48   * @todo We use the basedir for the glob, but we need to set the descriptor.
49   * @todo better use of inheritance from MavenTag
50   */
51  public class ReactorTag
52      extends MavenTag
53  {
54      private static final Log LOGGER = LogFactory.getLog( ReactorTag.class );
55  
56      /**
57       * The glob used to select projects within the base directory.
58       */
59      private String glob;
60  
61      /**
62       * Projects to include.
63       */
64      private String includes;
65  
66      /**
67       * Projects to exclude.
68       */
69      private String excludes;
70  
71      /**
72       * Banner to display when each project is processed.
73       */
74      private String banner;
75  
76      /**
77       * project base directory
78       */
79      private File basedir;
80  
81      /**
82       * The name of the context variable to store the project collection in.
83       */
84      private String collectionVar = "reactorProjects";
85  
86      /**
87       * Whether to actually execute the default or specified goals, or
88       * simply collect the projects in order.
89       */
90      private boolean collectOnly = false;
91  
92      /**
93       * Whether to sort the projects by dependencies.
94       */
95      private boolean sort = true;
96  
97      /**
98       * Post processing flag. If this is set then we will hold on to the
99       * processed projects, otherwise they will be dumped.
100      */
101     private boolean postProcessing = false;
102 
103     /**
104      * Storage for projects that failed in the build.
105      */
106     private Collection failedProjects = new ArrayList();
107 
108     /**
109      * List of Project objects to use instead of glob or includes.
110      */
111     private List projectList = null;
112 
113     // ----------------------------------------------------------------------
114     // A C C E S S O R S
115     // ----------------------------------------------------------------------
116 
117     /**
118      * Set the project list.
119      *
120      * @param projectList the project list
121      */
122     public void setProjectList( List projectList )
123     {
124         this.projectList = projectList;
125     }
126 
127     /**
128      * Get the project list.
129      *
130      * @return the project list
131      */
132     public List getProjectList()
133     {
134         return projectList;
135     }
136 
137     /**
138      * Setter for the basedir property
139      * XXX if the method it overrides is deprecated, is this also deprecated?
140      *
141      * @param basedir the base directory for execution of the project
142      */
143     public void setBasedir( File basedir )
144     {
145         this.basedir = basedir;
146     }
147 
148     /**
149      * Getter for the basedir property
150      *
151      * @return the base directory for execution of the project
152      */
153     public File getBasedir()
154     {
155         return this.basedir;
156     }
157 
158     public void setSort( boolean sort )
159     {
160         this.sort = sort;
161     }
162 
163     /**
164      * Set the collectOnly attribute. This controls whether reactor
165      * will actually execute any goals on the projects, or simply
166      * collect the sorted set of projects included in this reactor
167      * invocation.
168      *
169      * @param collectOnly if true, don't execute project goals; simply
170      *                    gather the sorted project collection and store. Otherwise,
171      *                    operate as before (execute either specified goals or default goal
172      *                    for each project).
173      */
174     public void setCollectOnly( boolean collectOnly )
175     {
176         this.collectOnly = collectOnly;
177     }
178 
179     /**
180      * Set the collectionVar attribute. This controls the context
181      * variable which will contain the sorted set of projects included
182      * in this reactor invocation.
183      *
184      * @param collectionVar The context variable for the project collection
185      *                      discovered here.
186      */
187     public void setCollectionVar( String collectionVar )
188     {
189         this.collectionVar = collectionVar;
190     }
191 
192     /**
193      * Set the postProcessing attribute.
194      *
195      * @param postProcessing
196      */
197     public void setPostProcessing( boolean postProcessing )
198     {
199         this.postProcessing = postProcessing;
200     }
201 
202     /**
203      * Get the postProcessing attribute.
204      *
205      * @return The
206      */
207     public boolean getPostProcessing()
208     {
209         return postProcessing;
210     }
211 
212     /**
213      * Set the glob used to find projects.
214      *
215      * @param glob The glob used to find projects.
216      * @deprecated Use includes/excludes instead.
217      */
218     public void setGlob( String glob )
219     {
220         LOGGER.warn( "\nDEPRECATION WARNING: use the 'includes' attribute instead of the 'glob' attribute.\n" );
221         this.glob = glob;
222     }
223 
224     /**
225      * Get the glob used to find projects.
226      *
227      * @return String The glob.
228      */
229     public String getGlob()
230     {
231         return glob;
232     }
233 
234     /**
235      * Set the includes used to find projects.
236      *
237      * @param includes The includes used to find projects.
238      */
239     public void setIncludes( String includes )
240     {
241         this.includes = includes;
242     }
243 
244     /**
245      * Get the includes used to find projects.
246      *
247      * @return String The includes.
248      */
249     public String getIncludes()
250     {
251         return includes;
252     }
253 
254     /**
255      * Set the excludes used to find projects.
256      *
257      * @param excludes The excludes used to find projects.
258      */
259     public void setExcludes( String excludes )
260     {
261         this.excludes = excludes;
262     }
263 
264     /**
265      * Get the excludes used to find projects.
266      *
267      * @return String The excludes.
268      */
269     public String getExcludes()
270     {
271         return excludes;
272     }
273 
274     /**
275      * Set the banner to be displayed for each project while
276      * it is being processed.
277      *
278      * @param banner The banner to use for each processed project.
279      */
280     public void setBanner( String banner )
281     {
282         this.banner = banner;
283     }
284 
285     /**
286      * Get the banner to be displayed for project while it
287      * is being processed.
288      *
289      * @return String The banner.
290      */
291     public String getBanner()
292     {
293         if ( banner == null )
294         {
295             banner = "Processing";
296         }
297 
298         return banner;
299     }
300 
301     /**
302      * Execute the body of the reactor tag.
303      *
304      * @param output The output sink.
305      * @throws JellyTagException If an error occurs while processing the tag.
306      */
307     public void doTag( XMLOutput output )
308         throws MissingAttributeException, JellyTagException
309     {
310         checkAttribute( getBasedir(), "basedir" );
311 
312         if ( ( getGlob() == null ) && ( getIncludes() == null ) && ( projectList == null ) )
313         {
314             throw new MissingAttributeException( "glob|includes|projectList" );
315         }
316 
317         LOGGER.info( "Starting the reactor..." );
318 
319         List sortedProjects = null;
320         try
321         {
322             sortedProjects = getSortedProjects();
323         }
324         catch ( Exception e )
325         {
326             throw new JellyTagException( "Error getting projects", e );
327         }
328 
329         LOGGER.info( "Our processing order:" );
330 
331         for ( Iterator i = sortedProjects.iterator(); i.hasNext(); )
332         {
333             Project p = (Project) i.next();
334             LOGGER.info( p.getName() );
335         }
336 
337         ArrayList reactorProjects = new ArrayList();
338 
339         Runtime r = Runtime.getRuntime();
340         for ( Iterator i = sortedProjects.iterator(); i.hasNext(); )
341         {
342             // The basedir needs to be set for the project
343             // We just need the descriptor.
344 
345             Project project = (Project) i.next();
346             beforeProject( project );
347 
348             final long mb = 1024 * 1024;
349             LOGGER.info( "+----------------------------------------" );
350             LOGGER.info( "| " + getBanner() + " " + project.getName() );
351             LOGGER.info( "| Memory: " + ( ( r.totalMemory() - r.freeMemory() ) / mb ) + "M/" + ( r.totalMemory() / mb )
352                 + "M" );
353             LOGGER.info( "+----------------------------------------" );
354 
355             // We only try to attain goals if they have been set. The reactor
356             // might be in use to collect project information for the purpose
357             // of, say, generating a sites from a set of components.
358             List goalList = null;
359             if ( getGoals() != null )
360             {
361                 goalList = MavenUtils.getGoalListFromCsv( getGoals() );
362             }
363 
364             beforeLaunchGoals( project );
365 
366             try
367             {
368                 if ( !collectOnly )
369                 {
370                     getMavenContext().getMavenSession().attainGoals( project, goalList );
371                 }
372             }
373             catch ( Exception e )
374             {
375                 onException( project, e );
376                 // TODO: there is a risk that continuing may leave the project in an inconsistent state.
377                 //  -- attainGoals needs to split fatal from non-fatal exceptions and we only ignore non-fatal
378                 if ( !isIgnoreFailures() )
379                 {
380                     throw new JellyTagException( "Reactor subproject failure occurred", e );
381                 }
382             }
383 
384             afterLaunchGoals( project );
385             afterProject( project );
386 
387             if ( getPostProcessing() || collectOnly )
388             {
389                 reactorProjects.add( project );
390             }
391         }
392 
393         getContext().setVariable( collectionVar, reactorProjects );
394         Collection c = (Collection) getContext().getVariable( MavenConstants.FAILED_PROJECTS );
395         if ( c == null )
396         {
397             c = new ArrayList( failedProjects );
398         }
399         else
400         {
401             c.addAll( failedProjects );
402         }
403         getContext().setVariable( MavenConstants.FAILED_PROJECTS, c );
404     }
405 
406     /**
407      * Get a list of projects to process, sorted by dependency processing order.
408      * Maintained as a separate method so that the original projects and dependency resolver memory is freed.
409      *
410      * @return the project list
411      */
412     private List getSortedProjects()
413         throws MavenException
414     {
415         if ( projectList != null )
416         {
417             LOGGER.debug( "using existing list of projects: " + projectList );
418             return projectList;
419         }
420 
421         String projectIncludes;
422         if ( getGlob() != null )
423         {
424             projectIncludes = getGlob();
425         }
426         else
427         {
428             projectIncludes = getIncludes();
429         }
430 
431         List projects = MavenUtils.getProjects( getBasedir(), projectIncludes, getExcludes(), getMavenContext()
432             .getMavenSession().getRootContext() );
433 
434         if ( sort )
435         {
436             DependencyResolver dr = new DependencyResolver();
437             dr.setProjects( projects );
438 
439             return dr.getSortedDependencies( false );
440         }
441         else
442         {
443             return projects;
444         }
445     }
446 
447     /**
448      * This method is running before launching a project.
449      *
450      * @param project the currentProject
451      */
452     public void beforeProject( Project project )
453     {
454     }
455 
456     /**
457      * This method is running before launching project goals.
458      *
459      * @param project the currentProject
460      */
461     public void beforeLaunchGoals( Project project )
462     {
463     }
464 
465     /**
466      * This method is running after launching project goals.
467      *
468      * @param project the currentProject
469      */
470     public void afterLaunchGoals( Project project )
471     {
472     }
473 
474     /**
475      * This method is running after launching a project.
476      *
477      * @param project the currentProject
478      */
479     public void afterProject( Project project )
480     {
481     }
482 
483     /**
484      * This method is running when an exception occurs whatever
485      * the ignoreFailures value.
486      *
487      * @param project the currentProject
488      * @param e       the exception
489      */
490     public void onException( Project project, Exception e )
491     {
492         getContext().setVariable( MavenConstants.BUILD_FAILURE, "true" );
493         failedProjects.add( project );
494     }
495 
496     /**
497      * This method is running when an exception occurs in
498      * dependency resolution.
499      *
500      * @param e the exception
501      */
502     public void onDependencyResolutionException( Exception e )
503     {
504     }
505 }