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;
016    
017    import java.util.ArrayList;
018    import java.util.Iterator;
019    import java.util.List;
020    
021    import org.apache.commons.logging.Log;
022    import org.apache.commons.logging.LogFactory;
023    import org.apache.hivemind.ApplicationRuntimeException;
024    import org.apache.hivemind.HiveMind;
025    import org.apache.hivemind.Occurances;
026    import org.apache.hivemind.ShutdownCoordinator;
027    import org.apache.hivemind.events.RegistryShutdownListener;
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.internal.ServiceModelFactory;
032    import org.apache.hivemind.order.Orderer;
033    import org.apache.hivemind.schema.Schema;
034    import org.apache.hivemind.service.InterfaceSynthesizer;
035    import org.apache.hivemind.util.ToStringBuilder;
036    
037    /**
038     * Abstract implementation of {@link org.apache.hivemind.internal.ServicePoint}. Provides some of
039     * the machinery for creating new service instances, delegating most of it to the
040     * {@link org.apache.hivemind.internal.ServiceModel} instace for the service.
041     * 
042     * @author Howard Lewis Ship
043     */
044    public final class ServicePointImpl extends AbstractExtensionPoint implements
045            ConstructableServicePoint
046    {
047        private Object _service;
048    
049        private boolean _building;
050    
051        private String _serviceInterfaceName;
052    
053        private Class _serviceInterface;
054    
055        private Class _declaredInterface;
056    
057        private ServiceImplementationConstructor _defaultServiceConstructor;
058    
059        private ServiceImplementationConstructor _serviceConstructor;
060    
061        private List _interceptorContributions;
062    
063        private boolean _interceptorsOrdered;
064    
065        private Schema _parametersSchema;
066    
067        private Occurances _parametersCount;
068    
069        private String _serviceModel;
070    
071        private ShutdownCoordinator _shutdownCoordinator;
072    
073        private ServiceModel _serviceModelObject;
074    
075        protected void extendDescription(ToStringBuilder builder)
076        {
077            if (_service != null)
078                builder.append("service", _service);
079    
080            builder.append("serviceInterfaceName", _serviceInterfaceName);
081            builder.append("defaultServiceConstructor", _defaultServiceConstructor);
082            builder.append("serviceConstructor", _serviceConstructor);
083            builder.append("interceptorContributions", _interceptorContributions);
084            builder.append("parametersSchema", _parametersSchema);
085            builder.append("parametersCount", _parametersCount);
086            builder.append("serviceModel", _serviceModel);
087    
088            if (_building)
089                builder.append("building", _building);
090        }
091    
092        public void addInterceptorContribution(ServiceInterceptorContribution contribution)
093        {
094            if (_interceptorContributions == null)
095                _interceptorContributions = new ArrayList();
096    
097            _interceptorContributions.add(contribution);
098        }
099    
100        public synchronized Class getServiceInterface()
101        {
102            if (_serviceInterface == null)
103                _serviceInterface = lookupServiceInterface();
104    
105            return _serviceInterface;
106        }
107    
108        public synchronized Class getDeclaredInterface()
109        {
110            if (_declaredInterface == null)
111                _declaredInterface = lookupDeclaredInterface();
112    
113            return _declaredInterface;
114        }
115    
116        /** @since 1.1 */
117    
118        public String getServiceInterfaceClassName()
119        {
120            return _serviceInterfaceName;
121        }
122    
123        private Class lookupDeclaredInterface()
124        {
125            Class result = null;
126    
127            try
128            {
129                result = getModule().resolveType(_serviceInterfaceName);
130            }
131            catch (Exception ex)
132            {
133                throw new ApplicationRuntimeException(ImplMessages.badInterface(
134                        _serviceInterfaceName,
135                        getExtensionPointId()), getLocation(), ex);
136            }
137    
138            return result;
139        }
140    
141        private Class lookupServiceInterface()
142        {
143            Class declaredInterface = getDeclaredInterface();
144    
145            if (declaredInterface.isInterface())
146                return declaredInterface;
147    
148            // Not an interface ... a class. Synthesize an interface from the class itself.
149    
150            InterfaceSynthesizer is = (InterfaceSynthesizer) getModule().getService(
151                    HiveMind.INTERFACE_SYNTHESIZER_SERVICE,
152                    InterfaceSynthesizer.class);
153    
154            return is.synthesizeInterface(declaredInterface);
155        }
156    
157        public void setServiceConstructor(ServiceImplementationConstructor contribution,
158                boolean defaultConstructor)
159        {
160            if (defaultConstructor)
161                _defaultServiceConstructor = contribution;
162            else
163                _serviceConstructor = contribution;
164        }
165    
166        public void setServiceInterfaceName(String string)
167        {
168            _serviceInterfaceName = string;
169        }
170    
171        public void setParametersSchema(Schema schema)
172        {
173            _parametersSchema = schema;
174        }
175    
176        public Schema getParametersSchema()
177        {
178            return _parametersSchema;
179        }
180    
181        public ServiceImplementationConstructor getServiceConstructor(boolean defaultConstructor)
182        {
183            return defaultConstructor ? _defaultServiceConstructor : _serviceConstructor;
184        }
185    
186        /**
187         * Invoked by {@link #getService(Class)} to get a service implementation from the
188         * {@link ServiceModel}.
189         * <p>
190         * TODO: I'm concerned that this synchronized method could cause a deadlock. It would take a LOT
191         * (mutually dependent services in multiple threads being realized at the same time).
192         */
193        private synchronized Object getService()
194        {
195            if (_service == null)
196            {
197    
198                if (_building)
199                    throw new ApplicationRuntimeException(ImplMessages.recursiveServiceBuild(this));
200    
201                _building = true;
202    
203                try
204                {
205    
206                    ServiceModelFactory factory = getModule().getServiceModelFactory(getServiceModel());
207    
208                    _serviceModelObject = factory.createServiceModelForService(this);
209    
210                    _service = _serviceModelObject.getService();
211                }
212                finally
213                {
214                    _building = false;
215                }
216            }
217    
218            return _service;
219        }
220    
221        public Object getService(Class serviceInterface)
222        {
223            Object result = getService();
224    
225            if (!serviceInterface.isAssignableFrom(result.getClass()))
226            {
227                throw new ApplicationRuntimeException(ImplMessages.serviceWrongInterface(
228                        this,
229                        serviceInterface), getLocation(), null);
230            }
231    
232            return result;
233        }
234    
235        public String getServiceModel()
236        {
237            return _serviceModel;
238        }
239    
240        public void setServiceModel(String model)
241        {
242            _serviceModel = model;
243        }
244    
245        public void clearConstructorInformation()
246        {
247            _serviceConstructor = null;
248            _interceptorContributions = null;
249        }
250    
251        // Hm. Does this need to be synchronized?
252    
253        public List getOrderedInterceptorContributions()
254        {
255            if (!_interceptorsOrdered)
256            {
257                _interceptorContributions = orderInterceptors();
258                _interceptorsOrdered = true;
259            }
260    
261            return _interceptorContributions;
262        }
263    
264        private List orderInterceptors()
265        {
266            if (HiveMind.isEmpty(_interceptorContributions))
267                return null;
268    
269            // Any error logging should go to the extension point
270            // we're constructing.
271    
272            Log log = LogFactory.getLog(getExtensionPointId());
273    
274            Orderer orderer = new Orderer(log, getModule().getErrorHandler(), ImplMessages
275                    .interceptorContribution());
276    
277            Iterator i = _interceptorContributions.iterator();
278            while (i.hasNext())
279            {
280                ServiceInterceptorContribution sic = (ServiceInterceptorContribution) i.next();
281    
282                // Sort them into runtime excecution order. When we build
283                // the interceptor stack we'll apply them in reverse order,
284                // building outward from the core service implementation.
285    
286                orderer.add(sic, sic.getName(), sic.getPrecedingInterceptorIds(), sic
287                        .getFollowingInterceptorIds());
288            }
289    
290            return orderer.getOrderedObjects();
291        }
292    
293        public void setShutdownCoordinator(ShutdownCoordinator coordinator)
294        {
295            _shutdownCoordinator = coordinator;
296        }
297    
298        public void addRegistryShutdownListener(RegistryShutdownListener listener)
299        {
300            _shutdownCoordinator.addRegistryShutdownListener(listener);
301        }
302    
303        /**
304         * Forces the service into existence.
305         */
306        public void forceServiceInstantiation()
307        {
308            getService();
309    
310            _serviceModelObject.instantiateService();
311        }
312    
313        public Occurances getParametersCount()
314        {
315            return _parametersCount;
316        }
317    
318        public void setParametersCount(Occurances occurances)
319        {
320            _parametersCount = occurances;
321        }
322    
323        /**
324         * Returns the service constructor, if defined, or the default service constructor. The default
325         * service constructor comes from the &lt;service-point&gt; itself; other modules can override
326         * this default using an &lt;implementation&gt; element.
327         */
328    
329        public ServiceImplementationConstructor getServiceConstructor()
330        {
331            return _serviceConstructor == null ? _defaultServiceConstructor : _serviceConstructor;
332        }
333    }