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