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 AbstractServiceModelImpl
036 {
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
045 private final ThreadEventNotifier _notifier;
046
047 /**
048 * Used to store the active service for the current thread.
049 */
050 private final ThreadLocal _activeService = new ThreadLocal();
051
052 /** @since 1.1 */
053
054 private Class _serviceInterface;
055
056 public ThreadedServiceModel(ConstructableServicePoint servicePoint)
057 {
058 super(servicePoint);
059
060 _serviceInterface = servicePoint.getServiceInterface();
061
062 Module module = getServicePoint().getModule();
063
064 _notifier = (ThreadEventNotifier) module.getService(
065 HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
066 ThreadEventNotifier.class);
067
068 _serviceProxy = createServiceProxy();
069 }
070
071 class CleanupListener implements ThreadCleanupListener
072 {
073 // The core itself
074 private final Object _core;
075
076 CleanupListener(Object core)
077 {
078 _core = core;
079 }
080
081 public void threadDidCleanup()
082 {
083 unbindServiceFromCurrentThread();
084
085 if (_core instanceof Discardable)
086 {
087 Discardable d = (Discardable) _core;
088
089 d.threadDidDiscardService();
090 }
091 }
092 }
093
094 /**
095 * Always returns the service proxy.
096 */
097 public Object getService()
098 {
099 // In 1.1 and earlier, we would lazily create the _serviceProxy here; but that meant the
100 // method had to be synchronized, which created a choke point.
101
102 // The result is an interceptor stack, where the final (most deeply nested) object
103 // is the serviceProxy. The serviceProxy obtains the instance for the current thread
104 // and delegates to it. This is a little bit different than SingletonServiceModel, which
105 // creates a pair of proxies so as to defer creation of the interceptors as well. In both
106 // cases, the interceptors are only created once.
107
108 return _serviceProxy;
109 }
110
111 /**
112 * Creates a proxy instance for the service, and returns it, wrapped in any interceptors for the
113 * service.
114 */
115 private Object createServiceProxy()
116 {
117 ConstructableServicePoint servicePoint = getServicePoint();
118
119 if (_log.isDebugEnabled())
120 _log.debug("Creating ThreadedProxy for service " + servicePoint.getExtensionPointId());
121
122 Object proxy = ProxyUtils.createDelegatingProxy(
123 "ThreadedProxy",
124 this,
125 "getServiceImplementationForCurrentThread",
126 servicePoint);
127
128 Object intercepted = addInterceptors(proxy);
129
130 RegistryShutdownListener outerProxy = ProxyUtils
131 .createOuterProxy(intercepted, servicePoint);
132
133 servicePoint.addRegistryShutdownListener(outerProxy);
134
135 return outerProxy;
136 }
137
138 /**
139 * Invoked by the proxy to return the active service impl for this thread, constructing it as
140 * necessary.
141 */
142 public Object getServiceImplementationForCurrentThread()
143 {
144 Object result = _activeService.get();
145
146 if (result == null)
147 result = constructInstanceForCurrentThread();
148
149 return result;
150 }
151
152 private Object constructInstanceForCurrentThread()
153 {
154 try
155 {
156 Object core = constructCoreServiceImplementation();
157
158 if (core instanceof RegistryShutdownListener)
159 _log.error(ServiceModelMessages.registryCleanupIgnored(getServicePoint()));
160
161 _notifier.addThreadCleanupListener(new CleanupListener(core));
162
163 // Once more ... with bean services, its possible that
164 // the factory generated bean does not implement the (synthetic) service
165 // interface, so create a bridge to it.
166
167 if (!_serviceInterface.isInstance(core))
168 core = constructBridgeProxy(core);
169
170 _activeService.set(core);
171
172 return core;
173 }
174 catch (Exception ex)
175 {
176 throw new ApplicationRuntimeException(ServiceModelMessages.unableToConstructService(
177 getServicePoint(),
178 ex), ex);
179 }
180 }
181
182 private void unbindServiceFromCurrentThread()
183 {
184 _activeService.set(null);
185 }
186
187 /**
188 * Invokes {@link #getServiceImplementationForCurrentThread()} to force the creation of the
189 * service implementation.
190 */
191
192 public void instantiateService()
193 {
194 getServiceImplementationForCurrentThread();
195 }
196
197 }