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 }