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.List; 018 019 import org.apache.commons.logging.Log; 020 import org.apache.commons.logging.LogFactory; 021 import org.apache.hivemind.ApplicationRuntimeException; 022 import org.apache.hivemind.HiveMind; 023 import org.apache.hivemind.ShutdownCoordinator; 024 import org.apache.hivemind.events.RegistryShutdownListener; 025 import org.apache.hivemind.impl.ConstructableServicePoint; 026 import org.apache.hivemind.impl.InterceptorStackImpl; 027 import org.apache.hivemind.impl.ProxyBuilder; 028 import org.apache.hivemind.internal.ServiceImplementationConstructor; 029 import org.apache.hivemind.internal.ServiceInterceptorContribution; 030 import org.apache.hivemind.internal.ServiceModel; 031 import org.apache.hivemind.service.ClassFab; 032 import org.apache.hivemind.util.ConstructorUtils; 033 034 /** 035 * Base class for implementing {@link org.apache.hivemind.internal.ServiceModel}. 036 * 037 * @author Howard Lewis Ship 038 */ 039 public abstract class AbstractServiceModelImpl implements ServiceModel 040 { 041 /** 042 * This log is created from the log's service id, which is the appropriate place to log any 043 * messages related to creating (or managing) the service implementation, proxy, etc. Subclasses 044 * should make use of this Log as well. 045 */ 046 protected final Log _log; 047 048 private ConstructableServicePoint _servicePoint; 049 050 /** @since 1.1 */ 051 private Class _bridgeProxyClass; 052 053 public AbstractServiceModelImpl(ConstructableServicePoint servicePoint) 054 { 055 _log = LogFactory.getLog(servicePoint.getExtensionPointId()); 056 057 _servicePoint = servicePoint; 058 } 059 060 protected Object addInterceptors(Object core) 061 { 062 List interceptors = _servicePoint.getOrderedInterceptorContributions(); 063 064 int count = interceptors == null ? 0 : interceptors.size(); 065 066 if (count == 0) 067 return core; 068 069 InterceptorStackImpl stack = new InterceptorStackImpl(_log, _servicePoint, core); 070 071 // They are sorted into runtime execution order. Since we build from the 072 // core service impl outwarads, we have to reverse the runtime execution 073 // order to get the build order. 074 // That is, if user expects interceptors in order A B C (perhaps using 075 // the rules: A before B, C after B). 076 // Then that's the order for interceptors list: A B C 077 // To get that runtime execution order, we wrap C around the core, 078 // wrap B around C, and wrap A around B. 079 080 for (int i = count - 1; i >= 0; i--) 081 { 082 ServiceInterceptorContribution ic = (ServiceInterceptorContribution) interceptors 083 .get(i); 084 085 stack.process(ic); 086 } 087 088 // Whatever's on top is the final service. 089 090 return stack.peek(); 091 } 092 093 /** 094 * Constructs the core service implementation (by invoking the 095 * {@link ServiceImplementationConstructor}), and checks that the result is non-null and 096 * assignable to the service interface. 097 */ 098 protected Object constructCoreServiceImplementation() 099 { 100 if (_log.isDebugEnabled()) 101 _log.debug("Constructing core service implementation for service " 102 + _servicePoint.getExtensionPointId()); 103 104 Class serviceInterface = _servicePoint.getServiceInterface(); 105 Class declaredInterface = _servicePoint.getDeclaredInterface(); 106 107 ServiceImplementationConstructor constructor = _servicePoint.getServiceConstructor(); 108 Object result = constructor.constructCoreServiceImplementation(); 109 110 if (result == null) 111 throw new ApplicationRuntimeException(ServiceModelMessages 112 .factoryReturnedNull(_servicePoint), constructor.getLocation(), null); 113 114 // The factory should provice something that either implements the service interface 115 // or the declared interface. Again, they are normally the same, but with services 116 // defined in terms of a class (not an interface), the service interface is 117 // synthetic, and the declared interface is the actual class. 118 119 if (!(serviceInterface.isInstance(result) || declaredInterface.isInstance(result))) 120 throw new ApplicationRuntimeException(ServiceModelMessages.factoryWrongInterface( 121 _servicePoint, 122 result, 123 serviceInterface), constructor.getLocation(), null); 124 125 HiveMind.setLocation(result, constructor.getLocation()); 126 127 return result; 128 } 129 130 /** 131 * Constructs the service implementation; this is invoked from 132 * {@link org.apache.hivemind.internal.ServicePoint#getService(Class)} (for singletons), or from 133 * the generated deferrable proxy (for most service models). Primarily, invokes 134 * {@link #constructNewServiceImplementation()} from within a block that checks for recursive 135 * builds. 136 */ 137 138 protected Object constructServiceImplementation() 139 { 140 Object result = constructNewServiceImplementation(); 141 142 // After succesfully building, we don't need 143 // some of the definition stuff again. 144 145 _servicePoint.clearConstructorInformation(); 146 147 return result; 148 } 149 150 /** 151 * Constructs a new implementation of the service, starting with a core implementation, then 152 * adding any interceptors. 153 */ 154 protected Object constructNewServiceImplementation() 155 { 156 try 157 { 158 Object core = constructCoreServiceImplementation(); 159 160 Object intercepted = addInterceptors(core); 161 162 return intercepted; 163 } 164 catch (Exception ex) 165 { 166 throw new ApplicationRuntimeException(ServiceModelMessages.unableToConstructService( 167 _servicePoint, 168 ex), ex); 169 } 170 171 } 172 173 public ConstructableServicePoint getServicePoint() 174 { 175 return _servicePoint; 176 } 177 178 /** 179 * Need to bridge from the service interface to the actual type. 180 * 181 * @since 1.1 182 */ 183 protected Object constructBridgeProxy(Object service) 184 { 185 Class bridgeProxyClass = getBridgeProxyClass(service); 186 187 return ConstructorUtils.invokeConstructor(bridgeProxyClass, new Object[] 188 { service }); 189 } 190 191 /** 192 * Factored out of {@link #constructBridgeProxy(Object)} to keep the synchronized block as small 193 * as possible. 194 * 195 * @since 1.2 196 */ 197 private synchronized Class getBridgeProxyClass(Object service) 198 { 199 if (_bridgeProxyClass == null) 200 _bridgeProxyClass = constructBridgeProxyClass(service); 201 202 return _bridgeProxyClass; 203 } 204 205 /** 206 * Assumes that the factory will keep cranking out instances of the same class. 207 * 208 * @since 1.1 209 */ 210 211 private Class constructBridgeProxyClass(Object service) 212 { 213 ProxyBuilder builder = new ProxyBuilder("BridgeProxy", getServicePoint()); 214 215 ClassFab cf = builder.getClassFab(); 216 217 Class serviceType = service.getClass(); 218 219 cf.addField("_service", serviceType); 220 221 cf.addConstructor(new Class[] 222 { serviceType }, null, "{ super(); _service = $1; }"); 223 224 builder.addServiceMethods("_service"); 225 226 return cf.createClass(); 227 } 228 229 /** 230 * Invoked after creating a service implementation object; if the object implements 231 * {@link org.apache.hivemind.events.RegistryShutdownListener}, then the object is added as a 232 * listener. 233 * 234 * @param service 235 * the service implementation 236 * @see ShutdownCoordinator 237 * @since 1.2 238 */ 239 protected void registerWithShutdownCoordinator(Object service) 240 { 241 if (service instanceof RegistryShutdownListener) 242 { 243 ShutdownCoordinator coordinator = ((ShutdownCoordinator) getServicePoint().getModule() 244 .getService(ShutdownCoordinator.class)); 245 246 RegistryShutdownListener asListener = (RegistryShutdownListener) service; 247 coordinator.addRegistryShutdownListener(asListener); 248 } 249 } 250 }