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