001: // Copyright 2004, 2005 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.hivemind.impl.servicemodel;
016:
017: import org.apache.hivemind.ApplicationRuntimeException;
018: import org.apache.hivemind.Discardable;
019: import org.apache.hivemind.HiveMind;
020: import org.apache.hivemind.events.RegistryShutdownListener;
021: import org.apache.hivemind.impl.ConstructableServicePoint;
022: import org.apache.hivemind.impl.ProxyUtils;
023: import org.apache.hivemind.internal.Module;
024: import org.apache.hivemind.service.ThreadCleanupListener;
025: import org.apache.hivemind.service.ThreadEventNotifier;
026:
027: /**
028: * Like {@link org.apache.hivemind.impl.servicemodel.SingletonServiceModel}, this method returns a
029: * proxy (implementing the service interface); unlike SingletonServiceModel, it <em>always</em>
030: * returns the proxy. Invoking a service method on the proxy constructs a service implementation and
031: * binds it to the current thread.
032: *
033: * @author Howard Lewis Ship
034: */
035: public final class ThreadedServiceModel extends
036: AbstractServiceModelImpl {
037: /**
038: * Name of a method in the deferred proxy that is used to obtain the constructed service.
039: */
040: protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
041:
042: private final Object _serviceProxy;
043:
044: private final ThreadEventNotifier _notifier;
045:
046: /**
047: * Used to store the active service for the current thread.
048: */
049: private final ThreadLocal _activeService = new ThreadLocal();
050:
051: /** @since 1.1 */
052:
053: private Class _serviceInterface;
054:
055: public ThreadedServiceModel(ConstructableServicePoint servicePoint) {
056: super (servicePoint);
057:
058: _serviceInterface = servicePoint.getServiceInterface();
059:
060: Module module = getServicePoint().getModule();
061:
062: _notifier = (ThreadEventNotifier) module.getService(
063: HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
064: ThreadEventNotifier.class);
065:
066: _serviceProxy = createServiceProxy();
067: }
068:
069: class CleanupListener implements ThreadCleanupListener {
070: // The core itself
071: private final Object _core;
072:
073: CleanupListener(Object core) {
074: _core = core;
075: }
076:
077: public void threadDidCleanup() {
078: unbindServiceFromCurrentThread();
079:
080: if (_core instanceof Discardable) {
081: Discardable d = (Discardable) _core;
082:
083: d.threadDidDiscardService();
084: }
085: }
086: }
087:
088: /**
089: * Always returns the service proxy.
090: */
091: public Object getService() {
092: // In 1.1 and earlier, we would lazily create the _serviceProxy here; but that meant the
093: // method had to be synchronized, which created a choke point.
094:
095: // The result is an interceptor stack, where the final (most deeply nested) object
096: // is the serviceProxy. The serviceProxy obtains the instance for the current thread
097: // and delegates to it. This is a little bit different than SingletonServiceModel, which
098: // creates a pair of proxies so as to defer creation of the interceptors as well. In both
099: // cases, the interceptors are only created once.
100:
101: return _serviceProxy;
102: }
103:
104: /**
105: * Creates a proxy instance for the service, and returns it, wrapped in any interceptors for the
106: * service.
107: */
108: private Object createServiceProxy() {
109: ConstructableServicePoint servicePoint = getServicePoint();
110:
111: if (_log.isDebugEnabled())
112: _log.debug("Creating ThreadedProxy for service "
113: + servicePoint.getExtensionPointId());
114:
115: Object proxy = ProxyUtils.createDelegatingProxy(
116: "ThreadedProxy", this ,
117: "getServiceImplementationForCurrentThread",
118: servicePoint);
119:
120: Object intercepted = addInterceptors(proxy);
121:
122: RegistryShutdownListener outerProxy = ProxyUtils
123: .createOuterProxy(intercepted, servicePoint);
124:
125: servicePoint.addRegistryShutdownListener(outerProxy);
126:
127: return outerProxy;
128: }
129:
130: /**
131: * Invoked by the proxy to return the active service impl for this thread, constructing it as
132: * necessary.
133: */
134: public Object getServiceImplementationForCurrentThread() {
135: Object result = _activeService.get();
136:
137: if (result == null)
138: result = constructInstanceForCurrentThread();
139:
140: return result;
141: }
142:
143: private Object constructInstanceForCurrentThread() {
144: try {
145: Object core = constructCoreServiceImplementation();
146:
147: if (core instanceof RegistryShutdownListener)
148: _log.error(ServiceModelMessages
149: .registryCleanupIgnored(getServicePoint()));
150:
151: _notifier
152: .addThreadCleanupListener(new CleanupListener(core));
153:
154: // Once more ... with bean services, its possible that
155: // the factory generated bean does not implement the (synthetic) service
156: // interface, so create a bridge to it.
157:
158: if (!_serviceInterface.isInstance(core))
159: core = constructBridgeProxy(core);
160:
161: _activeService.set(core);
162:
163: return core;
164: } catch (Exception ex) {
165: throw new ApplicationRuntimeException(ServiceModelMessages
166: .unableToConstructService(getServicePoint(), ex),
167: ex);
168: }
169: }
170:
171: private void unbindServiceFromCurrentThread() {
172: _activeService.set(null);
173: }
174:
175: /**
176: * Invokes {@link #getServiceImplementationForCurrentThread()} to force the creation of the
177: * service implementation.
178: */
179:
180: public void instantiateService() {
181: getServiceImplementationForCurrentThread();
182: }
183:
184: }
|