View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.execution.scope.internal;
20  
21  import java.lang.annotation.Annotation;
22  import java.util.Collection;
23  import java.util.HashMap;
24  import java.util.IdentityHashMap;
25  import java.util.LinkedList;
26  import java.util.Map;
27  import java.util.function.Supplier;
28  
29  import com.google.inject.Key;
30  import com.google.inject.OutOfScopeException;
31  import com.google.inject.Provider;
32  import com.google.inject.Scope;
33  import com.google.inject.name.Names;
34  import com.google.inject.util.Providers;
35  import org.apache.maven.execution.MojoExecutionEvent;
36  import org.apache.maven.execution.MojoExecutionListener;
37  import org.apache.maven.execution.scope.WeakMojoExecutionListener;
38  import org.apache.maven.plugin.MojoExecutionException;
39  
40  /**
41   * MojoExecutionScope
42   */
43  public class MojoExecutionScope implements Scope, org.apache.maven.di.Scope, MojoExecutionListener {
44      private static final Provider<Object> SEEDED_KEY_PROVIDER = () -> {
45          throw new IllegalStateException();
46      };
47  
48      private static final class ScopeState {
49          private final Map<Key<?>, Provider<?>> seeded = new HashMap<>();
50  
51          private final Map<Key<?>, Object> provided = new HashMap<>();
52      }
53  
54      private final ThreadLocal<LinkedList<ScopeState>> values = new ThreadLocal<>();
55  
56      public MojoExecutionScope() {}
57  
58      public void enter() {
59          LinkedList<ScopeState> stack = values.get();
60          if (stack == null) {
61              stack = new LinkedList<>();
62              values.set(stack);
63          }
64          stack.addFirst(new ScopeState());
65      }
66  
67      private ScopeState getScopeState() {
68          LinkedList<ScopeState> stack = values.get();
69          if (stack == null || stack.isEmpty()) {
70              throw new IllegalStateException();
71          }
72          return stack.getFirst();
73      }
74  
75      public void exit() throws MojoExecutionException {
76          final LinkedList<ScopeState> stack = values.get();
77          if (stack == null || stack.isEmpty()) {
78              throw new IllegalStateException();
79          }
80          stack.removeFirst();
81          if (stack.isEmpty()) {
82              values.remove();
83          }
84      }
85  
86      public <T> void seed(Class<T> clazz, Provider<T> value) {
87          getScopeState().seeded.put(Key.get(clazz), value);
88      }
89  
90      public <T> void seed(Class<T> clazz, final T value) {
91          getScopeState().seeded.put(Key.get(clazz), Providers.of(value));
92      }
93  
94      public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
95          return () -> {
96              LinkedList<ScopeState> stack = values.get();
97              if (stack == null || stack.isEmpty()) {
98                  throw new OutOfScopeException("Cannot access " + key + " outside of a scoping block");
99              }
100 
101             ScopeState state = stack.getFirst();
102 
103             Provider<?> seeded = state.seeded.get(key);
104 
105             if (seeded != null) {
106                 return (T) seeded.get();
107             }
108 
109             T provided = (T) state.provided.get(key);
110             if (provided == null && unscoped != null) {
111                 provided = unscoped.get();
112                 state.provided.put(key, provided);
113             }
114 
115             return provided;
116         };
117     }
118 
119     @Override
120     public <T> Supplier<T> scope(org.apache.maven.di.Key<T> key, Annotation scope, Supplier<T> unscoped) {
121         Object qualifier = key.getQualifier();
122         Key k = qualifier != null
123                 ? Key.get(key.getType(), qualifier instanceof String s ? Names.named(s) : (Annotation) qualifier)
124                 : Key.get(key.getType());
125         Provider<T> up = unscoped::get;
126         Provider<T> p = scope(k, up);
127         return p::get;
128     }
129 
130     @SuppressWarnings({"unchecked"})
131     public static <T> Provider<T> seededKeyProvider() {
132         return (Provider<T>) SEEDED_KEY_PROVIDER;
133     }
134 
135     public void beforeMojoExecution(MojoExecutionEvent event) throws MojoExecutionException {
136         for (WeakMojoExecutionListener provided : getProvidedListeners()) {
137             provided.beforeMojoExecution(event);
138         }
139     }
140 
141     public void afterMojoExecutionSuccess(MojoExecutionEvent event) throws MojoExecutionException {
142         for (WeakMojoExecutionListener provided : getProvidedListeners()) {
143             provided.afterMojoExecutionSuccess(event);
144         }
145     }
146 
147     public void afterExecutionFailure(MojoExecutionEvent event) {
148         for (WeakMojoExecutionListener provided : getProvidedListeners()) {
149             provided.afterExecutionFailure(event);
150         }
151     }
152 
153     private Collection<WeakMojoExecutionListener> getProvidedListeners() {
154         // the same instance can be provided multiple times under different Key's
155         // deduplicate instances to avoid redundant beforeXXX/afterXXX callbacks
156         IdentityHashMap<WeakMojoExecutionListener, Object> listeners = new IdentityHashMap<>();
157         for (Object provided : getScopeState().provided.values()) {
158             if (provided instanceof WeakMojoExecutionListener) {
159                 listeners.put((WeakMojoExecutionListener) provided, null);
160             }
161         }
162         return listeners.keySet();
163     }
164 }