Updated Branches:
  refs/heads/master 07fecbd9e -> b99658c94

MNG-5549 introduced MojoExecutionListener and ProjectExecutionListener

Signed-off-by: Igor Fedorenko <ifedore...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/maven/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven/commit/bc84e6c7
Tree: http://git-wip-us.apache.org/repos/asf/maven/tree/bc84e6c7
Diff: http://git-wip-us.apache.org/repos/asf/maven/diff/bc84e6c7

Branch: refs/heads/master
Commit: bc84e6c7d307f119d82d5ae8082cc5e4f7eb7996
Parents: 07fecbd
Author: Igor Fedorenko <ifedore...@apache.org>
Authored: Sat Dec 14 19:43:42 2013 -0500
Committer: Igor Fedorenko <ifedore...@apache.org>
Committed: Sat Dec 14 19:43:42 2013 -0500

----------------------------------------------------------------------
 .../maven/execution/MojoExecutionListener.java  |  46 +++++++
 .../execution/ProjectExecutionListener.java     |  49 +++++++
 .../execution/scope/MojoExecutionListener.java  |  48 -------
 .../scope/WeakMojoExecutionListener.java        |  50 ++++++++
 .../scope/internal/MojoExecutionScope.java      |  36 ++++--
 .../CompoundProjectExecutionListener.java       |  76 +++++++++++
 .../internal/LifecycleModuleBuilder.java        |  34 ++++-
 .../plugin/CompoundMojoExecutionListener.java   |  67 ++++++++++
 .../maven/plugin/DefaultBuildPluginManager.java |  33 ++++-
 .../DelegatingMojoExecutionListener.java        |  61 +++++++++
 .../DelegatingProjectExecutionListener.java     |  66 ++++++++++
 .../maven/lifecycle/LifecycleExecutorTest.java  | 127 +++++++++++++++++++
 12 files changed, 626 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/execution/MojoExecutionListener.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/execution/MojoExecutionListener.java
 
