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    }