View Javadoc

1   /*
2    * Copyright 2007 Tim Peierls
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.directwebremoting.guice;
17  
18  import com.google.inject.Key;
19  import com.google.inject.Provider;
20  import com.google.inject.Scope;
21  import com.google.inject.util.ToStringBuilder;
22  
23  import java.util.Collection;
24  import java.util.ArrayList;
25  import java.util.List;
26  import static java.util.Collections.synchronizedList;
27  import static java.util.Collections.unmodifiableList;
28  
29  import java.util.concurrent.Callable;
30  import java.util.concurrent.ConcurrentHashMap;
31  import java.util.concurrent.ConcurrentMap;
32  import java.util.concurrent.ExecutionException;
33  import java.util.concurrent.Future;
34  import java.util.concurrent.FutureTask;
35  
36  import org.directwebremoting.util.Logger;
37  import static org.directwebremoting.guice.AbstractContextScope.State.*;
38  
39  /**
40   * Partial implementation of {@link ContextScope}. Concrete implementations
41   * must pass the context identifier type to the super constructor and define 
42   * {@code get()} to return the current context identifier (and to return null 
43   * or throw an exception if there is no current context). They must also implement
44   * the {@link ContextRegistry} interface.
45   * @author Tim Peierls [tim at peierls dot net]
46   */
47  public abstract class AbstractContextScope<C, R> 
48      implements ContextScope<C>, ContextRegistry<C, R>
49  {        
50      protected AbstractContextScope(Class<C> type, String scopeName) 
51      {
52          this.type = type;
53          this.scopeName = scopeName;
54      }
55      
56      public String toString()
57      {
58          return scopeName;
59      }
60      
61      public List<Key<?>> getKeysInScope()
62      {
63          synchronized (scopedKeys)
64          {
65              return new ArrayList<Key<?>>(scopedKeys);
66          }
67      }
68      
69      public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) 
70      {
71          if (log.isDebugEnabled())
72          {
73              log.debug(String.format(
74                  "scope %s: adding key %s with creator %s", 
75                  scopeName, key, creator
76              ));
77          }
78          
79          scopedKeys.add(key);
80          final String name = key.toString();
81          return new Provider<T>() 
82          {
83              public T get() 
84              {
85                  if (log.isDebugEnabled())
86                  {
87                      log.debug(String.format(
88                          "scope %s: getting key %s with creator %s", 
89                          scopeName, key, creator
90                      ));
91                  }
92          
93                  C context = getContext(key);
94                  R registry = registryFor(context);
95                  InstanceProvider<T> future = 
96                      AbstractContextScope.this.get(registry, key, name);
97                  if (future == null)
98                  {
99                      InstanceProvider<T> futureTask = new FutureTaskProvider<T>(creator);
100                     future = putIfAbsent(registry, key, name, futureTask);
101                     if (future == null)
102                     {
103                         future = futureTask;
104                         futureTask.run();
105                         if (Thread.currentThread().isInterrupted())
106                         {
107                             remove(registry, key, name, futureTask);
108                         }
109                     }
110                 }
111                 return future.get();
112             }
113 
114             public String toString() 
115             {
116                 return new ToStringBuilder(this.getClass())
117                     .add("scopeName", scopeName)
118                     .add("type", type)
119                     .add("key", key)
120                     .add("creator", creator)
121                     .toString();
122             }
123         };
124     }
125 
126     public abstract C get();
127 
128 
129     public Class<C> type() 
130     {
131         return type;
132     }
133     
134     public Collection<C> getOpenContexts()
135     {
136         Collection<C> openContexts = new ArrayList<C>();
137         for (C context : contexts.keySet())
138         {
139             if (contexts.get(context) == OPEN)
140             {
141                 openContexts.add(context);
142             }
143         }
144         return openContexts;
145     }
146     
147     public void close(C context, ContextCloseHandler<?>... closeHandlers)
148     {
149         if (!contexts.replace(context, OPEN, CLOSED))
150         {
151             // Context hadn't been opened or was already closed.
152             return;
153         }
154         
155         for (InstanceProvider<?> provider : registeredProviders(registryFor(context)))
156         {
157             Object value = null;
158             try
159             {
160                 value = provider.get();
161             }
162             catch (RuntimeException e)
163             {
164                 // Ignore runtime exceptions: they were thrown when
165                 // attempting creation and mean that no object was
166                 // created.
167             }
168             
169             if (value == null)
170             {
171                 // No instance was created by this provider, so we ignore.
172                 continue;
173             }
174             
175             for (ContextCloseHandler<?> closeHandler : closeHandlers)
176             {
177                 handleClose(closeHandler, value);
178             }
179         }
180     }
181     
182     public void closeAll(ContextCloseHandler<?>... closeHandlers)
183     {
184         for (C context : getOpenContexts())
185         {
186             close(context, closeHandlers);
187         }
188     }
189     
190     private <T> void handleClose(ContextCloseHandler<T> closeHandler, Object value)
191     {
192         Class<T> type = closeHandler.type();
193         if (type.isInstance(value))
194         {
195             try
196             {
197                 closeHandler.close(type.cast(value));
198             }
199             catch (Exception e)
200             {
201                 // Ignore exceptions when closing,
202                 // the closeHandler should have taken
203                 // appropriate action before rethrowing.
204             }
205         }
206     }
207     
208     private C getContext(Key<?> key)
209     {
210         C context = null;
211         RuntimeException caught = null;
212         try
213         {
214             context = get();
215             if (contexts.putIfAbsent(context, OPEN) == CLOSED)
216             {
217                 // Context is closed.
218                 context = null;
219             }
220         }
221         catch (RuntimeException ex)
222         {
223             caught = ex;
224         }
225         if (context == null)
226         {
227             throw new OutOfScopeException(this, key, caught);
228         }
229         
230         return context;
231     }
232     
233     private Collection<InstanceProvider<?>> registeredProviders(R registry) 
234     {
235         List<InstanceProvider<?>> providers = new ArrayList<InstanceProvider<?>>();
236         for (Key<?> key : getKeysInScope())
237         {
238             InstanceProvider<?> provider = get(registry, key, key.toString());
239             if (provider != null)
240             {
241                 providers.add(provider);
242             }
243         }
244         return providers;
245     }
246     
247     enum State
248     {
249         OPEN,
250         CLOSED
251     }
252     
253     private final Class<C> type;
254     
255     private final String scopeName;
256     
257     /* @GuardedBy("self") */
258     private final List<Key<?>> scopedKeys = synchronizedList(new ArrayList<Key<?>>());
259     
260     private final ConcurrentMap<C, State> contexts = new ConcurrentHashMap<C, State>();
261 
262     /**
263      * The log stream
264      */
265     private static final Logger log = Logger.getLogger(AbstractContextScope.class);
266 }