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.ShutdownCoordinator; 021 import org.apache.hivemind.events.RegistryShutdownListener; 022 import org.apache.hivemind.impl.ConstructableServicePoint; 023 import org.apache.hivemind.impl.ProxyUtils; 024 import org.apache.hivemind.internal.Module; 025 import org.apache.hivemind.service.ThreadCleanupListener; 026 import org.apache.hivemind.service.ThreadEventNotifier; 027 028 /** 029 * Like {@link org.apache.hivemind.impl.servicemodel.SingletonServiceModel}, this method returns a proxy 030 * (implementing the service interface); unlike SingletonServiceModel, it <em>always</em> returns 031 * the proxy. Invoking a service method on the proxy constructs a service implementation and binds 032 * it to the current thread. 033 * 034 * @author Howard Lewis Ship 035 */ 036 public final class ThreadedServiceModel extends AbstractServiceModelImpl 037 { 038 /** 039 * Name of a method in the deferred proxy that is used to obtain the constructed service. 040 */ 041 protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service"; 042 043 private Object _serviceProxy; 044 045 private ThreadEventNotifier _notifier; 046 047 /** @since 1.1 */ 048 049 private Class _serviceInterface; 050 051 public ThreadedServiceModel(ConstructableServicePoint servicePoint) 052 { 053 super(servicePoint); 054 055 _serviceInterface = servicePoint.getServiceInterface(); 056 } 057 058 class CleanupListener implements ThreadCleanupListener 059 { 060 // The core itself 061 private Object _core; 062 063 CleanupListener(Object core) 064 { 065 _core = core; 066 } 067 068 public void threadDidCleanup() 069 { 070 // Orhpan this object 071 _notifier.removeThreadCleanupListener(this); 072 073 unbindServiceFromCurrentThread(); 074 075 if (_core instanceof Discardable) 076 { 077 Discardable d = (Discardable) _core; 078 079 d.threadDidDiscardService(); 080 } 081 } 082 } 083 084 /** 085 * Used to store the active service for the current thread. 086 */ 087 private ThreadLocal _activeService; 088 089 /** 090 * Always returns the service proxy. 091 */ 092 public synchronized Object getService() 093 { 094 // _activeService will be null on first invocation; a good 095 // time to create it, the proxy, and find the notifier. 096 097 if (_activeService == null) 098 { 099 _activeService = new ThreadLocal(); 100 101 Module module = getServicePoint().getModule(); 102 103 _notifier = (ThreadEventNotifier) module.getService( 104 HiveMind.THREAD_EVENT_NOTIFIER_SERVICE, 105 ThreadEventNotifier.class); 106 107 _serviceProxy = createServiceProxy(); 108 } 109 110 // The result is an interceptor stack, where the final (most deeply nested) object 111 // is the serviceProxy. The serviceProxy obtains the instance for the current thread 112 // and delegates to it. This is a little bit different than SingletonServiceModel, which 113 // creates a pair of proxies so as to defer creation of the interceptors as well. In both 114 // cases, the interceptors are only created once. 115 116 return _serviceProxy; 117 } 118 119 /** 120 * Creates a proxy instance for the service, and returns it, wrapped in any interceptors for the 121 * service. 122 */ 123 private Object createServiceProxy() 124 { 125 ConstructableServicePoint servicePoint = getServicePoint(); 126 127 if (_log.isDebugEnabled()) 128 _log.debug("Creating ThreadedProxy for service " + servicePoint.getExtensionPointId()); 129 130 Object proxy = ProxyUtils.createDelegatingProxy( 131 "ThreadedProxy", 132 this, 133 "getServiceImplementationForCurrentThread", 134 servicePoint); 135 136 Object intercepted = addInterceptors(proxy); 137 138 RegistryShutdownListener outerProxy = ProxyUtils 139 .createOuterProxy(intercepted, servicePoint); 140 141 ShutdownCoordinator coordinator = servicePoint.getShutdownCoordinator(); 142 143 coordinator.addRegistryShutdownListener(outerProxy); 144 145 return outerProxy; 146 } 147 148 /** 149 * Invoked by the proxy to return the active service impl for this thread, constructing it as 150 * necessary. 151 */ 152 public Object getServiceImplementationForCurrentThread() 153 { 154 Object result = _activeService.get(); 155 156 if (result == null) 157 result = constructServiceForCurrentThread(); 158 159 return result; 160 } 161 162 private synchronized Object constructServiceForCurrentThread() 163 { 164 try 165 { 166 Object core = constructCoreServiceImplementation(); 167 168 if (core instanceof RegistryShutdownListener) 169 _log.error(ServiceModelMessages.registryCleanupIgnored(getServicePoint())); 170 171 _notifier.addThreadCleanupListener(new CleanupListener(core)); 172 173 // Once more ... with bean services, its possible that 174 // the factory generated bean does not implement the (synthetic) service 175 // interface, so create a bridge to it. 176 177 if (!_serviceInterface.isInstance(core)) 178 core = constructBridgeProxy(core); 179 180 _activeService.set(core); 181 182 return core; 183 } 184 catch (Exception ex) 185 { 186 throw new ApplicationRuntimeException(ServiceModelMessages.unableToConstructService( 187 getServicePoint(), 188 ex), ex); 189 } 190 } 191 192 private void unbindServiceFromCurrentThread() 193 { 194 _activeService.set(null); 195 } 196 197 /** 198 * Invokes {@link #getServiceImplementationForCurrentThread()} to force the creation of the 199 * service implementation. 200 */ 201 202 public void instantiateService() 203 { 204 getServiceImplementationForCurrentThread(); 205 } 206 207 }