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, true, "The following builds were skipped:", skippedJobs );
217     }
218 
219     public void logFailedBuildLog( Log logger, boolean ignoreFailures )
220             throws MojoFailureException
221     {
222         updateStats();
223 
224         List<BuildJob> jobToLogs = new ArrayList<>( failedJobs );
225         jobToLogs.addAll( errorJobs );
226 
227         for ( BuildJob buildJob: jobToLogs )
228         {
229             File buildLogFile = buildJob.getBuildlog() != null ? new File( buildJob.getBuildlog() ) : null;
230             if ( buildLogFile != null && buildLogFile.exists() )
231             {
232                 try
233                 {
234                     // prepare message with build.log in one string to omit begin [ERROR], [WARN]
235                     // so whole log will be displayed without decoration
236                     StringBuilder buildLogMessage = new StringBuilder( );
237                     buildLogMessage.append( System.lineSeparator() );
238                     buildLogMessage.append( System.lineSeparator() );
239                     buildLogMessage.append( "*** begin build.log for: " + buildJob.getProject() + " ***" );
240                     buildLogMessage.append( System.lineSeparator() );
241                     try ( FileReader buildLogReader = new FileReader( buildLogFile ) )
242                     {
243                         buildLogMessage.append( IOUtil.toString( buildLogReader ) );
244                     }
245                     buildLogMessage.append( "*** end build.log for: " + buildJob.getProject() + " ***" );
246                     buildLogMessage.append( System.lineSeparator() );
247 
248                     logWithLevel( logger, ignoreFailures, SEPARATOR );
249                     logWithLevel( logger, ignoreFailures,  buildLogMessage.toString() );
250                     logWithLevel( logger, ignoreFailures, SEPARATOR );
251                     logWithLevel( logger, ignoreFailures, "" );
252 
253                 }
254                 catch ( IOException e )
255                 {
256                     throw new MojoFailureException( e.getMessage(), e );
257                 }
258             }
259         }
260     }
261 
262 
263     /**
264      * Handles the build failures in this session.
265      *
266      * @param logger The mojo logger to output messages to, must not be <code>null</code>.
267      * @param ignoreFailures A flag whether failures should be ignored or whether a build failure should be signaled.
268      * @throws MojoFailureException If failures are present and not ignored.
269      */
270     public void handleFailures( Log logger, boolean ignoreFailures )
271         throws MojoFailureException
272     {
273         updateStats();
274 
275         if ( !failedJobs.isEmpty() )
276         {
277             String message = failedJobs.size() + " build" + ( failedJobs.size() == 1 ? "" : "s" ) + " failed.";
278 
279             if ( ignoreFailures )
280             {
281                 logger.warn( "Ignoring that " + message );
282             }
283             else
284             {
285                 throw new MojoFailureException( message + " See console output above for details." );
286             }
287         }
288 
289         if ( !errorJobs.isEmpty() )
290         {
291             String message = errorJobs.size() + " build" + ( errorJobs.size() == 1 ? "" : "s" ) + " in error.";
292 
293             if ( ignoreFailures )
294             {
295                 logger.warn( "Ignoring that " + message );
296             }
297             else
298             {
299                 throw new MojoFailureException( message + " See console output above for details." );
300             }
301         }
302     }
303 
304     /**
305      * Log list of jobs.
306      *
307      * @param logger logger to write
308      * @param warn flag indicate log level
309      * @param buildJobs jobs to list
310      */
311     private void logBuildJobList( Log logger, boolean warn, String header, List<BuildJob> buildJobs )
312     {
313         if ( buildJobs.isEmpty() )
314         {
315             return;
316         }
317 
318         logWithLevel( logger, warn, header );
319 
320         for ( BuildJob buildJob : buildJobs )
321         {
322             logWithLevel( logger, warn, "*  " + buildJob.getProject() );
323         }
324 
325         logger.info( SEPARATOR );
326     }
327 
328     /**
329      * Log message in correct level depends on flag.
330      *
331      * @param logger logger to write
332      * @param warn flag indicate log level
333      * @param message message to write
334      */
335     private void logWithLevel( Log logger, boolean warn, String message )
336     {
337 
338         if ( warn )
339         {
340             logger.warn( message );
341         }
342         else
343         {
344             logger.error( message );
345         }
346     }
347 }