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 java.util.List;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021import org.apache.hivemind.ApplicationRuntimeException;
022import org.apache.hivemind.HiveMind;
023import org.apache.hivemind.ShutdownCoordinator;
024import org.apache.hivemind.events.RegistryShutdownListener;
025import org.apache.hivemind.impl.ConstructableServicePoint;
026import org.apache.hivemind.impl.InterceptorStackImpl;
027import org.apache.hivemind.impl.ProxyBuilder;
028import org.apache.hivemind.internal.ServiceImplementationConstructor;
029import org.apache.hivemind.internal.ServiceInterceptorContribution;
030import org.apache.hivemind.internal.ServiceModel;
031import org.apache.hivemind.service.ClassFab;
032import 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 */
039public 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}