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 1cbaf533460..548fe6c8ffd 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 @@ -90,12 +90,8 @@ public void buildProject( MavenSession session, MavenSession rootSession, Reacto // session may be different from rootSession seeded in DefaultMaven // explicitly seed the right session here to make sure it is used by Guice - final boolean scoped = session != rootSession; - if ( scoped ) - { - sessionScope.enter(); - sessionScope.seed( MavenSession.class, session ); - } + sessionScope.enter( reactorContext.getSessionScopeMemento() ); + sessionScope.seed( MavenSession.class, session ); try { @@ -149,10 +145,7 @@ public void buildProject( MavenSession session, MavenSession rootSession, Reacto } finally { - if ( scoped ) - { - sessionScope.exit(); - } + sessionScope.exit(); session.setCurrentProject( null ); diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleStarter.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleStarter.java index 83449812608..cee80739234 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleStarter.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleStarter.java @@ -107,7 +107,8 @@ public void execute( MavenSession session ) ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus( session.getProjectDependencyGraph() ); reactorContext = - new ReactorContext( result, projectIndex, oldContextClassLoader, reactorBuildStatus ); + new ReactorContext( result, projectIndex, oldContextClassLoader, reactorBuildStatus, + sessionScope.memento() ); String builderId = session.getRequest().getBuilderId(); Builder builder = builders.get( builderId ); diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorContext.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorContext.java index 076c6229f8e..7df53140452 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorContext.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorContext.java @@ -20,6 +20,7 @@ */ import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.session.scope.internal.SessionScope; /** * Context that is fixed for the entire reactor build. @@ -39,13 +40,17 @@ public class ReactorContext private final ReactorBuildStatus reactorBuildStatus; + private final SessionScope.Memento sessionScope; + public ReactorContext( MavenExecutionResult result, ProjectIndex projectIndex, - ClassLoader originalContextClassLoader, ReactorBuildStatus reactorBuildStatus ) + ClassLoader originalContextClassLoader, ReactorBuildStatus reactorBuildStatus, + SessionScope.Memento sessionScope ) { this.result = result; this.projectIndex = projectIndex; this.originalContextClassLoader = originalContextClassLoader; this.reactorBuildStatus = reactorBuildStatus; + this.sessionScope = sessionScope; } public ReactorBuildStatus getReactorBuildStatus() @@ -68,4 +73,11 @@ public ClassLoader getOriginalContextClassLoader() return originalContextClassLoader; } + /** + * @since 3.3.0 + */ + public SessionScope.Memento getSessionScopeMemento() + { + return sessionScope; + } } diff --git a/maven-core/src/main/java/org/apache/maven/session/scope/internal/SessionScope.java b/maven-core/src/main/java/org/apache/maven/session/scope/internal/SessionScope.java index 41187fd80cb..ac423bc6c92 100644 --- a/maven-core/src/main/java/org/apache/maven/session/scope/internal/SessionScope.java +++ b/maven-core/src/main/java/org/apache/maven/session/scope/internal/SessionScope.java @@ -19,16 +19,16 @@ * under the License. */ -import java.util.Collection; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; import com.google.inject.Key; import com.google.inject.OutOfScopeException; import com.google.inject.Provider; import com.google.inject.Scope; +import com.google.inject.util.Providers; /** * SessionScope @@ -36,6 +36,18 @@ public class SessionScope implements Scope { + /** + * @since 3.3.0 + */ + public static class Memento + { + final Map, Provider> seeded; + + Memento( final Map, Provider> seeded ) + { + this.seeded = Collections.unmodifiableMap( new HashMap<>( seeded ) ); + } + } private static final Provider SEEDED_KEY_PROVIDER = new Provider() { @@ -48,127 +60,110 @@ public Object get() /** * ScopeState */ - protected static final class ScopeState + private static final class ScopeState { - private final ConcurrentMap, CachingProvider> provided = new ConcurrentHashMap<>(); + private final Map, Provider> seeded = new HashMap<>(); - public void seed( Class clazz, Provider value ) - { - provided.put( Key.get( clazz ), new CachingProvider<>( value ) ); - } + private final Map, Object> provided = new HashMap<>(); + } - @SuppressWarnings( "unchecked" ) - public Provider scope( Key key, final Provider unscoped ) - { - Provider provider = provided.get( key ); - if ( provider == null ) - { - CachingProvider newValue = new CachingProvider<>( unscoped ); - provider = provided.putIfAbsent( key, newValue ); - if ( provider == null ) - { - provider = newValue; - } - } - return ( Provider ) provider; - } + private final ThreadLocal> values = new ThreadLocal<>(); - public Collection> providers() + public void enter() + { + LinkedList stack = values.get(); + if ( stack == null ) { - return provided.values(); + stack = new LinkedList<>(); + values.set( stack ); } - + stack.addFirst( new ScopeState() ); } - private final List values = new CopyOnWriteArrayList<>(); - - public void enter() + /** + * @since 3.3.0 + */ + public void enter( Memento memento ) { - values.add( 0, new ScopeState() ); + enter(); + getScopeState().seeded.putAll( memento.seeded ); } - protected ScopeState getScopeState() + private ScopeState getScopeState() { - if ( values.isEmpty() ) + LinkedList stack = values.get(); + if ( stack == null || stack.isEmpty() ) { - throw new OutOfScopeException( "Cannot access session scope outside of a scoping block" ); + throw new IllegalStateException(); } - return values.get( 0 ); + return stack.getFirst(); } public void exit() { - if ( values.isEmpty() ) + final LinkedList stack = values.get(); + if ( stack == null || stack.isEmpty() ) { throw new IllegalStateException(); } - values.remove( 0 ); + stack.removeFirst(); + if ( stack.isEmpty() ) + { + values.remove(); + } + } + + /** + * @since 3.3.0 + */ + public Memento memento() + { + LinkedList stack = values.get(); + return new Memento( stack != null ? stack.getFirst().seeded : Collections., Provider>emptyMap() ); } public void seed( Class clazz, Provider value ) { - getScopeState().seed( clazz, value ); + getScopeState().seeded.put( Key.get( clazz ), value ); } public void seed( Class clazz, final T value ) { - seed( clazz, new Provider() - { - @Override - public T get() - { - return value; - } - } ); + getScopeState().seeded.put( Key.get( clazz ), Providers.of( value ) ); } public Provider scope( final Key key, final Provider unscoped ) { - // Lazy evaluating provider return new Provider() { - @Override + @SuppressWarnings( "unchecked" ) public T get() { - return getScopeState().scope( key, unscoped ).get(); - } - }; - } + LinkedList stack = values.get(); + if ( stack == null || stack.isEmpty() ) + { + throw new OutOfScopeException( "Cannot access " + key + " outside of a scoping block" ); + } - /** - * CachingProvider - * @param - */ - protected static class CachingProvider implements Provider - { - private final Provider provider; - private volatile T value; + ScopeState state = stack.getFirst(); - CachingProvider( Provider provider ) - { - this.provider = provider; - } + Provider seeded = state.seeded.get( key ); - public T value() - { - return value; - } + if ( seeded != null ) + { + return (T) seeded.get(); + } - @Override - public T get() - { - if ( value == null ) - { - synchronized ( this ) + T provided = (T) state.provided.get( key ); + if ( provided == null && unscoped != null ) { - if ( value == null ) - { - value = provider.get(); - } + provided = unscoped.get(); + state.provided.put( key, provided ); } + + return provided; } - return value; - } + }; } @SuppressWarnings( { "unchecked" } ) diff --git a/maven-core/src/test/java/org/apache/maven/session/scope/internal/SessionScopeTest.java b/maven-core/src/test/java/org/apache/maven/session/scope/internal/SessionScopeTest.java deleted file mode 100644 index 099e4dddb8f..00000000000 --- a/maven-core/src/test/java/org/apache/maven/session/scope/internal/SessionScopeTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.apache.maven.session.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.Provider; - -import com.google.inject.Key; -import com.google.inject.OutOfScopeException; -import org.apache.maven.model.locator.DefaultModelLocator; -import org.apache.maven.model.locator.ModelLocator; -import org.apache.maven.plugin.DefaultPluginRealmCache; -import org.apache.maven.plugin.PluginRealmCache; -import org.junit.Test; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.fail; - -public class SessionScopeTest { - - @Test - public void testScope() throws Exception - { - SessionScope scope = new SessionScope(); - - try - { - scope.seed( ModelLocator.class, new DefaultModelLocator() ); - fail( "Expected a " + OutOfScopeException.class.getName() + " exception to be thrown" ); - } - catch ( OutOfScopeException e ) - { - // expected - } - - Provider pml = scope.scope( Key.get( ModelLocator.class), new DefaultModelLocatorProvider() ); - assertNotNull( pml ); - try - { - pml.get(); - fail( "Expected a " + OutOfScopeException.class.getName() + " exception to be thrown" ); - } - catch ( OutOfScopeException e ) - { - // expected - } - - Provider pmst = scope.scope( Key.get( PluginRealmCache.class ), new DefaultPluginRealmCacheProvider() ); - assertNotNull( pmst ); - - scope.enter(); - - final DefaultModelLocator dml1 = new DefaultModelLocator(); - scope.seed( ModelLocator.class, dml1 ); - - assertSame( dml1, pml.get() ); - - PluginRealmCache mst1 = pmst.get(); - assertSame( mst1, pmst.get() ); - Provider pmst1 = scope.scope( Key.get( PluginRealmCache.class ), new DefaultPluginRealmCacheProvider() ); - assertNotNull( pmst1 ); - assertSame( mst1, pmst1.get() ); - - scope.enter(); - - pmst1 = scope.scope( Key.get( PluginRealmCache.class ), new DefaultPluginRealmCacheProvider() ); - assertNotNull( pmst1 ); - assertNotSame( mst1, pmst1.get() ); - - scope.exit(); - - assertSame( mst1, pmst.get() ); - - scope.exit(); - - try - { - pmst.get(); - fail( "Expected a " + OutOfScopeException.class.getName() + " exception to be thrown" ); - } - catch ( OutOfScopeException e ) - { - // expected - } - try - { - scope.seed( ModelLocator.class, new DefaultModelLocator() ); - fail( "Expected a " + OutOfScopeException.class.getName() + " exception to be thrown" ); - } - catch ( OutOfScopeException e ) - { - // expected - } - } - - private static class DefaultPluginRealmCacheProvider implements com.google.inject.Provider - { - @Override - public PluginRealmCache get() - { - return new DefaultPluginRealmCache(); - } - } - - private static class DefaultModelLocatorProvider implements com.google.inject.Provider - { - @Override - public ModelLocator get() - { - return new DefaultModelLocator(); - } - } - -}