001package org.apache.maven.execution.scope.internal;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.Collection;
023import java.util.IdentityHashMap;
024import java.util.LinkedList;
025import java.util.Map;
026
027import javax.inject.Named;
028import javax.inject.Singleton;
029
030import org.apache.maven.execution.MojoExecutionEvent;
031import org.apache.maven.execution.MojoExecutionListener;
032import org.apache.maven.execution.scope.MojoExecutionScoped;
033import org.apache.maven.execution.scope.WeakMojoExecutionListener;
034import org.apache.maven.plugin.MojoExecution;
035import org.apache.maven.plugin.MojoExecutionException;
036import org.apache.maven.project.MavenProject;
037import org.codehaus.plexus.PlexusContainer;
038import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
039
040import com.google.common.collect.Maps;
041import com.google.inject.AbstractModule;
042import com.google.inject.Key;
043import com.google.inject.Module;
044import com.google.inject.OutOfScopeException;
045import com.google.inject.Provider;
046import com.google.inject.Scope;
047import com.google.inject.util.Providers;
048
049@Named
050@Singleton
051public class MojoExecutionScope
052    implements Scope, MojoExecutionListener
053{
054    private static final Provider<Object> SEEDED_KEY_PROVIDER = new Provider<Object>()
055    {
056        public Object get()
057        {
058            throw new IllegalStateException();
059        }
060    };
061
062    private static final class ScopeState
063    {
064        public final Map<Key<?>, Provider<?>> seeded = Maps.newHashMap();
065
066        public final Map<Key<?>, Object> provided = Maps.newHashMap();
067    }
068
069    private final ThreadLocal<LinkedList<ScopeState>> values = new ThreadLocal<LinkedList<ScopeState>>();
070
071    public MojoExecutionScope()
072    {
073    }
074
075    public void enter()
076    {
077        LinkedList<ScopeState> stack = values.get();
078        if ( stack == null )
079        {
080            stack = new LinkedList<ScopeState>();
081            values.set( stack );
082        }
083        stack.addFirst( new ScopeState() );
084    }
085
086    private ScopeState getScopeState()
087    {
088        LinkedList<ScopeState> stack = values.get();
089        if ( stack == null || stack.isEmpty() )
090        {
091            throw new IllegalStateException();
092        }
093        return stack.getFirst();
094    }
095
096    public void exit()
097        throws MojoExecutionException
098    {
099        final LinkedList<ScopeState> stack = values.get();
100        if ( stack == null || stack.isEmpty() )
101        {
102            throw new IllegalStateException();
103        }
104        stack.removeFirst();
105        if ( stack.isEmpty() )
106        {
107            values.remove();
108        }
109    }
110
111    public <T> void seed( Class<T> clazz, Provider<T> value )
112    {
113        getScopeState().seeded.put( Key.get( clazz ), value );
114    }
115
116    public <T> void seed( Class<T> clazz, final T value )
117    {
118        getScopeState().seeded.put( Key.get( clazz ), Providers.of( value ) );
119    }
120
121    public <T> Provider<T> scope( final Key<T> key, final Provider<T> unscoped )
122    {
123        return new Provider<T>()
124        {
125            @SuppressWarnings( "unchecked" )
126            public T get()
127            {
128                LinkedList<ScopeState> stack = values.get();
129                if ( stack == null || stack.isEmpty() )
130                {
131                    throw new OutOfScopeException( "Cannot access " + key + " outside of a scoping block" );
132                }
133
134                ScopeState state = stack.getFirst();
135
136                Provider<?> seeded = state.seeded.get( key );
137
138                if ( seeded != null )
139                {
140                    return (T) seeded.get();
141                }
142
143                T provided = (T) state.provided.get( key );
144                if ( provided == null && unscoped != null )
145                {
146                    provided = unscoped.get();
147                    state.provided.put( key, provided );
148                }
149
150                return provided;
151            }
152        };
153    }
154
155    @SuppressWarnings( { "unchecked" } )
156    public static <T> Provider<T> seededKeyProvider()
157    {
158        return (Provider<T>) SEEDED_KEY_PROVIDER;
159    }
160
161    public static Module getScopeModule( PlexusContainer container )
162        throws ComponentLookupException
163    {
164        final MojoExecutionScope scope = container.lookup( MojoExecutionScope.class );
165        return new AbstractModule()
166        {
167            @Override
168            protected void configure()
169            {
170                bindScope( MojoExecutionScoped.class, scope );
171
172                // standard scope bindings
173                bind( MavenProject.class ).toProvider( MojoExecutionScope.<MavenProject> seededKeyProvider() ).in( scope );
174                bind( MojoExecution.class ).toProvider( MojoExecutionScope.<MojoExecution> seededKeyProvider() ).in( scope );
175            }
176        };
177    }
178
179    public void beforeMojoExecution( MojoExecutionEvent event )
180        throws MojoExecutionException
181    {
182        for ( WeakMojoExecutionListener provided : getProvidedListeners() )
183        {
184            provided.beforeMojoExecution( event );
185        }
186    }
187
188    public void afterMojoExecutionSuccess( MojoExecutionEvent event )
189        throws MojoExecutionException
190    {
191        for ( WeakMojoExecutionListener provided : getProvidedListeners() )
192        {
193            provided.afterMojoExecutionSuccess( event );
194        }
195    }
196
197    public void afterExecutionFailure( MojoExecutionEvent event )
198    {
199        for ( WeakMojoExecutionListener provided : getProvidedListeners() )
200        {
201            provided.afterExecutionFailure( event );
202        }
203    }
204
205    private Collection<WeakMojoExecutionListener> getProvidedListeners()
206    {
207        // the same instance can be provided multiple times under different Key's
208        // deduplicate instances to avoid redundant beforeXXX/afterXXX callbacks
209        IdentityHashMap<WeakMojoExecutionListener, Object> listeners =
210            new IdentityHashMap<WeakMojoExecutionListener, Object>();
211        for ( Object provided : getScopeState().provided.values() )
212        {
213            if ( provided instanceof WeakMojoExecutionListener )
214            {
215                listeners.put( (WeakMojoExecutionListener) provided, null );
216            }
217        }
218        return listeners.keySet();
219    }
220}