diff --git a/maven-core/pom.xml b/maven-core/pom.xml index ce86e3b936..66e39ba424 100644 --- a/maven-core/pom.xml +++ b/maven-core/pom.xml @@ -161,6 +161,19 @@ org.codehaus.plexus plexus-component-metadata + + org.sonatype.plugins + sisu-maven-plugin + 1.1 + + + + main-index + test-index + + + + org.codehaus.modello modello-maven-plugin diff --git a/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java b/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java index 41c7dc2906..a30a47ecf0 100644 --- a/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java +++ b/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java @@ -139,6 +139,7 @@ private void importMavenApi( Map imports ) imports.put( "org.apache.maven.configuration", coreRealm ); imports.put( "org.apache.maven.exception", coreRealm ); imports.put( "org.apache.maven.execution", coreRealm ); + imports.put( "org.apache.maven.execution.scope", coreRealm ); imports.put( "org.apache.maven.lifecycle", coreRealm ); imports.put( "org.apache.maven.model", coreRealm ); imports.put( "org.apache.maven.monitor", coreRealm ); 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 new file mode 100644 index 0000000000..7a9c84b4a7 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/execution/scope/MojoExecutionListener.java @@ -0,0 +1,48 @@ +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. + *

+ * 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; +} diff --git a/maven-core/src/main/java/org/apache/maven/execution/scope/MojoExecutionScoped.java b/maven-core/src/main/java/org/apache/maven/execution/scope/MojoExecutionScoped.java new file mode 100644 index 0000000000..c53297cb88 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/execution/scope/MojoExecutionScoped.java @@ -0,0 +1,42 @@ +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 static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import com.google.inject.ScopeAnnotation; + +/** + * Indicates that annotated component should be instantiated before mojo execution starts and discarded after mojo + * execution completes. + * + * @author igor + * @since 3.1.2 + */ +@Target( { TYPE } ) +@Retention( RUNTIME ) +@ScopeAnnotation +public @interface MojoExecutionScoped +{ +} diff --git a/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionModule.java b/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionModule.java new file mode 100644 index 0000000000..65ba1817e1 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionModule.java @@ -0,0 +1,45 @@ +package org.apache.maven.execution.scope.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 javax.inject.Named; + +import org.apache.maven.execution.scope.MojoExecutionScoped; + +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.name.Names; + +@Named +public class MojoExecutionModule + implements Module +{ + + public void configure( Binder binder ) + { + final MojoExecutionScope executionScope = new MojoExecutionScope(); + + // tell Guice about the scope + binder.bindScope( MojoExecutionScoped.class, executionScope ); + + // make our scope instance injectable + binder.bind( MojoExecutionScope.class ).annotatedWith( Names.named( MojoExecutionScope.SCOPE_NAME ) ).toInstance( executionScope ); + } +} 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 new file mode 100644 index 0000000000..3d021fdd80 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScope.java @@ -0,0 +1,200 @@ +package org.apache.maven.execution.scope.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.LinkedList; +import java.util.Map; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.scope.MojoExecutionListener; +import org.apache.maven.execution.scope.MojoExecutionScoped; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +import com.google.common.collect.Maps; +import com.google.inject.AbstractModule; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.OutOfScopeException; +import com.google.inject.Provider; +import com.google.inject.Scope; +import com.google.inject.util.Providers; + +public class MojoExecutionScope + implements Scope +{ + public static final String SCOPE_NAME = "mojoExecution"; + + private static final Provider SEEDED_KEY_PROVIDER = new Provider() + { + public Object get() + { + throw new IllegalStateException(); + } + }; + + private static final class ScopeState + { + public final Map, Provider> seeded = Maps.newHashMap(); + + public final Map, Object> provided = Maps.newHashMap(); + } + + private final ThreadLocal> values = new ThreadLocal>(); + + public MojoExecutionScope() + { + } + + public void enter() + { + LinkedList stack = values.get(); + if ( stack == null ) + { + stack = new LinkedList(); + values.set( stack ); + } + stack.addFirst( new ScopeState() ); + } + + private ScopeState getScopeState() + { + LinkedList stack = values.get(); + if ( stack == null || stack.isEmpty() ) + { + throw new IllegalStateException(); + } + return stack.getFirst(); + } + + public void exit() + throws MojoExecutionException + { + final LinkedList stack = values.get(); + if ( stack == null || stack.isEmpty() ) + { + throw new IllegalStateException(); + } + stack.removeFirst(); + if ( stack.isEmpty() ) + { + values.remove(); + } + } + + public void seed( Class clazz, Provider value ) + { + getScopeState().seeded.put( Key.get( clazz ), value ); + } + + public void seed( Class clazz, final T value ) + { + getScopeState().seeded.put( Key.get( clazz ), Providers.of( value ) ); + } + + public Provider scope( final Key key, final Provider unscoped ) + { + return new Provider() + { + @SuppressWarnings( "unchecked" ) + public T get() + { + LinkedList stack = values.get(); + if ( stack == null || stack.isEmpty() ) + { + throw new OutOfScopeException( "Cannot access " + key + " outside of a scoping block" ); + } + + ScopeState state = stack.getFirst(); + + Provider seeded = state.seeded.get( key ); + + if ( seeded != null ) + { + return (T) seeded.get(); + } + + T provided = (T) state.provided.get( key ); + if ( provided == null ) + { + provided = unscoped.get(); + state.provided.put( key, provided ); + } + + return provided; + } + }; + } + + @SuppressWarnings( { "unchecked" } ) + public static Provider seededKeyProvider() + { + return (Provider) SEEDED_KEY_PROVIDER; + } + + public static Module getScopeModule( PlexusContainer container ) + throws ComponentLookupException + { + final MojoExecutionScope scope = container.lookup( MojoExecutionScope.class ); + return new AbstractModule() + { + @Override + protected void configure() + { + bindScope( MojoExecutionScoped.class, scope ); + + // standard scope bindings + bind( MavenSession.class ).toProvider( MojoExecutionScope. seededKeyProvider() ).in( scope ); + bind( MavenProject.class ).toProvider( MojoExecutionScope. seededKeyProvider() ).in( scope ); + bind( MojoExecution.class ).toProvider( MojoExecutionScope. seededKeyProvider() ).in( scope ); + } + }; + } + + public void afterExecutionSuccess() + throws MojoExecutionException + { + for ( Object provided : getScopeState().provided.values() ) + { + if ( provided instanceof MojoExecutionListener ) + { + ( (MojoExecutionListener) provided ).afterMojoExecutionSuccess(); + // TODO maybe deal with multiple MojoExecutionExceptions + } + } + } + + public void afterExecutionAlways() + throws MojoExecutionException + { + for ( Object provided : getScopeState().provided.values() ) + { + if ( provided instanceof MojoExecutionListener ) + { + ( (MojoExecutionListener) provided ).afterMojoExecutionAlways(); + // TODO maybe deal with multiple MojoExecutionExceptions + } + } + } + +} 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 178f4c857b..6fe5daa85d 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.util.List; import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.scope.internal.MojoExecutionScope; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; @@ -48,8 +49,10 @@ public class DefaultBuildPluginManager @Requirement private LegacySupport legacySupport; + @Requirement( hint = MojoExecutionScope.SCOPE_NAME ) + private MojoExecutionScope scope; + /** - * * @param plugin * @param repositories * @param session @@ -92,8 +95,14 @@ public void executeMojo( MavenSession session, MojoExecution mojoExecution ) MavenSession oldSession = legacySupport.getSession(); + scope.enter(); + try { + scope.seed( MavenSession.class, session ); + scope.seed( MavenProject.class, project ); + scope.seed( MojoExecution.class, mojoExecution ); + mojo = mavenPluginManager.getConfiguredMojo( Mojo.class, session, mojoExecution ); legacySupport.setSession( session ); @@ -104,6 +113,8 @@ public void executeMojo( MavenSession session, MojoExecution mojoExecution ) try { mojo.execute(); + + scope.afterExecutionSuccess(); } catch ( ClassCastException e ) { @@ -155,8 +166,12 @@ public void executeMojo( MavenSession session, MojoExecution mojoExecution ) } finally { + scope.afterExecutionAlways(); + mavenPluginManager.releaseMojo( mojo, mojoExecution ); + scope.exit(); + Thread.currentThread().setContextClassLoader( oldClassLoader ); legacySupport.setSession( oldSession ); diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java index 8f22abe20a..8c8f507463 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java @@ -41,6 +41,7 @@ import org.apache.maven.artifact.Artifact; import org.apache.maven.classrealm.ClassRealmManager; import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.scope.internal.MojoExecutionScope; import org.apache.maven.model.Plugin; import org.apache.maven.monitor.logging.DefaultLog; import org.apache.maven.plugin.ContextEnabled; @@ -66,6 +67,7 @@ import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; import org.apache.maven.project.MavenProject; import org.apache.maven.rtinfo.RuntimeInformation; +import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.annotations.Component; @@ -389,9 +391,10 @@ private void createPluginRealm( PluginDescriptor pluginDescriptor, MavenSession container.addComponentDescriptor( componentDescriptor ); } - container.discoverComponents( pluginRealm ); + ( (DefaultPlexusContainer) container ).discoverComponents( pluginRealm, + MojoExecutionScope.getScopeModule( container ) ); } - catch ( PlexusConfigurationException e ) + catch ( ComponentLookupException e ) { throw new PluginContainerException( plugin, pluginRealm, "Error in component graph of plugin " + plugin.getId() + ": " + e.getMessage(), e ); diff --git a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java index 1d99343f64..cba7432e3f 100644 --- a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java +++ b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java @@ -33,6 +33,7 @@ import org.apache.maven.artifact.InvalidRepositoryException; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.classrealm.ClassRealmManager; +import org.apache.maven.execution.scope.internal.MojoExecutionScope; import org.apache.maven.model.Build; import org.apache.maven.model.Extension; import org.apache.maven.model.Model; @@ -47,6 +48,7 @@ import org.apache.maven.plugin.version.PluginVersionResolutionException; import org.apache.maven.plugin.version.PluginVersionResolver; import org.apache.maven.repository.RepositorySystem; +import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.annotations.Component; @@ -266,7 +268,8 @@ public synchronized ProjectRealmCache.CacheRecord createProjectRealm( MavenProje try { - container.discoverComponents( extensionRealm ); + ( (DefaultPlexusContainer) container ).discoverComponents( extensionRealm, + MojoExecutionScope.getScopeModule( container ) ); } catch ( Exception e ) { diff --git a/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java b/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java index 6534fc13a4..283a83cfa7 100644 --- a/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java +++ b/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java @@ -44,6 +44,7 @@ import org.apache.maven.repository.RepositorySystem; import org.apache.maven.repository.internal.MavenRepositorySystemUtils; import org.codehaus.plexus.ContainerConfiguration; +import org.codehaus.plexus.PlexusConstants; import org.codehaus.plexus.PlexusTestCase; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.util.FileUtils; @@ -98,7 +99,7 @@ protected File getProject( String name ) */ protected void customizeContainerConfiguration( ContainerConfiguration containerConfiguration ) { - containerConfiguration.setAutoWiring( true ); + containerConfiguration.setAutoWiring( true ).setClassPathScanning( PlexusConstants.SCANNING_INDEX );; } protected MavenExecutionRequest createMavenExecutionRequest( File pom ) diff --git a/maven-core/src/test/java/org/apache/maven/execution/scope/internal/MojoExecutionScopeTest.java b/maven-core/src/test/java/org/apache/maven/execution/scope/internal/MojoExecutionScopeTest.java new file mode 100644 index 0000000000..6e6f9feee0 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/execution/scope/internal/MojoExecutionScopeTest.java @@ -0,0 +1,53 @@ +/* + * 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. + */ +package org.apache.maven.execution.scope.internal; + +import junit.framework.TestCase; + +import com.google.inject.Key; + +public class MojoExecutionScopeTest + extends TestCase +{ + public void testNestedEnter() + throws Exception + { + MojoExecutionScope scope = new MojoExecutionScope(); + + scope.enter(); + + Object o1 = new Object(); + scope.seed( Object.class, o1 ); + assertSame( o1, scope.scope( Key.get( Object.class ), null ).get() ); + + scope.enter(); + Object o2 = new Object(); + scope.seed( Object.class, o2 ); + assertSame( o2, scope.scope( Key.get( Object.class ), null ).get() ); + + scope.exit(); + assertSame( o1, scope.scope( Key.get( Object.class ), null ).get() ); + + scope.exit(); + + try + { + scope.exit(); + } + catch ( IllegalStateException expected ) + { + } + } +}