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