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 015package org.apache.hivemind.impl.servicemodel; 016 017import org.apache.hivemind.ApplicationRuntimeException; 018import org.apache.hivemind.Discardable; 019import org.apache.hivemind.HiveMind; 020import org.apache.hivemind.events.RegistryShutdownListener; 021import org.apache.hivemind.impl.ConstructableServicePoint; 022import org.apache.hivemind.impl.ProxyUtils; 023import org.apache.hivemind.internal.Module; 024import org.apache.hivemind.service.ThreadCleanupListener; 025import 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 */ 035public 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}