View Javadoc
1   package org.apache.maven.session.scope.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.ConcurrentHashMap;
26  import java.util.concurrent.CopyOnWriteArrayList;
27  
28  import com.google.inject.Key;
29  import com.google.inject.OutOfScopeException;
30  import com.google.inject.Provider;
31  import com.google.inject.Scope;
32  
33  /**
34   * SessionScope
35   */
36  public class SessionScope
37      implements Scope
38  {
39  
40      private static final Provider<Object> SEEDED_KEY_PROVIDER = () ->
41      {
42          throw new IllegalStateException();
43      };
44  
45      /**
46       * ScopeState
47       */
48      protected static final class ScopeState
49      {
50          private final Map<Key<?>, CachingProvider<?>> provided = new ConcurrentHashMap<>();
51  
52          public <T> void seed( Class<T> clazz, Provider<T> value )
53          {
54              provided.put( Key.get( clazz ), new CachingProvider<>( value ) );
55          }
56  
57          @SuppressWarnings( "unchecked" )
58          public <T> Provider<T> scope( Key<T> key, Provider<T> unscoped )
59          {
60              Provider<?> provider = provided.computeIfAbsent( key, k -> new CachingProvider<>( unscoped ) );
61              return ( Provider<T> ) provider;
62          }
63  
64          public Collection<CachingProvider<?>> providers()
65          {
66              return provided.values();
67          }
68      }
69  
70      private final List<ScopeState> values = new CopyOnWriteArrayList<>();
71  
72      public void enter()
73      {
74          values.add( 0, new ScopeState() );
75      }
76  
77      protected ScopeState getScopeState()
78      {
79          if ( values.isEmpty() )
80          {
81              throw new OutOfScopeException( "Cannot access session scope outside of a scoping block" );
82          }
83          return values.get( 0 );
84      }
85  
86      public void exit()
87      {
88          if ( values.isEmpty() )
89          {
90              throw new IllegalStateException();
91          }
92          values.remove( 0 );
93      }
94  
95      public <T> void seed( Class<T> clazz, Provider<T> value )
96      {
97          getScopeState().seed( clazz, value );
98      }
99  
100     public <T> void seed( Class<T> clazz, final T value )
101     {
102         seed( clazz, ( Provider<T> ) () -> value );
103     }
104 
105     public <T> Provider<T> scope( final Key<T> key, final Provider<T> unscoped )
106     {
107         // Lazy evaluating provider
108         return () -> getScopeState().scope( key, unscoped ).get();
109     }
110 
111     /**
112      * A provider wrapping an existing provider with a cache
113      * @param <T> the provided type
114      */
115     protected static class CachingProvider<T> implements Provider<T>
116     {
117         private final Provider<T> provider;
118         private volatile T value;
119 
120         CachingProvider( Provider<T> provider )
121         {
122             this.provider = provider;
123         }
124 
125         public T value()
126         {
127             return value;
128         }
129 
130         @Override
131         public T get()
132         {
133             if ( value == null )
134             {
135                 synchronized ( this )
136                 {
137                     if ( value == null )
138                     {
139                         value = provider.get();
140                     }
141                 }
142             }
143             return value;
144         }
145     }
146 
147     @SuppressWarnings( { "unchecked" } )
148     public static <T> Provider<T> seededKeyProvider()
149     {
150         return (Provider<T>) SEEDED_KEY_PROVIDER;
151     }
152 }