View Javadoc
1   package org.apache.maven.plugins.invoker;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
23  
24  import java.io.File;
25  import java.io.FileReader;
26  import java.io.IOException;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.apache.maven.plugin.MojoFailureException;
31  import org.apache.maven.plugins.invoker.model.BuildJob;
32  import org.apache.maven.plugin.logging.Log;
33  import org.apache.maven.shared.utils.io.IOUtil;
34  
35  /**
36   * Tracks a set of build jobs and their results.
37   *
38   * @author Benjamin Bentmann
39   */
40  class InvokerSession
41  {
42      private static final String SEPARATOR = buffer().strong(
43              "-------------------------------------------------" ).toString();
44  
45      private List<BuildJob> buildJobs;
46  
47      private List<BuildJob> failedJobs;
48  
49      private List<BuildJob> errorJobs;
50  
51      private List<BuildJob> successfulJobs;
52  
53      private List<BuildJob> skippedJobs;
54  
55      /**
56       * Creates a new empty session.
57       */
58      InvokerSession()
59      {
60          buildJobs = new ArrayList<>();
61      }
62  
63      /**
64       * Creates a session that initially contains the specified build jobs.
65       *
66       * @param buildJobs The build jobs to set, must not be <code>null</code>.
67       */
68      InvokerSession( List<BuildJob> buildJobs )
69      {
70          this.buildJobs = new ArrayList<>( buildJobs );
71      }
72  
73      /**
74       * Adds the specified build job to this session.
75       *
76       * @param buildJob The build job to add, must not be <code>null</code>.
77       */
78      public void addJob( BuildJob buildJob )
79      {
80          buildJobs.add( buildJob );
81  
82          resetStats();
83      }
84  
85      /**
86       * Sets the build jobs of this session.
87       *
88       * @param buildJobs The build jobs to set, must not be <code>null</code>.
89       */
90      public void setJobs( List<? extends BuildJob> buildJobs )
91      {
92          this.buildJobs = new ArrayList<>( buildJobs );
93  
94          resetStats();
95      }
96  
97      /**
98       * Gets the build jobs in this session.
99       *
100      * @return The build jobs in this session, can be empty but never <code>null</code>.
101      */
102     public List<BuildJob> getJobs()
103     {
104         return buildJobs;
105     }
106 
107     /**
108      * Gets the successful build jobs in this session.
109      *
110      * @return The successful build jobs in this session, can be empty but never <code>null</code>.
111      */
112     public List<BuildJob> getSuccessfulJobs()
113     {
114         updateStats();
115 
116         return successfulJobs;
117     }
118 
119     /**
120      * Gets the failed build jobs in this session.
121      *
122      * @return The failed build jobs in this session, can be empty but never <code>null</code>.
123      */
124     public List<BuildJob> getFailedJobs()
125     {
126         updateStats();
127 
128         return failedJobs;
129     }
130 
131     /**
132      * Gets the build jobs which had errors for this session.
133      *
134      * @return The build jobs in error for this session, can be empty but never <code>null</code>.
135      */
136     public List<BuildJob> getErrorJobs()
137     {
138         updateStats();
139 
140         return errorJobs;
141     }
142 
143     /**
144      * Gets the skipped build jobs in this session.
145      *
146      * @return The skipped build jobs in this session, can be empty but never <code>null</code>.
147      */
148     public List<BuildJob> getSkippedJobs()
149     {
150         updateStats();
151 
152         return skippedJobs;
153     }
154 
155     private void resetStats()
156     {
157         successfulJobs = null;
158         failedJobs = null;
159         skippedJobs = null;
160         errorJobs = null;
161     }
162 
163     private void updateStats()
164     {
165         if ( successfulJobs != null && skippedJobs != null && failedJobs != null && errorJobs != null )
166         {
167             return;
168         }
169 
170         successfulJobs = new ArrayList<>();
171         failedJobs = new ArrayList<>();
172         skippedJobs = new ArrayList<>();
173         errorJobs = new ArrayList<>();
174 
175         for ( BuildJob buildJob : buildJobs )
176         {
177             if ( BuildJob.Result.SUCCESS.equals( buildJob.getResult() ) )
178             {
179                 successfulJobs.add( buildJob );
180             }
181             else if ( BuildJob.Result.SKIPPED.equals( buildJob.getResult() ) )
182             {
183                 skippedJobs.add( buildJob );
184             }
185             else if ( BuildJob.Result.ERROR.equals( buildJob.getResult() ) )
186             {
187                 errorJobs.add( buildJob );
188             }
189             else if ( buildJob.getResult() != null )
190             {
191                 failedJobs.add( buildJob );
192             }
193         }
194     }
195 
196     /**
197      * Prints a summary of this session to the specified logger.
198      *
199      * @param logger The mojo logger to output messages to, must not be <code>null</code>.
200      * @param ignoreFailures A flag whether failures should be ignored or whether a build failure should be signaled.
201      */
202     public void logSummary( Log logger, boolean ignoreFailures )
203     {
204         updateStats();
205 
206         logger.info( SEPARATOR );
207         logger.info( "Build Summary:" );
208         logger.info( "  Passed: " + successfulJobs.size()
209                 + ", Failed: " + failedJobs.size()
210                 + ", Errors: " + errorJobs.size()
211                 + ", Skipped: " + skippedJobs.size() );
212         logger.info( SEPARATOR );
213 
214         logBuildJobList( logger, ignoreFailures, "The following builds failed:", failedJobs );
215         logBuildJobList( logger, ignoreFailures, "The following builds finished with error:", errorJobs );
216         logBuildJobList( logger, ignoreFailures, "The following builds was skipped:", skippedJobs );
217     }
218 
219     public void logFailedBuildLog( Log logger, boolean ignoreFailures )
220             throws MojoFailureException
221     {
222         List<BuildJob> jobToLogs = new ArrayList<>( failedJobs );
223         jobToLogs.addAll( errorJobs );
224 
225         for ( BuildJob buildJob: jobToLogs )
226         {
227             File buildLogFile = buildJob.getBuildlog() != null ? new File( buildJob.getBuildlog() ) : null;
228             if ( buildLogFile != null && buildLogFile.exists() )
229             {
230                 try
231                 {
232                     // prepare message with build.log in one string to omit begin [ERROR], [WARN]
233                     // so whole log will be displayed without decoration
234                     StringBuilder buildLogMessage = new StringBuilder( );
235                     buildLogMessage.append( System.lineSeparator() );
236                     buildLogMessage.append( System.lineSeparator() );
237                     buildLogMessage.append( "*** begin build.log for: " + buildJob.getProject() + " ***" );
238                     buildLogMessage.append( System.lineSeparator() );
239                     buildLogMessage.append( IOUtil.toString( new FileReader( buildLogFile ) ) );
240                     buildLogMessage.append( "*** end build.log for: " + buildJob.getProject() + " ***" );
241                     buildLogMessage.append( System.lineSeparator() );
242 
243                     logWithLevel( logger, ignoreFailures, SEPARATOR );
244                     logWithLevel( logger, ignoreFailures,  buildLogMessage.toString() );
245                     logWithLevel( logger, ignoreFailures, SEPARATOR );
246                     logWithLevel( logger, ignoreFailures, "" );
247 
248                 }
249                 catch ( IOException e )
250                 {
251                     throw new MojoFailureException( e.getMessage(), e );
252                 }
253             }
254         }
255     }
256 
257 
258     /**
259      * Handles the build failures in this session.
260      *
261      * @param logger The mojo logger to output messages to, must not be <code>null</code>.
262      * @param ignoreFailures A flag whether failures should be ignored or whether a build failure should be signaled.
263      * @throws MojoFailureException If failures are present and not ignored.
264      */
265     public void handleFailures( Log logger, boolean ignoreFailures )
266         throws MojoFailureException
267     {
268         updateStats();
269 
270         if ( !failedJobs.isEmpty() )
271         {
272             String message = failedJobs.size() + " build" + ( failedJobs.size() == 1 ? "" : "s" ) + " failed.";
273 
274             if ( ignoreFailures )
275             {
276                 logger.warn( "Ignoring that " + message );
277             }
278             else
279             {
280                 throw new MojoFailureException( message + " See console output above for details." );
281             }
282         }
283 
284         if ( !errorJobs.isEmpty() )
285         {
286             String message = errorJobs.size() + " build" + ( errorJobs.size() == 1 ? "" : "s" ) + " in error.";
287 
288             if ( ignoreFailures )
289             {
290                 logger.warn( "Ignoring that " + message );
291             }
292             else
293             {
294                 throw new MojoFailureException( message + " See console output above for details." );
295             }
296         }
297     }
298 
299     /**
300      * Log list of jobs.
301      *
302      * @param logger logger to write
303      * @param warn flag indicate log level
304      * @param buildJobs jobs to list
305      */
306     private void logBuildJobList( Log logger, boolean warn, String header, List<BuildJob> buildJobs )
307     {
308         if ( buildJobs.isEmpty() )
309         {
310             return;
311         }
312 
313         logWithLevel( logger, warn, header );
314 
315         for ( BuildJob buildJob : buildJobs )
316         {
317             logWithLevel( logger, warn, "*  " + buildJob.getProject() );
318         }
319 
320         logger.info( SEPARATOR );
321     }
322 
323     /**
324      * Log message in correct level depends on flag.
325      *
326      * @param logger logger to write
327      * @param warn flag indicate log level
328      * @param message message to write
329      */
330     private void logWithLevel( Log logger, boolean warn, String message )
331     {
332 
333         if ( warn )
334         {
335             logger.warn( message );
336         }
337         else
338         {
339             logger.error( message );
340         }
341     }
342 }