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    }