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}