001 package org.apache.maven.cli;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.util.Date;
023
024 import org.apache.maven.execution.AbstractExecutionListener;
025 import org.apache.maven.execution.BuildFailure;
026 import org.apache.maven.execution.BuildSuccess;
027 import org.apache.maven.execution.BuildSummary;
028 import org.apache.maven.execution.ExecutionEvent;
029 import org.apache.maven.execution.MavenExecutionResult;
030 import org.apache.maven.execution.MavenSession;
031 import org.apache.maven.plugin.MojoExecution;
032 import org.apache.maven.project.MavenProject;
033 import org.codehaus.plexus.logging.Logger;
034
035 /**
036 * Logs execution events to a user-supplied logger.
037 *
038 * @author Benjamin Bentmann
039 */
040 public class ExecutionEventLogger
041 extends AbstractExecutionListener
042 {
043 private final Logger logger;
044
045 private static final int LINE_LENGTH = 72;
046
047 public ExecutionEventLogger( Logger logger )
048 {
049 if ( logger == null )
050 {
051 throw new IllegalArgumentException( "logger missing" );
052 }
053
054 this.logger = logger;
055 }
056
057 private static String chars( char c, int count )
058 {
059 StringBuilder buffer = new StringBuilder( count );
060
061 for ( int i = count; i > 0; i-- )
062 {
063 buffer.append( c );
064 }
065
066 return buffer.toString();
067 }
068
069 private static String getFormattedTime( long time )
070 {
071 // NOTE: DateFormat is not suitable to format timespans of 24h+
072
073 long h = time / ( 60 * 60 * 1000 );
074 long m = ( time - h * 60 * 60 * 1000 ) / ( 60 * 1000 );
075 long s = ( time - h * 60 * 60 * 1000 - m * 60 * 1000 ) / 1000;
076 long ms = time % 1000;
077
078 String format;
079 if ( h > 0 )
080 {
081 format = "%1$d:%2$02d:%3$02d.%4$03ds";
082 }
083 else if ( m > 0 )
084 {
085 format = "%2$d:%3$02d.%4$03ds";
086 }
087 else
088 {
089 format = "%3$d.%4$03ds";
090 }
091
092 return String.format( format, h, m, s, ms );
093 }
094
095 @Override
096 public void projectDiscoveryStarted( ExecutionEvent event )
097 {
098 if ( logger.isInfoEnabled() )
099 {
100 logger.info( "Scanning for projects..." );
101 }
102 }
103
104 @Override
105 public void sessionStarted( ExecutionEvent event )
106 {
107 if ( logger.isInfoEnabled() && event.getSession().getProjects().size() > 1 )
108 {
109 logger.info( chars( '-', LINE_LENGTH ) );
110
111 logger.info( "Reactor Build Order:" );
112
113 logger.info( "" );
114
115 for ( MavenProject project : event.getSession().getProjects() )
116 {
117 logger.info( project.getName() );
118 }
119 }
120 }
121
122 @Override
123 public void sessionEnded( ExecutionEvent event )
124 {
125 if ( logger.isInfoEnabled() )
126 {
127 if ( event.getSession().getProjects().size() > 1 )
128 {
129 logReactorSummary( event.getSession() );
130 }
131
132 logResult( event.getSession() );
133
134 logStats( event.getSession() );
135
136 logger.info( chars( '-', LINE_LENGTH ) );
137 }
138 }
139
140 private void logReactorSummary( MavenSession session )
141 {
142 logger.info( chars( '-', LINE_LENGTH ) );
143
144 logger.info( "Reactor Summary:" );
145
146 logger.info( "" );
147
148 MavenExecutionResult result = session.getResult();
149
150 for ( MavenProject project : session.getProjects() )
151 {
152 StringBuilder buffer = new StringBuilder( 128 );
153
154 buffer.append( project.getName() );
155
156 buffer.append( ' ' );
157 while ( buffer.length() < LINE_LENGTH - 21 )
158 {
159 buffer.append( '.' );
160 }
161 buffer.append( ' ' );
162
163 BuildSummary buildSummary = result.getBuildSummary( project );
164
165 if ( buildSummary == null )
166 {
167 buffer.append( "SKIPPED" );
168 }
169 else if ( buildSummary instanceof BuildSuccess )
170 {
171 buffer.append( "SUCCESS [" );
172 buffer.append( getFormattedTime( buildSummary.getTime() ) );
173 buffer.append( "]" );
174 }
175 else if ( buildSummary instanceof BuildFailure )
176 {
177 buffer.append( "FAILURE [" );
178 buffer.append( getFormattedTime( buildSummary.getTime() ) );
179 buffer.append( "]" );
180 }
181
182 logger.info( buffer.toString() );
183 }
184 }
185
186 private void logResult( MavenSession session )
187 {
188 logger.info( chars( '-', LINE_LENGTH ) );
189
190 if ( session.getResult().hasExceptions() )
191 {
192 logger.info( "BUILD FAILURE" );
193 }
194 else
195 {
196 logger.info( "BUILD SUCCESS" );
197 }
198 }
199
200 private void logStats( MavenSession session )
201 {
202 logger.info( chars( '-', LINE_LENGTH ) );
203
204 Date finish = new Date();
205
206 long time = finish.getTime() - session.getRequest().getStartTime().getTime();
207
208 String wallClock = session.getRequest().isThreadConfigurationPresent() ? " (Wall Clock)" : "";
209
210 logger.info( "Total time: " + getFormattedTime( time ) + wallClock );
211
212 logger.info( "Finished at: " + finish );
213
214 System.gc();
215
216 Runtime r = Runtime.getRuntime();
217
218 long MB = 1024 * 1024;
219
220 logger.info( "Final Memory: " + ( r.totalMemory() - r.freeMemory() ) / MB + "M/" + r.totalMemory() / MB + "M" );
221 }
222
223 @Override
224 public void projectSkipped( ExecutionEvent event )
225 {
226 if ( logger.isInfoEnabled() )
227 {
228 logger.info( chars( ' ', LINE_LENGTH ) );
229 logger.info( chars( '-', LINE_LENGTH ) );
230
231 logger.info( "Skipping " + event.getProject().getName() );
232 logger.info( "This project has been banned from the build due to previous failures." );
233
234 logger.info( chars( '-', LINE_LENGTH ) );
235 }
236 }
237
238 @Override
239 public void projectStarted( ExecutionEvent event )
240 {
241 if ( logger.isInfoEnabled() )
242 {
243 logger.info( chars( ' ', LINE_LENGTH ) );
244 logger.info( chars( '-', LINE_LENGTH ) );
245
246 logger.info( "Building " + event.getProject().getName() + " " + event.getProject().getVersion() );
247
248 logger.info( chars( '-', LINE_LENGTH ) );
249 }
250 }
251
252 @Override
253 public void mojoSkipped( ExecutionEvent event )
254 {
255 if ( logger.isWarnEnabled() )
256 {
257 logger.warn( "Goal " + event.getMojoExecution().getGoal()
258 + " requires online mode for execution but Maven is currently offline, skipping" );
259 }
260 }
261
262 /**
263 * <pre>--- mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId ---</pre>
264 */
265 @Override
266 public void mojoStarted( ExecutionEvent event )
267 {
268 if ( logger.isInfoEnabled() )
269 {
270 StringBuilder buffer = new StringBuilder( 128 );
271
272 buffer.append( "--- " );
273 append( buffer, event.getMojoExecution() );
274 append( buffer, event.getProject() );
275 buffer.append( " ---" );
276
277 logger.info( "" );
278 logger.info( buffer.toString() );
279 }
280 }
281
282 /**
283 * <pre>>>> mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId >>></pre>
284 */
285 @Override
286 public void forkStarted( ExecutionEvent event )
287 {
288 if ( logger.isInfoEnabled() )
289 {
290 StringBuilder buffer = new StringBuilder( 128 );
291
292 buffer.append( ">>> " );
293 append( buffer, event.getMojoExecution() );
294 append( buffer, event.getProject() );
295 buffer.append( " >>>" );
296
297 logger.info( "" );
298 logger.info( buffer.toString() );
299 }
300 }
301
302 /**
303 * <pre><<< mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId <<<</pre>
304 */
305 @Override
306 public void forkSucceeded( ExecutionEvent event )
307 {
308 if ( logger.isInfoEnabled() )
309 {
310 StringBuilder buffer = new StringBuilder( 128 );
311
312 buffer.append( "<<< " );
313 append( buffer, event.getMojoExecution() );
314 append( buffer, event.getProject() );
315 buffer.append( " <<<" );
316
317 logger.info( "" );
318 logger.info( buffer.toString() );
319 }
320 }
321
322 private void append( StringBuilder buffer, MojoExecution me )
323 {
324 buffer.append( me.getArtifactId() ).append( ':' ).append( me.getVersion() );
325 buffer.append( ':' ).append( me.getGoal() );
326 if ( me.getExecutionId() != null )
327 {
328 buffer.append( " (" ).append( me.getExecutionId() ).append( ')' );
329 }
330 }
331
332 private void append( StringBuilder buffer, MavenProject project )
333 {
334 buffer.append( " @ " ).append( project.getArtifactId() );
335 }
336
337 @Override
338 public void forkedProjectStarted( ExecutionEvent event )
339 {
340 if ( logger.isInfoEnabled() && event.getMojoExecution().getForkedExecutions().size() > 1 )
341 {
342 logger.info( chars( ' ', LINE_LENGTH ) );
343 logger.info( chars( '>', LINE_LENGTH ) );
344
345 logger.info( "Forking " + event.getProject().getName() + " " + event.getProject().getVersion() );
346
347 logger.info( chars( '>', LINE_LENGTH ) );
348 }
349 }
350
351 }