1919 * under the License.
2020 */
2121
22- import java .util .Collection ;
23- import java .util .List ;
24- import java .util .concurrent .ConcurrentHashMap ;
25- import java .util .concurrent .ConcurrentMap ;
26- import java .util .concurrent .CopyOnWriteArrayList ;
22+ import java .util .Collections ;
23+ import java .util .HashMap ;
24+ import java .util .LinkedList ;
25+ import java .util .Map ;
2726
2827import com .google .inject .Key ;
2928import com .google .inject .OutOfScopeException ;
3029import com .google .inject .Provider ;
3130import com .google .inject .Scope ;
31+ import com .google .inject .util .Providers ;
3232
3333/**
3434 * SessionScope
3535 */
3636public class SessionScope
3737 implements Scope
3838{
39+ /**
40+ * @since 3.3.0
41+ */
42+ public static class Memento
43+ {
44+ final Map <Key <?>, Provider <?>> seeded ;
45+
46+ Memento ( final Map <Key <?>, Provider <?>> seeded )
47+ {
48+ this .seeded = Collections .unmodifiableMap ( new HashMap <>( seeded ) );
49+ }
50+ }
3951
4052 private static final Provider <Object > SEEDED_KEY_PROVIDER = new Provider <Object >()
4153 {
@@ -48,127 +60,110 @@ public Object get()
4860 /**
4961 * ScopeState
5062 */
51- protected static final class ScopeState
63+ private static final class ScopeState
5264 {
53- private final ConcurrentMap <Key <?>, CachingProvider <?>> provided = new ConcurrentHashMap <>();
65+ private final Map <Key <?>, Provider <?>> seeded = new HashMap <>();
5466
55- public <T > void seed ( Class <T > clazz , Provider <T > value )
56- {
57- provided .put ( Key .get ( clazz ), new CachingProvider <>( value ) );
58- }
67+ private final Map <Key <?>, Object > provided = new HashMap <>();
68+ }
5969
60- @ SuppressWarnings ( "unchecked" )
61- public <T > Provider <T > scope ( Key <T > key , final Provider <T > unscoped )
62- {
63- Provider <?> provider = provided .get ( key );
64- if ( provider == null )
65- {
66- CachingProvider <?> newValue = new CachingProvider <>( unscoped );
67- provider = provided .putIfAbsent ( key , newValue );
68- if ( provider == null )
69- {
70- provider = newValue ;
71- }
72- }
73- return ( Provider <T > ) provider ;
74- }
70+ private final ThreadLocal <LinkedList <ScopeState >> values = new ThreadLocal <>();
7571
76- public Collection <CachingProvider <?>> providers ()
72+ public void enter ()
73+ {
74+ LinkedList <ScopeState > stack = values .get ();
75+ if ( stack == null )
7776 {
78- return provided .values ();
77+ stack = new LinkedList <>();
78+ values .set ( stack );
7979 }
80-
80+ stack . addFirst ( new ScopeState () );
8181 }
8282
83- private final List <ScopeState > values = new CopyOnWriteArrayList <>();
84-
85- public void enter ()
83+ /**
84+ * @since 3.3.0
85+ */
86+ public void enter ( Memento memento )
8687 {
87- values .add ( 0 , new ScopeState () );
88+ enter ();
89+ getScopeState ().seeded .putAll ( memento .seeded );
8890 }
8991
90- protected ScopeState getScopeState ()
92+ private ScopeState getScopeState ()
9193 {
92- if ( values .isEmpty () )
94+ LinkedList <ScopeState > stack = values .get ();
95+ if ( stack == null || stack .isEmpty () )
9396 {
94- throw new OutOfScopeException ( "Cannot access session scope outside of a scoping block" );
97+ throw new IllegalStateException ( );
9598 }
96- return values . get ( 0 );
99+ return stack . getFirst ( );
97100 }
98101
99102 public void exit ()
100103 {
101- if ( values .isEmpty () )
104+ final LinkedList <ScopeState > stack = values .get ();
105+ if ( stack == null || stack .isEmpty () )
102106 {
103107 throw new IllegalStateException ();
104108 }
105- values .remove ( 0 );
109+ stack .removeFirst ();
110+ if ( stack .isEmpty () )
111+ {
112+ values .remove ();
113+ }
114+ }
115+
116+ /**
117+ * @since 3.3.0
118+ */
119+ public Memento memento ()
120+ {
121+ LinkedList <ScopeState > stack = values .get ();
122+ return new Memento ( stack != null ? stack .getFirst ().seeded : Collections .<Key <?>, Provider <?>>emptyMap () );
106123 }
107124
108125 public <T > void seed ( Class <T > clazz , Provider <T > value )
109126 {
110- getScopeState ().seed ( clazz , value );
127+ getScopeState ().seeded . put ( Key . get ( clazz ) , value );
111128 }
112129
113130 public <T > void seed ( Class <T > clazz , final T value )
114131 {
115- seed ( clazz , new Provider <T >()
116- {
117- @ Override
118- public T get ()
119- {
120- return value ;
121- }
122- } );
132+ getScopeState ().seeded .put ( Key .get ( clazz ), Providers .of ( value ) );
123133 }
124134
125135 public <T > Provider <T > scope ( final Key <T > key , final Provider <T > unscoped )
126136 {
127- // Lazy evaluating provider
128137 return new Provider <T >()
129138 {
130- @ Override
139+ @ SuppressWarnings ( "unchecked" )
131140 public T get ()
132141 {
133- return getScopeState ().scope ( key , unscoped ).get ();
134- }
135- };
136- }
142+ LinkedList <ScopeState > stack = values .get ();
143+ if ( stack == null || stack .isEmpty () )
144+ {
145+ throw new OutOfScopeException ( "Cannot access " + key + " outside of a scoping block" );
146+ }
137147
138- /**
139- * CachingProvider
140- * @param <T>
141- */
142- protected static class CachingProvider <T > implements Provider <T >
143- {
144- private final Provider <T > provider ;
145- private volatile T value ;
148+ ScopeState state = stack .getFirst ();
146149
147- CachingProvider ( Provider <T > provider )
148- {
149- this .provider = provider ;
150- }
150+ Provider <?> seeded = state .seeded .get ( key );
151151
152- public T value ( )
153- {
154- return value ;
155- }
152+ if ( seeded != null )
153+ {
154+ return ( T ) seeded . get () ;
155+ }
156156
157- @ Override
158- public T get ()
159- {
160- if ( value == null )
161- {
162- synchronized ( this )
157+ T provided = (T ) state .provided .get ( key );
158+ if ( provided == null && unscoped != null )
163159 {
164- if ( value == null )
165- {
166- value = provider .get ();
167- }
160+ provided = unscoped .get ();
161+ state .provided .put ( key , provided );
168162 }
163+
164+ return provided ;
169165 }
170- return value ;
171- }
166+ };
172167 }
173168
174169 @ SuppressWarnings ( { "unchecked" } )
0 commit comments