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 core 078 * the core service implementation, which may optionally implement 079 * {@link PoolManageable} 080 */ 081 PooledService(Object core) 082 { 083 _core = core; 084 085 if (core instanceof PoolManageable) 086 _managed = (PoolManageable) core; 087 else 088 _managed = NULL_MANAGEABLE; 089 } 090 091 public void threadDidCleanup() 092 { 093 unbindPooledServiceFromCurrentThread(this); 094 } 095 096 void activate() 097 { 098 _managed.activateService(); 099 } 100 101 void passivate() 102 { 103 _managed.passivateService(); 104 } 105 106 /** 107 * Returns the configured service implementation. 108 */ 109 public Object getService() 110 { 111 return _core; 112 } 113 114 } 115 116 public PooledServiceModel(ConstructableServicePoint servicePoint) 117 { 118 super(servicePoint); 119 120 _serviceInterface = servicePoint.getServiceInterface(); 121 122 Module module = getServicePoint().getModule(); 123 124 _notifier = (ThreadEventNotifier) module.getService( 125 HiveMind.THREAD_EVENT_NOTIFIER_SERVICE, 126 ThreadEventNotifier.class); 127 128 _serviceProxy = constructServiceProxy(); 129 } 130 131 public Object getService() 132 { 133 return _serviceProxy; 134 } 135 136 /** 137 * Constructs the service proxy and returns it, wrapped in any interceptors. 138 */ 139 private Object constructServiceProxy() 140 { 141 ConstructableServicePoint servicePoint = getServicePoint(); 142 143 if (_log.isDebugEnabled()) 144 _log.debug("Creating PooledProxy for service " + servicePoint.getExtensionPointId()); 145 146 Object proxy = ProxyUtils.createDelegatingProxy( 147 "PooledProxy", 148 this, 149 "getServiceImplementationForCurrentThread", 150 servicePoint); 151 152 Object intercepted = addInterceptors(proxy); 153 154 RegistryShutdownListener outerProxy = ProxyUtils 155 .createOuterProxy(intercepted, servicePoint); 156 157 servicePoint.addRegistryShutdownListener(outerProxy); 158 159 return outerProxy; 160 } 161 162 public Object getServiceImplementationForCurrentThread() 163 { 164 PooledService pooled = (PooledService) _activeService.get(); 165 166 if (pooled == null) 167 { 168 pooled = obtainPooledService(); 169 170 pooled.activate(); 171 172 _notifier.addThreadCleanupListener(pooled); 173 _activeService.set(pooled); 174 } 175 176 return pooled.getService(); 177 } 178 179 private PooledService obtainPooledService() 180 { 181 PooledService result = getServiceFromPool(); 182 183 if (result == null) 184 result = constructPooledService(); 185 186 return result; 187 } 188 189 private synchronized PooledService getServiceFromPool() 190 { 191 int count = _servicePool.size(); 192 193 if (count == 0) 194 return null; 195 196 return (PooledService) _servicePool.remove(count - 1); 197 } 198 199 private synchronized void returnServiceToPool(PooledService pooled) 200 { 201 _servicePool.add(pooled); 202 } 203 204 private PooledService constructPooledService() 205 { 206 try 207 { 208 Object core = constructCoreServiceImplementation(); 209 210 // This is related to bean services. 211 212 if (!_serviceInterface.isInstance(core)) 213 core = constructBridgeProxy(core); 214 215 registerWithShutdownCoordinator(core); 216 217 return new PooledService(core); 218 } 219 catch (Exception ex) 220 { 221 throw new ApplicationRuntimeException(ServiceModelMessages.unableToConstructService( 222 getServicePoint(), 223 ex), ex); 224 } 225 } 226 227 private void unbindPooledServiceFromCurrentThread(PooledService pooled) 228 { 229 _activeService.set(null); 230 231 pooled.passivate(); 232 233 returnServiceToPool(pooled); 234 } 235 236 /** 237 * Invokes {@link #getServiceImplementationForCurrentThread()} to instantiate an instance of the 238 * service. 239 */ 240 public void instantiateService() 241 { 242 getServiceImplementationForCurrentThread(); 243 } 244 245 }