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 java.util.ArrayList;
018 import java.util.List;
019
020 import org.apache.hivemind.ApplicationRuntimeException;
021 import org.apache.hivemind.HiveMind;
022 import org.apache.hivemind.PoolManageable;
023 import org.apache.hivemind.events.RegistryShutdownListener;
024 import org.apache.hivemind.impl.ConstructableServicePoint;
025 import org.apache.hivemind.impl.ProxyUtils;
026 import org.apache.hivemind.internal.Module;
027 import org.apache.hivemind.service.ThreadCleanupListener;
028 import org.apache.hivemind.service.ThreadEventNotifier;
029
030 /**
031 * Similar to the
032 * {@link org.apache.hivemind.impl.servicemodel.ThreadedServiceModel threaded service model},
033 * except that, once created, services are pooled for later use.
034 *
035 * @author Howard Lewis Ship
036 */
037 public class PooledServiceModel extends AbstractServiceModelImpl
038 {
039 /**
040 * Name of a method in the deferred proxy that is used to obtain the constructed service.
041 */
042 protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
043
044 private final Object _serviceProxy;
045
046 private final ThreadEventNotifier _notifier;
047
048 private final ThreadLocal _activeService = new ThreadLocal();
049
050 private final List _servicePool = new ArrayList();
051
052 /** @since 1.1 */
053
054 private Class _serviceInterface;
055
056 /**
057 * Shared, null implementation of PoolManageable.
058 */
059 private static final PoolManageable NULL_MANAGEABLE = new PoolManageable()
060 {
061 public void activateService()
062 {
063 }
064
065 public void passivateService()
066 {
067 }
068 };
069
070 private class PooledService implements ThreadCleanupListener
071 {
072 private Object _core;
073
074 private PoolManageable _managed;
075
076 /**
077 * @param service
078 * the full service implementation, including any interceptors
079 * @param core
080 * the core service implementation, which may optionally implement
081 * {@link PoolManageable}
082 */
083 PooledService(Object core)
084 {
085 _core = core;
086
087 if (core instanceof PoolManageable)
088 _managed = (PoolManageable) core;
089 else
090 _managed = NULL_MANAGEABLE;
091 }
092
093 public void threadDidCleanup()
094 {
095 unbindPooledServiceFromCurrentThread(this);
096 }
097
098 void activate()
099 {
100 _managed.activateService();
101 }
102
103 void passivate()
104 {
105 _managed.passivateService();
106 }
107
108 /**
109 * Returns the configured service implementation.
110 */
111 public Object getService()
112 {
113 return _core;
114 }
115
116 }
117
118 public PooledServiceModel(ConstructableServicePoint servicePoint)
119 {
120 super(servicePoint);
121
122 _serviceInterface = servicePoint.getServiceInterface();
123
124 Module module = getServicePoint().getModule();
125
126 _notifier = (ThreadEventNotifier) module.getService(
127 HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
128 ThreadEventNotifier.class);
129
130 _serviceProxy = constructServiceProxy();
131 }
132
133 public Object getService()
134 {
135 return _serviceProxy;
136 }
137
138 /**
139 * Constructs the service proxy and returns it, wrapped in any interceptors.
140 */
141 private Object constructServiceProxy()
142 {
143 ConstructableServicePoint servicePoint = getServicePoint();
144
145 if (_log.isDebugEnabled())
146 _log.debug("Creating PooledProxy for service " + servicePoint.getExtensionPointId());
147
148 Object proxy = ProxyUtils.createDelegatingProxy(
149 "PooledProxy",
150 this,
151 "getServiceImplementationForCurrentThread",
152 servicePoint);
153
154 Object intercepted = addInterceptors(proxy);
155
156 RegistryShutdownListener outerProxy = ProxyUtils
157 .createOuterProxy(intercepted, servicePoint);
158
159 servicePoint.addRegistryShutdownListener(outerProxy);
160
161 return outerProxy;
162 }
163
164 public Object getServiceImplementationForCurrentThread()
165 {
166 PooledService pooled = (PooledService) _activeService.get();
167
168 if (pooled == null)
169 {
170 pooled = obtainPooledService();
171
172 pooled.activate();
173
174 _notifier.addThreadCleanupListener(pooled);
175 _activeService.set(pooled);
176 }
177
178 return pooled.getService();
179 }
180
181 private PooledService obtainPooledService()
182 {
183 PooledService result = getServiceFromPool();
184
185 if (result == null)
186 result = constructPooledService();
187
188 return result;
189 }
190
191 private synchronized PooledService getServiceFromPool()
192 {
193 int count = _servicePool.size();
194
195 if (count == 0)
196 return null;
197
198 return (PooledService) _servicePool.remove(count - 1);
199 }
200
201 private synchronized void returnServiceToPool(PooledService pooled)
202 {
203 _servicePool.add(pooled);
204 }
205
206 private PooledService constructPooledService()
207 {
208 try
209 {
210 Object core = constructCoreServiceImplementation();
211
212 // This is related to bean services.
213
214 if (!_serviceInterface.isInstance(core))
215 core = constructBridgeProxy(core);
216
217 registerWithShutdownCoordinator(core);
218
219 return new PooledService(core);
220 }
221 catch (Exception ex)
222 {
223 throw new ApplicationRuntimeException(ServiceModelMessages.unableToConstructService(
224 getServicePoint(),
225 ex), ex);
226 }
227 }
228
229 private void unbindPooledServiceFromCurrentThread(PooledService pooled)
230 {
231 _activeService.set(null);
232
233 pooled.passivate();
234
235 returnServiceToPool(pooled);
236 }
237
238 /**
239 * Invokes {@link #getServiceImplementationForCurrentThread()} to instantiate an instance of the
240 * service.
241 */
242 public void instantiateService()
243 {
244 getServiceImplementationForCurrentThread();
245 }
246
247 }