View Javadoc
1   package com.soebes.maven.extensions;
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 java.util.Collections;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Map.Entry;
27  
28  import javax.inject.Named;
29  import javax.inject.Singleton;
30  
31  import org.apache.maven.execution.ExecutionEvent;
32  import org.apache.maven.execution.ExecutionEvent.Type;
33  import org.apache.maven.execution.MavenExecutionRequest;
34  import org.apache.maven.execution.MavenExecutionResult;
35  import org.apache.maven.project.DependencyResolutionRequest;
36  import org.apache.maven.project.DependencyResolutionResult;
37  import org.apache.maven.project.MavenProject;
38  import org.eclipse.aether.RepositoryEvent;
39  import org.eclipse.aether.RepositoryEvent.EventType;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  import com.soebes.maven.extensions.artifact.DeployTimer;
44  import com.soebes.maven.extensions.artifact.DownloadTimer;
45  import com.soebes.maven.extensions.artifact.InstallTimer;
46  import com.soebes.maven.extensions.metadata.MetadataDeploymentTimer;
47  import com.soebes.maven.extensions.metadata.MetadataDownloadTimer;
48  import com.soebes.maven.extensions.metadata.MetadataInstallTimer;
49  
50  /**
51   * @author Karl Heinz Marbaise <a href="mailto:kama@soebes.de">kama@soebes.de</a>
52   */
53  @Named
54  @Singleton
55  public class BuildTimeProfiler
56      extends LifeCycleOrdering
57  {
58      private final Logger LOGGER = LoggerFactory.getLogger( getClass() );
59  
60      final List<String> lifeCyclePhases;
61  
62      private final DiscoveryTimer discoveryTimer;
63  
64      private final GoalTimer goalTimer;
65  
66      private final MojoTimer mojoTimer;
67  
68      private final ProjectTimer projectTimer;
69  
70      private final SessionTimer sessionTimer;
71  
72      private final InstallTimer installTimer;
73  
74      private final DeployTimer deployTimer;
75  
76      private final DownloadTimer downloadTimer;
77  
78      private final MetadataDownloadTimer metadataDownloadTimer;
79  
80      private final MetadataDeploymentTimer metadataDeploymentTimer;
81  
82      private final MetadataInstallTimer metadataInstallTimer;
83  
84      private final ForkTimer forkTimer;
85  
86      private final ProjectTimer forkProject;
87  
88      public BuildTimeProfiler()
89      {
90          LOGGER.debug( "LifeCycleProfiler ctor called." );
91          this.lifeCyclePhases = Collections.<String>synchronizedList( new LinkedList<String>() );
92          this.discoveryTimer = new DiscoveryTimer();
93          this.goalTimer = new GoalTimer();
94          this.mojoTimer = new MojoTimer();
95          this.projectTimer = new ProjectTimer();
96          this.sessionTimer = new SessionTimer();
97          this.installTimer = new InstallTimer();
98          this.deployTimer = new DeployTimer();
99          this.downloadTimer = new DownloadTimer();
100 
101         this.metadataDownloadTimer = new MetadataDownloadTimer();
102         this.metadataDeploymentTimer = new MetadataDeploymentTimer();
103         this.metadataInstallTimer = new MetadataInstallTimer();
104         this.forkTimer = new ForkTimer();
105         this.forkProject = new ProjectTimer();
106 
107     }
108 
109     @Override
110     public void init( Context context )
111         throws Exception
112     {
113         super.init( context );
114         LOGGER.info( "Maven Build Time Profiler started. (Version {})", BuildTimeProfilerVersion.getVersion() );
115 
116         // Is this always in the context? Based on Maven Core yes.
117         String workingDirectory = (String) context.getData().get( "workingDirectory" );
118         LOGGER.debug( "MBTP: workingDirectory: " + workingDirectory );
119 
120         String multiModuleProjectDirectory = (String) context.getData().get( "multiModuleProjectDirectory" );
121         LOGGER.debug( "MBTP: multiModuleProjectDirectory: " + multiModuleProjectDirectory );
122 
123         // Properties systemProperties = (Properties) context.getData().get( "systemProperties" );
124         // for ( String propName : systemProperties.stringPropertyNames() )
125         // {
126         // String propValue = systemProperties.getProperty( propName );
127         // LOGGER.info( " systemProperty " + propName + ": '" + propValue + "'" );
128         // }
129         //
130         // Properties userProperties = (Properties) context.getData().get( "userProperties" );
131         // for ( String propName : userProperties.stringPropertyNames() )
132         // {
133         // String propValue = userProperties.getProperty( propName );
134         // LOGGER.info( " userProperty " + propName + ": '" + propValue + "'" );
135         // }
136         // data.put( "plexus", container );
137         // data.put( "workingDirectory", cliRequest.workingDirectory );
138         // data.put( "systemProperties", cliRequest.systemProperties );
139         // data.put( "userProperties", cliRequest.userProperties );
140         // data.put( "versionProperties", CLIReportingUtils.getBuildProperties() );
141 
142     }
143 
144     @Override
145     public void onEvent( Object event )
146         throws Exception
147     {
148         try
149         {
150             if ( event instanceof ExecutionEvent )
151             {
152                 executionEventHandler( (ExecutionEvent) event );
153             }
154             else if ( event instanceof org.eclipse.aether.RepositoryEvent )
155             {
156                 repositoryEventHandler( (RepositoryEvent) event );
157             }
158             else if ( event instanceof MavenExecutionRequest )
159             {
160                 executionRequestEventHandler( (MavenExecutionRequest) event );
161             }
162             else if ( event instanceof MavenExecutionResult )
163             {
164                 executionResultEventHandler( (MavenExecutionResult) event );
165             }
166             else if ( event instanceof DependencyResolutionRequest )
167             {
168                 dependencyResolutionRequest( (DependencyResolutionRequest) event );
169             }
170             else if ( event instanceof DependencyResolutionResult )
171             {
172                 dependencyResolutionResult( (DependencyResolutionResult) event );
173             }
174             // The following event type is available since Maven 3.3.1+
175             // else if ( event instanceof DefaultSettingsBuildingRequest) {
176             // DefaultSettingsBuildingRequest r = null;
177             // r.getGlobalSettingsFile();
178             // r.getGlobalSettingsSource();
179             // r.getSystemProperties();
180             // r.getUserSettingsFile();
181             // r.getUserSettingsSource();
182             //
183             // r.setGlobalSettingsFile( globalSettingsFile );
184             // r.setGlobalSettingsSource( globalSettingsSource );
185             // r.setSystemProperties( systemProperties );
186             // r.setUserProperties( userProperties );
187             // r.setUserSettingsFile( userSettingsFile );
188             // r.setUserSettingsSource( userSettingsSource );
189             // }
190             // The following event type is available since Maven 3.3.1+
191             // else if (event instanceof DefaultSettingsBuildingRequest) {
192             //
193             // DefaultSettingsBuildingRequest r = null;
194             // r.getGlobalSettingsSource().getLocation()
195             // }
196             // The following event type is available since Maven 3.3.1+
197             // else if (event instanceof DefaultToolchainsBuildingRequest) {
198             // DefaultToolchainsBuildingRequest r = null;
199             // r.getGlobalToolchainsSource().
200             // }
201             // The following event type is available since Maven 3.3.1+
202             // else if (event instanceof DefaultToolchainsBuildingResult) {
203             // DefaultToolchainsBuildingResult r = null;
204             // r.getEffectiveToolchains();
205             // r.getProblems();
206             // }
207             else
208             {
209                 // TODO: What kind of event we haven't considered?
210                 LOGGER.debug( "MBTP: Event {}", event.getClass().getCanonicalName() );
211             }
212         }
213         catch ( Exception e )
214         {
215             LOGGER.error( "MBTP: Exception", e );
216         }
217     }
218 
219     @Override
220     public void close()
221     {
222         LOGGER.debug( "MBTP: done." );
223     }
224 
225     private void dependencyResolutionResult( DependencyResolutionResult event )
226     {
227         LOGGER.debug( "MBTP: dependencyResolutionResult() {}", event.getResolvedDependencies().size() );
228     }
229 
230     private void dependencyResolutionRequest( DependencyResolutionRequest event )
231     {
232         LOGGER.debug( "MBTP: dependencyResolutionRequest()" );
233     }
234 
235     private void repositoryEventHandler( org.eclipse.aether.RepositoryEvent repositoryEvent )
236     {
237         EventType type = repositoryEvent.getType();
238         switch ( type )
239         {
240             case ARTIFACT_DOWNLOADING:
241                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
242                 downloadTimer.start( repositoryEvent );
243                 break;
244             case ARTIFACT_DOWNLOADED:
245                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
246                 downloadTimer.stop( repositoryEvent );
247                 break;
248 
249             case ARTIFACT_DEPLOYING:
250                 deployTimer.start( repositoryEvent );
251                 break;
252             case ARTIFACT_DEPLOYED:
253                 deployTimer.stop( repositoryEvent );
254                 break;
255 
256             case ARTIFACT_INSTALLING:
257                 installTimer.start( repositoryEvent );
258                 break;
259             case ARTIFACT_INSTALLED:
260                 installTimer.stop( repositoryEvent );
261                 break;
262 
263             case METADATA_DEPLOYING:
264                 metadataDeploymentTimer.start( repositoryEvent );
265                 break;
266             case METADATA_DEPLOYED:
267                 metadataDeploymentTimer.stop( repositoryEvent );
268                 break;
269 
270             case METADATA_DOWNLOADING:
271                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
272                 metadataDownloadTimer.start( repositoryEvent );
273                 break;
274             case METADATA_DOWNLOADED:
275                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
276                 metadataDownloadTimer.stop( repositoryEvent );
277                 break;
278 
279             case METADATA_INSTALLING:
280                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
281                 metadataInstallTimer.start( repositoryEvent );
282                 break;
283             case METADATA_INSTALLED:
284                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
285                 metadataInstallTimer.stop( repositoryEvent );
286                 break;
287 
288             case ARTIFACT_RESOLVING:
289             case ARTIFACT_RESOLVED:
290             case ARTIFACT_DESCRIPTOR_INVALID:
291             case ARTIFACT_DESCRIPTOR_MISSING:
292             case METADATA_RESOLVED:
293             case METADATA_RESOLVING:
294             case METADATA_INVALID:
295                 // Those events are not recorded.
296                 break;
297 
298             default:
299                 LOGGER.error( "MBTP: repositoryEventHandler {}", type );
300                 break;
301         }
302     }
303 
304     private void executionEventHandler( ExecutionEvent executionEvent )
305     {
306         LOGGER.debug( "executionEventHandler: {}", executionEvent.getType() );
307         Type type = executionEvent.getType();
308         switch ( type )
309         {
310             case ProjectDiscoveryStarted:
311                 // Start reading the pom files..
312                 discoveryTimer.discoveryStart();
313                 break;
314             case SessionStarted:
315                 // Reading of pom files done and structure now there.
316                 discoveryTimer.discoveryStop();
317                 sessionTimer.sessionStart();
318                 break;
319             case SessionEnded:
320                 // Everything is done.
321                 sessionTimer.sessionStop();
322                 break;
323 
324             case ForkStarted:
325                 forkTimer.start();
326                 break;
327             case ForkFailed:
328             case ForkSucceeded:
329                 forkTimer.stop();
330                 break;
331 
332             case ForkedProjectStarted:
333                 forkProject.projectStart( executionEvent );
334                 break;
335             case ForkedProjectFailed:
336             case ForkedProjectSucceeded:
337                 forkProject.projectStop( executionEvent );
338                 break;
339 
340             case MojoStarted:
341                 String phaseStart = executionEvent.getMojoExecution().getLifecyclePhase();
342                 // Key: phase, project, mojo
343                 if ( phaseStart == null )
344                 {
345                     goalTimer.mojoStart( executionEvent );
346                 }
347                 else
348                 {
349                     collectAllLifeCylcePhases( phaseStart );
350                     mojoTimer.mojoStart( executionEvent );
351                 }
352                 break;
353 
354             case MojoFailed:
355             case MojoSucceeded:
356             case MojoSkipped:
357                 String phaseStop = executionEvent.getMojoExecution().getLifecyclePhase();
358                 if ( phaseStop == null )
359                 {
360                     goalTimer.mojoStop( executionEvent );
361                 }
362                 else
363                 {
364                     mojoTimer.mojoStop( executionEvent );
365                 }
366                 break;
367 
368             case ProjectStarted:
369                 projectTimer.projectStart( executionEvent );
370                 break;
371 
372             case ProjectFailed:
373             case ProjectSucceeded:
374             case ProjectSkipped:
375                 projectTimer.projectStop( executionEvent );
376                 break;
377 
378             default:
379                 LOGGER.error( "MBTP: executionEventHandler: {}", type );
380                 break;
381         }
382 
383     }
384 
385     private void executionRequestEventHandler( MavenExecutionRequest event )
386     {
387         // event.getUserProperties().put( "revision", "1.2.3-SNAPSHOT" );
388         // event.getSystemProperties().put( "revision", "1.2.3-SNAPSHOT" );
389         // Can we do something more useful here?
390         LOGGER.debug( "MBTP: executionRequestEventHandler: {}", event.getExecutionListener() );
391     }
392 
393     private void executionResultEventHandler( MavenExecutionResult event )
394     {
395         orderLifeCycleOnPreparedOrder( lifeCyclePhases );
396 
397         LOGGER.debug( "MBTP: executionResultEventHandler: {}", event.getProject() );
398 
399         LOGGER.info( "--             Maven Build Time Profiler Summary                      --" );
400         LOGGER.info( "------------------------------------------------------------------------" );
401 
402         discoveryTimer.report();
403 
404         if ( mojoTimer.hasEvents() )
405         {
406             LOGGER.info( "Project Build Time (reactor order):" );
407             LOGGER.info( "" );
408             for ( MavenProject mavenProject : event.getTopologicallySortedProjects() )
409             {
410                 LOGGER.info( "{}:", mavenProject.getName() );
411 
412                 for ( String phase : lifeCyclePhases )
413                 {
414                     ProjectKey projectKey = mavenProjectToProjectKey( mavenProject );
415 
416                     if ( !mojoTimer.hasTimeForProjectAndPhase( projectKey, phase ) )
417                     {
418                         continue;
419                     }
420 
421                     long timeForPhaseAndProjectInMillis =
422                         mojoTimer.getTimeForProjectAndPhaseInMillis( projectKey, phase );
423                     LOGGER.info( "    {} ms : {}", String.format( "%8d", timeForPhaseAndProjectInMillis ), phase );
424 
425                 }
426 
427             }
428             LOGGER.info( "------------------------------------------------------------------------" );
429             LOGGER.info( "Lifecycle Phase summary:" );
430             LOGGER.info( "" );
431             for ( String phase : lifeCyclePhases )
432             {
433                 long timeForPhaseInMillis = mojoTimer.getTimeForPhaseInMillis( phase );
434                 LOGGER.info( "{} ms : {}", String.format( "%8d", timeForPhaseInMillis ), phase );
435             }
436 
437             // List all plugins per phase
438             LOGGER.info( "------------------------------------------------------------------------" );
439             LOGGER.info( "Plugins in lifecycle Phases:" );
440             LOGGER.info( "" );
441             for ( String phase : lifeCyclePhases )
442             {
443                 LOGGER.info( "{}:", phase );
444                 Map<ProjectMojo, SystemTime> plugisInPhase = mojoTimer.getPluginsInPhase( phase );
445                 for ( Entry<ProjectMojo, SystemTime> pluginInPhase : plugisInPhase.entrySet() )
446                 {
447                     LOGGER.info( "{} ms: {}", String.format( "%8d", pluginInPhase.getValue().getElapsedTime() ),
448                                  pluginInPhase.getKey().getMojo().getFullId() );
449                 }
450 
451             }
452             LOGGER.info( "------------------------------------------------------------------------" );
453         }
454 
455         if ( goalTimer.hasEvents() )
456         {
457             LOGGER.info( "Plugins directly called via goals:" );
458             LOGGER.info( "" );
459             goalTimer.report();
460             LOGGER.info( "------------------------------------------------------------------------" );
461         }
462 
463         installTimer.report();
464         downloadTimer.report();
465         deployTimer.report();
466         metadataInstallTimer.report();
467         metadataDownloadTimer.report();
468         metadataDeploymentTimer.report();
469 
470         forkTimer.report();
471         forkProject.report();
472     }
473 
474     private ProjectKey mavenProjectToProjectKey( MavenProject project )
475     {
476         return new ProjectKey( project.getGroupId(), project.getArtifactId(), project.getVersion() );
477     }
478 
479     private void collectAllLifeCylcePhases( String phase )
480     {
481         // Phase can be null if you call maven via:
482         // mvn site:stage (no life cycle started.)
483         if ( phase == null )
484         {
485             return;
486         }
487         LOGGER.debug( "collectAllLifeCyclePhases({})", phase );
488         if ( !lifeCyclePhases.contains( phase ) )
489         {
490             lifeCyclePhases.add( phase );
491         }
492     }
493 
494 }