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