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>&lt;&lt;&lt; mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId &lt;&lt;&lt;</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    }