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 }