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
015package org.apache.hivemind.impl;
016
017import java.util.ArrayList;
018import java.util.Iterator;
019import java.util.List;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023import org.apache.hivemind.ApplicationRuntimeException;
024import org.apache.hivemind.HiveMind;
025import org.apache.hivemind.Occurances;
026import org.apache.hivemind.ShutdownCoordinator;
027import org.apache.hivemind.events.RegistryShutdownListener;
028import org.apache.hivemind.internal.ServiceImplementationConstructor;
029import org.apache.hivemind.internal.ServiceInterceptorContribution;
030import org.apache.hivemind.internal.ServiceModel;
031import org.apache.hivemind.internal.ServiceModelFactory;
032import org.apache.hivemind.order.Orderer;
033import org.apache.hivemind.schema.Schema;
034import org.apache.hivemind.service.InterfaceSynthesizer;
035import 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 */
044public 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}