001: /*
002: * Copyright 2007 Tim Peierls
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.directwebremoting.guice;
017:
018: import java.util.ArrayList;
019: import java.util.Collection;
020: import java.util.List;
021: import java.util.concurrent.ConcurrentHashMap;
022: import java.util.concurrent.ConcurrentMap;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026:
027: import com.google.inject.Key;
028: import com.google.inject.Provider;
029: import com.google.inject.util.ToStringBuilder;
030:
031: import static java.util.Collections.synchronizedList;
032:
033: import static org.directwebremoting.guice.AbstractContextScope.State.CLOSED;
034: import static org.directwebremoting.guice.AbstractContextScope.State.OPEN;
035:
036: /**
037: * Partial implementation of {@link ContextScope}. Concrete implementations
038: * must pass the context identifier type to the super constructor and define
039: * {@code get()} to return the current context identifier (and to return null
040: * or throw an exception if there is no current context). They must also implement
041: * the {@link ContextRegistry} interface.
042: * @author Tim Peierls [tim at peierls dot net]
043: */
044: public abstract class AbstractContextScope<C, R> implements
045: ContextScope<C>, ContextRegistry<C, R> {
046: protected AbstractContextScope(Class<C> type, String scopeName) {
047: this .type = type;
048: this .scopeName = scopeName;
049: }
050:
051: /* (non-Javadoc)
052: * @see java.lang.Object#toString()
053: */
054: @Override
055: public String toString() {
056: return scopeName;
057: }
058:
059: /* (non-Javadoc)
060: * @see org.directwebremoting.guice.ContextScope#getKeysInScope()
061: */
062: public List<Key<?>> getKeysInScope() {
063: synchronized (scopedKeys) {
064: return new ArrayList<Key<?>>(scopedKeys);
065: }
066: }
067:
068: /* (non-Javadoc)
069: * @see org.directwebremoting.guice.ContextScope#scope(com.google.inject.Key, com.google.inject.Provider)
070: */
071: public <T> Provider<T> scope(final Key<T> key,
072: final Provider<T> creator) {
073: if (log.isDebugEnabled()) {
074: log.debug(String.format(
075: "scope %s: adding key %s with creator %s",
076: scopeName, key, creator));
077: }
078:
079: scopedKeys.add(key);
080: final String name = key.toString();
081: return new Provider<T>() {
082: public T get() {
083: if (log.isDebugEnabled()) {
084: log.debug(String.format(
085: "scope %s: getting key %s with creator %s",
086: scopeName, key, creator));
087: }
088:
089: C context = getContext(key);
090: R registry = registryFor(context);
091: InstanceProvider<T> future = AbstractContextScope.this
092: .get(registry, key, name);
093: if (future == null) {
094: InstanceProvider<T> futureTask = new FutureTaskProvider<T>(
095: creator);
096: future = putIfAbsent(registry, key, name,
097: futureTask);
098: if (future == null) {
099: future = futureTask;
100: futureTask.run();
101: if (Thread.currentThread().isInterrupted()) {
102: remove(registry, key, name, futureTask);
103: }
104: }
105: }
106: return future.get();
107: }
108:
109: @Override
110: public String toString() {
111: return new ToStringBuilder(this .getClass()).add(
112: "scopeName", scopeName).add("type", type).add(
113: "key", key).add("creator", creator).toString();
114: }
115: };
116: }
117:
118: public abstract C get();
119:
120: /* (non-Javadoc)
121: * @see org.directwebremoting.guice.ContextScope#type()
122: */
123: public Class<C> type() {
124: return type;
125: }
126:
127: /* (non-Javadoc)
128: * @see org.directwebremoting.guice.ContextScope#getOpenContexts()
129: */
130: public Collection<C> getOpenContexts() {
131: Collection<C> openContexts = new ArrayList<C>();
132: for (C context : contexts.keySet()) {
133: if (contexts.get(context) == OPEN) {
134: openContexts.add(context);
135: }
136: }
137: return openContexts;
138: }
139:
140: /* (non-Javadoc)
141: * @see org.directwebremoting.guice.ContextScope#close(java.lang.Object, org.directwebremoting.guice.ContextCloseHandler<?>[])
142: */
143: public void close(C context,
144: ContextCloseHandler<?>... closeHandlers) {
145: if (!contexts.replace(context, OPEN, CLOSED)) {
146: // Context hadn't been opened or was already closed.
147: return;
148: }
149:
150: for (InstanceProvider<?> provider : registeredProviders(registryFor(context))) {
151: Object value = null;
152: try {
153: value = provider.get();
154: } catch (RuntimeException e) {
155: // Ignore runtime exceptions: they were thrown when
156: // attempting creation and mean that no object was
157: // created.
158: }
159:
160: if (value == null) {
161: // No instance was created by this provider, so we ignore.
162: continue;
163: }
164:
165: for (ContextCloseHandler<?> closeHandler : closeHandlers) {
166: handleClose(closeHandler, value);
167: }
168: }
169: }
170:
171: /* (non-Javadoc)
172: * @see org.directwebremoting.guice.ContextScope#closeAll(org.directwebremoting.guice.ContextCloseHandler<?>[])
173: */
174: public void closeAll(ContextCloseHandler<?>... closeHandlers) {
175: for (C context : getOpenContexts()) {
176: close(context, closeHandlers);
177: }
178: }
179:
180: private <T> void handleClose(ContextCloseHandler<T> closeHandler,
181: Object value) {
182: Class<T> closeType = closeHandler.type();
183: if (closeType.isInstance(value)) {
184: try {
185: closeHandler.close(closeType.cast(value));
186: } catch (Exception e) {
187: // Ignore exceptions when closing,
188: // the closeHandler should have taken
189: // appropriate action before re-throwing.
190: }
191: }
192: }
193:
194: protected C getContext(Key<?> key) {
195: C context = null;
196: RuntimeException caught = null;
197: try {
198: context = get();
199: if (contexts.putIfAbsent(context, OPEN) == CLOSED) {
200: // Context is closed.
201: context = null;
202: }
203: } catch (RuntimeException ex) {
204: caught = ex;
205: }
206: if (context == null) {
207: throw new OutOfScopeException(this , key, caught);
208: }
209:
210: return context;
211: }
212:
213: private Collection<InstanceProvider<?>> registeredProviders(
214: R registry) {
215: List<InstanceProvider<?>> providers = new ArrayList<InstanceProvider<?>>();
216: for (Key<?> key : getKeysInScope()) {
217: InstanceProvider<?> provider = get(registry, key, key
218: .toString());
219: if (provider != null) {
220: providers.add(provider);
221: }
222: }
223: return providers;
224: }
225:
226: enum State {
227: OPEN, CLOSED
228: }
229:
230: protected final Class<C> type;
231:
232: protected final String scopeName;
233:
234: /* @GuardedBy("self") */
235: private final List<Key<?>> scopedKeys = synchronizedList(new ArrayList<Key<?>>());
236:
237: private final ConcurrentMap<C, State> contexts = new ConcurrentHashMap<C, State>();
238:
239: /**
240: * The log stream
241: */
242: protected static final Log log = LogFactory
243: .getLog(AbstractContextScope.class);
244: }
|