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 org.apache.maven.execution.MojoExecutionEvent;
028import org.apache.maven.execution.MojoExecutionListener;
029import org.apache.maven.execution.scope.WeakMojoExecutionListener;
030import org.apache.maven.plugin.MojoExecutionException;
031
032import com.google.common.collect.Maps;
033import com.google.inject.Key;
034import com.google.inject.OutOfScopeException;
035import com.google.inject.Provider;
036import com.google.inject.Scope;
037import com.google.inject.util.Providers;
038
039public class MojoExecutionScope
040    implements Scope, MojoExecutionListener
041{
042    private static final Provider<Object> SEEDED_KEY_PROVIDER = new Provider<Object>()
043    {
044        public Object get()
045        {
046            throw new IllegalStateException();
047        }
048    };
049
050    private static final class ScopeState
051    {
052        public final Map<Key<?>, Provider<?>> seeded = Maps.newHashMap();
053
054        public final Map<Key<?>, Object> provided = Maps.newHashMap();
055    }
056
057    private final ThreadLocal<LinkedList<ScopeState>> values = new ThreadLocal<>();
058
059    public MojoExecutionScope()
060    {
061    }
062
063    public void enter()
064    {
065        LinkedList<ScopeState> stack = values.get();
066        if ( stack == null )
067        {
068            stack = new LinkedList<>();
069            values.set( stack );
070        }
071        stack.addFirst( new ScopeState() );
072    }
073
074    private ScopeState getScopeState()
075    {
076        LinkedList<ScopeState> stack = values.get();
077        if ( stack == null || stack.isEmpty() )
078        {
079            throw new IllegalStateException();
080        }
081        return stack.getFirst();
082    }
083
084    public void exit()
085        throws MojoExecutionException
086    {
087        final LinkedList<ScopeState> stack = values.get();
088        if ( stack == null || stack.isEmpty() )
089        {
090            throw new IllegalStateException();
091        }
092        stack.removeFirst();
093        if ( stack.isEmpty() )
094        {
095            values.remove();
096        }
097    }
098
099    public <T> void seed( Class<T> clazz, Provider<T> value )
100    {
101        getScopeState().seeded.put( Key.get( clazz ), value );
102    }
103
104    public <T> void seed( Class<T> clazz, final T value )
105    {
106        getScopeState().seeded.put( Key.get( clazz ), Providers.of( value ) );
107    }
108
109    public <T> Provider<T> scope( final Key<T> key, final Provider<T> unscoped )
110    {
111        return new Provider<T>()
112        {
113            @SuppressWarnings( "unchecked" )
114            public T get()
115            {
116                LinkedList<ScopeState> stack = values.get();
117                if ( stack == null || stack.isEmpty() )
118                {
119                    throw new OutOfScopeException( "Cannot access " + key + " outside of a scoping block" );
120                }
121
122                ScopeState state = stack.getFirst();
123
124                Provider<?> seeded = state.seeded.get( key );
125
126                if ( seeded != null )
127                {
128                    return (T) seeded.get();
129                }
130
131                T provided = (T) state.provided.get( key );
132                if ( provided == null && unscoped != null )
133                {
134                    provided = unscoped.get();
135                    state.provided.put( key, provided );
136                }
137
138                return provided;
139            }
140        };
141    }
142
143    @SuppressWarnings( { "unchecked" } )
144    public static <T> Provider<T> seededKeyProvider()
145    {
146        return (Provider<T>) SEEDED_KEY_PROVIDER;
147    }
148
149    public void beforeMojoExecution( MojoExecutionEvent event )
150        throws MojoExecutionException
151    {
152        for ( WeakMojoExecutionListener provided : getProvidedListeners() )
153        {
154            provided.beforeMojoExecution( event );
155        }
156    }
157
158    public void afterMojoExecutionSuccess( MojoExecutionEvent event )
159        throws MojoExecutionException
160    {
161        for ( WeakMojoExecutionListener provided : getProvidedListeners() )
162        {
163            provided.afterMojoExecutionSuccess( event );
164        }
165    }
166
167    public void afterExecutionFailure( MojoExecutionEvent event )
168    {
169        for ( WeakMojoExecutionListener provided : getProvidedListeners() )
170        {
171            provided.afterExecutionFailure( event );
172        }
173    }
174
175    private Collection<WeakMojoExecutionListener> getProvidedListeners()
176    {
177        // the same instance can be provided multiple times under different Key's
178        // deduplicate instances to avoid redundant beforeXXX/afterXXX callbacks
179        IdentityHashMap<WeakMojoExecutionListener, Object> listeners =
180            new IdentityHashMap<>();
181        for ( Object provided : getScopeState().provided.values() )
182        {
183            if ( provided instanceof WeakMojoExecutionListener )
184            {
185                listeners.put( (WeakMojoExecutionListener) provided, null );
186            }
187        }
188        return listeners.keySet();
189    }
190}