b/maven-core/src/main/java/org/apache/maven/execution/MojoExecutionListener.java
new file mode 100644
index 0000000..6b824eb
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/execution/MojoExecutionListener.java
@@ -0,0 +1,46 @@
+package org.apache.maven.execution;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.execution.scope.WeakMojoExecutionListener;
+import org.apache.maven.plugin.Mojo;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Extension point that allows build extensions observe and possibly veto mojo 
executions.
+ * 
+ * @see WeakMojoExecutionListener
+ * @since 3.1.2
+ * @provisional This interface is part of work in progress and can be changed 
or removed without notice.
+ */
+public interface MojoExecutionListener
+{
+    public void beforeMojoExecution( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo )
+        throws MojoExecutionException;
+
+    public void afterMojoExecutionSuccess( MavenSession session, MavenProject 
project, MojoExecution execution,
+                                           Mojo mojo )
+        throws MojoExecutionException;
+
+    public void afterExecutionFailure( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo,
+                                       Throwable cause );
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/execution/ProjectExecutionListener.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/execution/ProjectExecutionListener.java
 
b/maven-core/src/main/java/org/apache/maven/execution/ProjectExecutionListener.java
new file mode 100644
index 0000000..d3d9739
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/execution/ProjectExecutionListener.java
@@ -0,0 +1,49 @@
+package org.apache.maven.execution;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+import org.apache.maven.lifecycle.LifecycleExecutionException;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Extension point that allows build extensions observe and possibly veto 
project build execution.
+ * 
+ * @see ExecutionListener
+ * @see MojoExecutionListener
+ * @since 3.1.2
+ * @provisional This interface is part of work in progress and can be changed 
or removed without notice.
+ */
+public interface ProjectExecutionListener
+{
+    public void beforeProjectExecution( MavenSession session, MavenProject 
project )
+        throws LifecycleExecutionException;
+
+    public void beforeProjectLifecycleExecution( MavenSession session, 
MavenProject project,
+                                                 List<MojoExecution> 
executionPlan )
+        throws LifecycleExecutionException;
+
+    public void afterProjectExecutionSuccess( MavenSession session, 
MavenProject project )
+        throws LifecycleExecutionException;
+
+    public void afterProjectExecutionFailure( MavenSession session, 
MavenProject project, Throwable cause );
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/execution/scope/MojoExecutionListener.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/execution/scope/MojoExecutionListener.java
 
b/maven-core/src/main/java/org/apache/maven/execution/scope/MojoExecutionListener.java
deleted file mode 100644
index 7a9c84b..0000000
--- 
a/maven-core/src/main/java/org/apache/maven/execution/scope/MojoExecutionListener.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.apache.maven.execution.scope;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.maven.plugin.MojoExecutionException;
-
-/**
- * Helper interface that allows mojo execution scoped components to be 
notified before execution start and after
- * execution completion. The main purpose of this interface is to allow mojo 
execution scoped components perform setup
- * before and cleanup after mojo execution.
- * <p>
- * TODO decide if Mojo should be passed as parameter of callback methods
- * 
- * @author igor
- * @since 3.1.2
- * @provisional This interface is part of work in progress and can be changed 
or removed without notice.
- */
-public interface MojoExecutionListener
-{
-    // TODO decide if this is needed
-    // public void beforeExecution() throws MojoExecutionException;
-
-    public void afterMojoExecutionSuccess()
-        throws MojoExecutionException;
-
-    // TODO decide if this is needed.
-    // public void afterExecutionFailure(Throwable t) throws 
MojoExecutionException;
-
-    public void afterMojoExecutionAlways()
-        throws MojoExecutionException;
-}

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/execution/scope/WeakMojoExecutionListener.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/execution/scope/WeakMojoExecutionListener.java
 
b/maven-core/src/main/java/org/apache/maven/execution/scope/WeakMojoExecutionListener.java
new file mode 100644
index 0000000..5537106
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/execution/scope/WeakMojoExecutionListener.java
@@ -0,0 +1,50 @@
+package org.apache.maven.execution.scope;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.MojoExecutionListener;
+import org.apache.maven.plugin.Mojo;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Extension point that allows build extensions observe and possibly veto mojo 
executions.
+ * <p>
+ * Unlike {@link MojoExecutionListener}, this extension point does not trigger 
instantiation of the component, hence
+ * "weak" class name prefix. Only applies to mojo execution scoped components.
+ * 
+ * @see MojoExecutionListener
+ * @since 3.1.2
+ * @provisional This interface is part of work in progress and can be changed 
or removed without notice.
+ */
+public interface WeakMojoExecutionListener
+{
+    public void beforeMojoExecution( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo )
+        throws MojoExecutionException;
+
+    public void afterMojoExecutionSuccess( MavenSession session, MavenProject 
project, MojoExecution execution,
+                                           Mojo mojo )
+        throws MojoExecutionException;
+
+    public void afterExecutionFailure( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo,
+                                       Throwable cause );
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScope.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScope.java
 
b/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScope.java
index ce169e1..80badfc 100644
--- 
a/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScope.java
+++ 
b/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScope.java
@@ -26,8 +26,10 @@ import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.apache.maven.execution.MavenSession;
-import org.apache.maven.execution.scope.MojoExecutionListener;
+import org.apache.maven.execution.MojoExecutionListener;
 import org.apache.maven.execution.scope.MojoExecutionScoped;
+import org.apache.maven.execution.scope.WeakMojoExecutionListener;
+import org.apache.maven.plugin.Mojo;
 import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.project.MavenProject;
@@ -46,7 +48,7 @@ import com.google.inject.util.Providers;
 @Named
 @Singleton
 public class MojoExecutionScope
-    implements Scope
+    implements Scope, MojoExecutionListener
 {
     private static final Provider<Object> SEEDED_KEY_PROVIDER = new 
Provider<Object>()
     {
@@ -138,7 +140,7 @@ public class MojoExecutionScope
                 }
 
                 T provided = (T) state.provided.get( key );
-                if ( provided == null )
+                if ( provided == null && unscoped != null )
                 {
                     provided = unscoped.get();
                     state.provided.put( key, provided );
@@ -174,28 +176,40 @@ public class MojoExecutionScope
         };
     }
 
-    public void afterExecutionSuccess()
+    public void beforeMojoExecution( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo )
         throws MojoExecutionException
     {
         for ( Object provided : getScopeState().provided.values() )
         {
-            if ( provided instanceof MojoExecutionListener )
+            if ( provided instanceof WeakMojoExecutionListener )
             {
-                ( (MojoExecutionListener) provided 
).afterMojoExecutionSuccess();
-                // TODO maybe deal with multiple MojoExecutionExceptions
+                ( (WeakMojoExecutionListener) provided ).beforeMojoExecution( 
session, project, execution, mojo );
             }
         }
     }
 
-    public void afterExecutionAlways()
+    public void afterMojoExecutionSuccess( MavenSession session, MavenProject 
project, MojoExecution execution,
+                                           Mojo mojo )
         throws MojoExecutionException
     {
         for ( Object provided : getScopeState().provided.values() )
         {
-            if ( provided instanceof MojoExecutionListener )
+            if ( provided instanceof WeakMojoExecutionListener )
             {
-                ( (MojoExecutionListener) provided 
).afterMojoExecutionAlways();
-                // TODO maybe deal with multiple MojoExecutionExceptions
+                ( (WeakMojoExecutionListener) provided 
).afterMojoExecutionSuccess( session, project, execution, mojo );
+            }
+        }
+    }
+
+    public void afterExecutionFailure( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo,
+                                       Throwable cause )
+    {
+        for ( Object provided : getScopeState().provided.values() )
+        {
+            if ( provided instanceof WeakMojoExecutionListener )
+            {
+                ( (WeakMojoExecutionListener) provided 
).afterExecutionFailure( session, project, execution, mojo,
+                                                                               
 cause );
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/lifecycle/internal/CompoundProjectExecutionListener.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/CompoundProjectExecutionListener.java
 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/CompoundProjectExecutionListener.java
new file mode 100644
index 0000000..8bdd210
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/CompoundProjectExecutionListener.java
@@ -0,0 +1,76 @@
+package org.apache.maven.lifecycle.internal;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.ProjectExecutionListener;
+import org.apache.maven.lifecycle.LifecycleExecutionException;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.project.MavenProject;
+
+class CompoundProjectExecutionListener
+    implements ProjectExecutionListener
+{
+    private final Collection<ProjectExecutionListener> listeners;
+
+    public CompoundProjectExecutionListener( 
Collection<ProjectExecutionListener> listeners )
+    {
+        this.listeners = listeners; // NB this is live injected collection
+    }
+
+    public void beforeProjectExecution( MavenSession session, MavenProject 
project )
+        throws LifecycleExecutionException
+    {
+        for ( ProjectExecutionListener listener : listeners )
+        {
+            listener.beforeProjectExecution( session, project );
+        }
+    }
+
+    public void beforeProjectLifecycleExecution( MavenSession session, 
MavenProject project,
+                                                 List<MojoExecution> 
executionPlan )
+        throws LifecycleExecutionException
+    {
+        for ( ProjectExecutionListener listener : listeners )
+        {
+            listener.beforeProjectLifecycleExecution( session, project, 
executionPlan );
+        }
+    }
+
+    public void afterProjectExecutionSuccess( MavenSession session, 
MavenProject project )
+        throws LifecycleExecutionException
+    {
+        for ( ProjectExecutionListener listener : listeners )
+        {
+            listener.afterProjectExecutionSuccess( session, project );
+        }
+    }
+
+    public void afterProjectExecutionFailure( MavenSession session, 
MavenProject project, Throwable cause )
+    {
+        for ( ProjectExecutionListener listener : listeners )
+        {
+            listener.afterProjectExecutionFailure( session, project, cause );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
index c8a3cf4..95f1c19 100644
--- 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
+++ 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
@@ -19,20 +19,23 @@ package org.apache.maven.lifecycle.internal;
  * under the License.
  */
 
+import java.util.HashSet;
+import java.util.List;
+
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.execution.BuildSuccess;
 import org.apache.maven.execution.ExecutionEvent;
 import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.ProjectExecutionListener;
 import org.apache.maven.lifecycle.MavenExecutionPlan;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.plexus.component.annotations.Component;
 import org.codehaus.plexus.component.annotations.Requirement;
 
-import java.util.HashSet;
-
 /**
  * Builds one or more lifecycles for a full module
- *
+ * 
  * @since 3.0
  * @author Benjamin Bentmann
  * @author Jason van Zyl
@@ -53,6 +56,18 @@ public class LifecycleModuleBuilder
     @Requirement
     private ExecutionEventCatapult eventCatapult;
 
+    private ProjectExecutionListener projectExecutionListener;
+
+    // this tricks plexus-component-metadata generate required metadata
+    @Requirement
+    private List<ProjectExecutionListener> projectExecutionListeners;
+
+    public void setProjectExecutionListeners( final 
List<ProjectExecutionListener> listeners )
+    {
+        this.projectExecutionListeners = listeners;
+        this.projectExecutionListener = new CompoundProjectExecutionListener( 
listeners );
+    }
+
     public void buildProject( MavenSession session, ReactorContext 
reactorContext, MavenProject currentProject,
                               TaskSegment taskSegment )
     {
@@ -75,16 +90,23 @@ public class LifecycleModuleBuilder
                 return;
             }
 
+            BuilderCommon.attachToThread( currentProject );
+
+            projectExecutionListener.beforeProjectExecution( rootSession, 
currentProject );
+
             eventCatapult.fire( ExecutionEvent.Type.ProjectStarted, session, 
null );
 
-            BuilderCommon.attachToThread( currentProject );
             MavenExecutionPlan executionPlan =
                 builderCommon.resolveBuildPlan( session, currentProject, 
taskSegment, new HashSet<Artifact>() );
+            List<MojoExecution> mojoExecutions = 
executionPlan.getMojoExecutions();
 
-            mojoExecutor.execute( session, executionPlan.getMojoExecutions(), 
reactorContext.getProjectIndex() );
+            projectExecutionListener.beforeProjectLifecycleExecution( 
rootSession, currentProject, mojoExecutions );
+            mojoExecutor.execute( session, mojoExecutions, 
reactorContext.getProjectIndex() );
 
             long buildEndTime = System.currentTimeMillis();
 
+            projectExecutionListener.afterProjectExecutionSuccess( 
rootSession, currentProject );
+
             reactorContext.getResult().addBuildSummary(
                 new BuildSuccess( currentProject, buildEndTime - 
buildStartTime ) );
 
@@ -93,6 +115,8 @@ public class LifecycleModuleBuilder
         catch ( Exception e )
         {
             builderCommon.handleBuildError( reactorContext, rootSession, 
session, currentProject, e, buildStartTime );
+
+            projectExecutionListener.afterProjectExecutionFailure( session, 
currentProject, e );
         }
         finally
         {

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/plugin/CompoundMojoExecutionListener.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/plugin/CompoundMojoExecutionListener.java
 
b/maven-core/src/main/java/org/apache/maven/plugin/CompoundMojoExecutionListener.java
new file mode 100644
index 0000000..7b5ff66
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/plugin/CompoundMojoExecutionListener.java
@@ -0,0 +1,67 @@
+package org.apache.maven.plugin;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Collection;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.MojoExecutionListener;
+import org.apache.maven.project.MavenProject;
+
+class CompoundMojoExecutionListener
+    implements MojoExecutionListener
+{
+
+    private final Collection<MojoExecutionListener> listeners;
+
+    public CompoundMojoExecutionListener( Collection<MojoExecutionListener> 
listeners )
+    {
+        this.listeners = listeners; // NB this is live injected collection
+    }
+
+    public void beforeMojoExecution( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo )
+        throws MojoExecutionException
+    {
+        for ( MojoExecutionListener listener : listeners )
+        {
+            listener.beforeMojoExecution( session, project, execution, mojo );
+        }
+    }
+
+    public void afterMojoExecutionSuccess( MavenSession session, MavenProject 
project, MojoExecution execution,
+                                           Mojo mojo )
+        throws MojoExecutionException
+    {
+        for ( MojoExecutionListener listener : listeners )
+        {
+            listener.afterMojoExecutionSuccess( session, project, execution, 
mojo );
+        }
+    }
+
+    public void afterExecutionFailure( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo,
+                                       Throwable cause )
+    {
+        for ( MojoExecutionListener listener : listeners )
+        {
+            listener.afterExecutionFailure( session, project, execution, mojo, 
cause );
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
 
b/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
index 949a228..5a10053 100644
--- 
a/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
+++ 
b/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
@@ -24,6 +24,7 @@ import java.io.PrintStream;
 import java.util.List;
 
 import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.MojoExecutionListener;
 import org.apache.maven.execution.scope.internal.MojoExecutionScope;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
@@ -52,6 +53,18 @@ public class DefaultBuildPluginManager
     @Requirement
     private MojoExecutionScope scope;
 
+    private MojoExecutionListener mojoExecutionListener;
+
+    // this tricks plexus-component-metadata generate required metadata
+    @Requirement( role = MojoExecutionListener.class )
+    private List<MojoExecutionListener> mojoExecutionListeners;
+
+    public void setMojoExecutionListeners( final List<MojoExecutionListener> 
listeners )
+    {
+        this.mojoExecutionListeners = listeners;
+        this.mojoExecutionListener = new CompoundMojoExecutionListener( 
listeners );
+    }
+
     /**
      * @param plugin
      * @param repositories
@@ -112,9 +125,11 @@ public class DefaultBuildPluginManager
             // MavenProjectHelper.attachArtifact(..).
             try
             {
+                mojoExecutionListener.beforeMojoExecution( oldSession, 
project, mojoExecution, mojo );
+
                 mojo.execute();
 
-                scope.afterExecutionSuccess();
+                mojoExecutionListener.afterMojoExecutionSuccess( oldSession, 
project, mojoExecution, mojo );
             }
             catch ( ClassCastException e )
             {
@@ -128,10 +143,14 @@ public class DefaultBuildPluginManager
         }
         catch ( PluginContainerException e )
         {
+            mojoExecutionListener.afterExecutionFailure( oldSession, project, 
mojoExecution, mojo, e );
+
             throw new PluginExecutionException( mojoExecution, project, e );
         }
         catch ( NoClassDefFoundError e )
         {
+            mojoExecutionListener.afterExecutionFailure( oldSession, project, 
mojoExecution, mojo, e );
+
             ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
             PrintStream ps = new PrintStream( os );
             ps.println( "A required class was missing while executing " + 
mojoDescriptor.getId() + ": "
@@ -144,6 +163,8 @@ public class DefaultBuildPluginManager
         }
         catch ( LinkageError e )
         {
+            mojoExecutionListener.afterExecutionFailure( oldSession, project, 
mojoExecution, mojo, e );
+
             ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
             PrintStream ps = new PrintStream( os );
             ps.println( "An API incompatibility was encountered while 
executing " + mojoDescriptor.getId() + ": "
@@ -156,6 +177,8 @@ public class DefaultBuildPluginManager
         }
         catch ( ClassCastException e )
         {
+            mojoExecutionListener.afterExecutionFailure( oldSession, project, 
mojoExecution, mojo, e );
+
             ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
             PrintStream ps = new PrintStream( os );
             ps.println( "A type incompatibility occured while executing " + 
mojoDescriptor.getId() + ": "
@@ -164,10 +187,14 @@ public class DefaultBuildPluginManager
 
             throw new PluginExecutionException( mojoExecution, project, 
os.toString(), e );
         }
-        finally
+        catch ( RuntimeException e )
         {
-            scope.afterExecutionAlways();
+            mojoExecutionListener.afterExecutionFailure( oldSession, project, 
mojoExecution, mojo, e );
 
+            throw e;
+        }
+        finally
+        {
             mavenPluginManager.releaseMojo( mojo, mojoExecution );
 
             scope.exit();

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/test/java/org/apache/maven/lifecycle/DelegatingMojoExecutionListener.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/test/java/org/apache/maven/lifecycle/DelegatingMojoExecutionListener.java
 
b/maven-core/src/test/java/org/apache/maven/lifecycle/DelegatingMojoExecutionListener.java
new file mode 100644
index 0000000..d58329c
--- /dev/null
+++ 
b/maven-core/src/test/java/org/apache/maven/lifecycle/DelegatingMojoExecutionListener.java
@@ -0,0 +1,61 @@
+package org.apache.maven.lifecycle;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.MojoExecutionListener;
+import org.apache.maven.plugin.Mojo;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+
+@Named
+@Singleton
+public class DelegatingMojoExecutionListener
+    implements MojoExecutionListener
+{
+    private final List<MojoExecutionListener> listeners = new 
CopyOnWriteArrayList<MojoExecutionListener>();
+
+    public void beforeMojoExecution( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo )
+        throws MojoExecutionException
+    {
+        for ( MojoExecutionListener listener : listeners )
+        {
+            listener.beforeMojoExecution( session, project, execution, mojo );
+        }
+    }
+
+    public void afterMojoExecutionSuccess( MavenSession session, MavenProject 
project, MojoExecution execution,
+                                           Mojo mojo )
+        throws MojoExecutionException
+    {
+        for ( MojoExecutionListener listener : listeners )
+        {
+            listener.afterMojoExecutionSuccess( session, project, execution, 
mojo );
+        }
+    }
+
+    public void afterExecutionFailure( MavenSession session, MavenProject 
project, MojoExecution execution, Mojo mojo,
+                                       Throwable cause )
+    {
+        for ( MojoExecutionListener listener : listeners )
+        {
+            listener.afterExecutionFailure( session, project, execution, mojo, 
cause );
+        }
+    }
+
+    public void addMojoExecutionListener( MojoExecutionListener listener )
+    {
+        this.listeners.add( listener );
+    }
+
+    public void removeMojoExecutionListener( MojoExecutionListener listener )
+    {
+        this.listeners.remove( listener );
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/test/java/org/apache/maven/lifecycle/DelegatingProjectExecutionListener.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/test/java/org/apache/maven/lifecycle/DelegatingProjectExecutionListener.java
 
b/maven-core/src/test/java/org/apache/maven/lifecycle/DelegatingProjectExecutionListener.java
new file mode 100644
index 0000000..27b53c6
--- /dev/null
+++ 
b/maven-core/src/test/java/org/apache/maven/lifecycle/DelegatingProjectExecutionListener.java
@@ -0,0 +1,66 @@
+package org.apache.maven.lifecycle;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.ProjectExecutionListener;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.project.MavenProject;
+
+@Named
+@Singleton
+public class DelegatingProjectExecutionListener
+    implements ProjectExecutionListener
+{
+    private final List<ProjectExecutionListener> listeners = new 
CopyOnWriteArrayList<ProjectExecutionListener>();
+
+    public void beforeProjectExecution( MavenSession session, MavenProject 
project )
+        throws LifecycleExecutionException
+    {
+        for ( ProjectExecutionListener listener : listeners )
+        {
+            listener.beforeProjectExecution( session, project );
+        }
+    }
+
+    public void beforeProjectLifecycleExecution( MavenSession session, 
MavenProject project,
+                                                 List<MojoExecution> 
executionPlan )
+        throws LifecycleExecutionException
+    {
+        for ( ProjectExecutionListener listener : listeners )
+        {
+            listener.beforeProjectLifecycleExecution( session, project, 
executionPlan );
+        }
+    }
+
+    public void afterProjectExecutionSuccess( MavenSession session, 
MavenProject project )
+        throws LifecycleExecutionException
+    {
+        for ( ProjectExecutionListener listener : listeners )
+        {
+            listener.afterProjectExecutionSuccess( session, project );
+        }
+    }
+
+    public void afterProjectExecutionFailure( MavenSession session, 
MavenProject project, Throwable cause )
+    {
+        for ( ProjectExecutionListener listener : listeners )
+        {
+            listener.afterProjectExecutionFailure( session, project, cause );
+        }
+    }
+
+    public void addProjectExecutionListener( ProjectExecutionListener listener 
)
+    {
+        this.listeners.add( listener );
+    }
+
+    public void removeProjectExecutionListener( ProjectExecutionListener 
listener )
+    {
+        this.listeners.remove( listener );
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/bc84e6c7/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java
----------------------------------------------------------------------
diff --git 
a/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java
 
b/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java
index 82e7e93..b983e55 100644
--- 
a/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java
+++ 
b/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java
@@ -18,11 +18,15 @@ package org.apache.maven.lifecycle;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.maven.AbstractCoreMavenComponentTestCase;
 import org.apache.maven.exception.ExceptionHandler;
 import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.MojoExecutionListener;
+import org.apache.maven.execution.ProjectDependencyGraph;
+import org.apache.maven.execution.ProjectExecutionListener;
 import 
org.apache.maven.lifecycle.internal.DefaultLifecycleTaskSegmentCalculator;
 import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
 import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator;
@@ -31,9 +35,12 @@ import 
org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculator;
 import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
 import org.apache.maven.lifecycle.internal.TaskSegment;
 import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.Mojo;
 import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoNotFoundException;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.project.MavenProject;
 import org.codehaus.plexus.component.annotations.Requirement;
 import org.codehaus.plexus.util.xml.Xpp3Dom;
 
@@ -380,4 +387,124 @@ public class LifecycleExecutorTest
         assertEquals("1.0", execution.getConfiguration().getChild( "version" 
).getAttribute( "default-value" ));
     }
 
+    public void testExecutionListeners()
+        throws Exception
+    {
+        final File pom = getProject( "project-basic" );
+        final MavenSession session = createMavenSession( pom );
+        session.setProjectDependencyGraph( new ProjectDependencyGraph()
+        {
+            public List<MavenProject> getUpstreamProjects( MavenProject 
project, boolean transitive )
+            {
+                return Collections.emptyList();
+            }
+
+            public List<MavenProject> getSortedProjects()
+            {
+                return Collections.singletonList( session.getCurrentProject() 
);
+            }
+
+            public List<MavenProject> getDownstreamProjects( MavenProject 
project, boolean transitive )
+            {
+                return Collections.emptyList();
+            }
+        } );
+
+        final List<String> log = new ArrayList<String>();
+
+        MojoExecutionListener mojoListener = new MojoExecutionListener()
+        {
+            public void beforeMojoExecution( MavenSession session, 
MavenProject project, MojoExecution execution,
+                                             Mojo mojo )
+                throws MojoExecutionException
+            {
+                log.add( "beforeMojoExecution " + project.getArtifactId() + 
":" + execution.getExecutionId() );
+            }
+
+            public void afterMojoExecutionSuccess( MavenSession session, 
MavenProject project, MojoExecution execution,
+                                                   Mojo mojo )
+                throws MojoExecutionException
+            {
+                log.add( "afterMojoExecutionSuccess " + 
project.getArtifactId() + ":" + execution.getExecutionId() );
+            }
+
+            public void afterExecutionFailure( MavenSession session, 
MavenProject project, MojoExecution execution,
+                                               Mojo mojo, Throwable cause )
+            {
+                log.add( "afterExecutionFailure " + project.getArtifactId() + 
":" + execution.getExecutionId() );
+            }
+        };
+        ProjectExecutionListener projectListener = new 
ProjectExecutionListener()
+        {
+            public void beforeProjectExecution( MavenSession session, 
MavenProject project )
+                throws LifecycleExecutionException
+            {
+                log.add( "beforeProjectExecution " + project.getArtifactId() );
+            }
+
+            public void beforeProjectLifecycleExecution( MavenSession session, 
MavenProject project,
+                                                         List<MojoExecution> 
executionPlan )
+                throws LifecycleExecutionException
+            {
+                log.add( "beforeProjectLifecycleExecution " + 
project.getArtifactId() );
+            }
+
+            public void afterProjectExecutionSuccess( MavenSession session, 
MavenProject project )
+                throws LifecycleExecutionException
+            {
+                log.add( "afterProjectExecutionSuccess " + 
project.getArtifactId() );
+            }
+
+            public void afterProjectExecutionFailure( MavenSession session, 
MavenProject project, Throwable cause )
+            {
+                log.add( "afterProjectExecutionFailure " + 
project.getArtifactId() );
+            }
+        };
+        lookup( DelegatingProjectExecutionListener.class 
).addProjectExecutionListener( projectListener );
+        lookup( DelegatingMojoExecutionListener.class 
).addMojoExecutionListener( mojoListener );
+
+        try
+        {
+            lifecycleExecutor.execute( session );
+        }
+        finally
+        {
+            lookup( DelegatingProjectExecutionListener.class 
).removeProjectExecutionListener( projectListener );
+            lookup( DelegatingMojoExecutionListener.class 
).removeMojoExecutionListener( mojoListener );
+        }
+
+        List<String> expectedLog = Arrays.asList( "beforeProjectExecution 
project-basic", //
+                                                  
"beforeProjectLifecycleExecution project-basic", //
+                                                  "beforeMojoExecution 
project-basic:default-resources", //
+                                                  "afterMojoExecutionSuccess 
project-basic:default-resources", //
+                                                  "beforeMojoExecution 
project-basic:default-compile", //
+                                                  "afterMojoExecutionSuccess 
project-basic:default-compile", //
+                                                  "beforeMojoExecution 
project-basic:default-testResources", //
+                                                  "afterMojoExecutionSuccess 
project-basic:default-testResources", //
+                                                  "beforeMojoExecution 
project-basic:default-testCompile", //
+                                                  "afterMojoExecutionSuccess 
project-basic:default-testCompile", //
+                                                  "beforeMojoExecution 
project-basic:default-test", //
+                                                  "afterMojoExecutionSuccess 
project-basic:default-test", //
+                                                  "beforeMojoExecution 
project-basic:default-jar", //
+                                                  "afterMojoExecutionSuccess 
project-basic:default-jar", //
+                                                  
"afterProjectExecutionSuccess project-basic" //
+        );
+
+        assertEventLog( expectedLog, log );
+    }
+
+    private static void assertEventLog( List<String> expectedList, 
List<String> actualList )
+    {
+        assertEquals( toString( expectedList ), toString( actualList ) );
+    }
+
+    private static String toString( List<String> lines )
+    {
+        StringBuilder sb = new StringBuilder();
+        for ( String line : lines )
+        {
+            sb.append( line ).append( '\n' );
+        }
+        return sb.toString();
+    }
 }

Reply via email to