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.service.impl;
016    
017    import java.lang.reflect.Constructor;
018    import java.lang.reflect.InvocationTargetException;
019    import java.lang.reflect.Method;
020    import java.lang.reflect.Modifier;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    import java.util.Collections;
024    import java.util.Comparator;
025    import java.util.HashSet;
026    import java.util.Iterator;
027    import java.util.List;
028    import java.util.Set;
029    
030    import org.apache.commons.logging.Log;
031    import org.apache.hivemind.ApplicationRuntimeException;
032    import org.apache.hivemind.ErrorHandler;
033    import org.apache.hivemind.HiveMind;
034    import org.apache.hivemind.Location;
035    import org.apache.hivemind.ServiceImplementationFactoryParameters;
036    import org.apache.hivemind.internal.Module;
037    import org.apache.hivemind.service.EventLinker;
038    import org.apache.hivemind.util.ConstructorUtils;
039    import org.apache.hivemind.util.PropertyUtils;
040    
041    /**
042     * Created by {@link org.apache.hivemind.service.impl.BuilderFactory} for each service to be
043     * created; encapsulates all the direct and indirect parameters used to construct a service.
044     * 
045     * @author Howard Lewis Ship
046     */
047    public class BuilderFactoryLogic
048    {
049        /** @since 1.1 */
050        private ServiceImplementationFactoryParameters _factoryParameters;
051    
052        private String _serviceId;
053    
054        private BuilderParameter _parameter;
055    
056        private Log _log;
057    
058        private Module _contributingModule;
059    
060        public BuilderFactoryLogic(ServiceImplementationFactoryParameters factoryParameters,
061                BuilderParameter parameter)
062        {
063            _factoryParameters = factoryParameters;
064            _parameter = parameter;
065    
066            _log = _factoryParameters.getLog();
067            _serviceId = factoryParameters.getServiceId();
068            _contributingModule = factoryParameters.getInvokingModule();
069        }
070    
071        public Object createService()
072        {
073            try
074            {
075                Object result = instantiateCoreServiceInstance();
076    
077                setProperties(result);
078    
079                registerForEvents(result);
080    
081                invokeInitializer(result);
082    
083                return result;
084            }
085            catch (Exception ex)
086            {
087                throw new ApplicationRuntimeException(ServiceMessages.failureBuildingService(
088                        _serviceId,
089                        ex), _parameter.getLocation(), ex);
090            }
091        }
092    
093        private void error(String message, Location location, Throwable cause)
094        {
095            _factoryParameters.getErrorLog().error(message, location, cause);
096        }
097    
098        private Object instantiateCoreServiceInstance()
099        {
100            Class serviceClass = _contributingModule.resolveType(_parameter.getClassName());
101    
102            List parameters = _parameter.getParameters();
103    
104            if (_parameter.getAutowireServices() && parameters.isEmpty())
105            {
106                return instantiateConstructorAutowiredInstance(serviceClass);
107            }
108    
109            return instantiateExplicitConstructorInstance(serviceClass, parameters);
110        }
111    
112        private Object instantiateExplicitConstructorInstance(Class serviceClass, List builderParameters)
113        {
114            int numberOfParams = builderParameters.size();
115            List constructorCandidates = getServiceConstructorsOfLength(serviceClass, numberOfParams);
116    
117            outer: for (Iterator candidates = constructorCandidates.iterator(); candidates.hasNext();)
118            {
119                Constructor candidate = (Constructor) candidates.next();
120    
121                Class[] parameterTypes = candidate.getParameterTypes();
122    
123                Object[] parameters = new Object[parameterTypes.length];
124    
125                for (int i = 0; i < numberOfParams; i++)
126                {
127                    BuilderFacet facet = (BuilderFacet) builderParameters.get(i);
128    
129                    if (!facet.isAssignableToType(_factoryParameters, parameterTypes[i]))
130                        continue outer;
131    
132                    parameters[i] = facet.getFacetValue(_factoryParameters, parameterTypes[i]);
133                }
134    
135                return ConstructorUtils.invoke(candidate, parameters);
136            }
137    
138            throw new ApplicationRuntimeException(ServiceMessages.unableToFindAutowireConstructor(),
139                    _parameter.getLocation(), null);
140        }
141    
142        private List getServiceConstructorsOfLength(Class serviceClass, int length)
143        {
144            List fixedLengthConstructors = new ArrayList();
145    
146            Constructor[] constructors = serviceClass.getDeclaredConstructors();
147    
148            outer: for (int i = 0; i < constructors.length; i++)
149            {
150                if (!Modifier.isPublic(constructors[i].getModifiers()))
151                    continue;
152    
153                Class[] parameterTypes = constructors[i].getParameterTypes();
154    
155                if (parameterTypes.length != length)
156                    continue;
157    
158                fixedLengthConstructors.add(constructors[i]);
159            }
160    
161            return fixedLengthConstructors;
162        }
163    
164        private Object instantiateConstructorAutowiredInstance(Class serviceClass)
165        {
166            List serviceConstructorCandidates = getOrderedServiceConstructors(serviceClass);
167    
168            outer: for (Iterator candidates = serviceConstructorCandidates.iterator(); candidates
169                    .hasNext();)
170            {
171                Constructor candidate = (Constructor) candidates.next();
172    
173                Class[] parameterTypes = candidate.getParameterTypes();
174    
175                Object[] parameters = new Object[parameterTypes.length];
176    
177                for (int i = 0; i < parameters.length; i++)
178                {
179                    BuilderFacet facet = _parameter.getFacetForType(
180                            _factoryParameters,
181                            parameterTypes[i]);
182    
183                    if (facet != null && facet.canAutowireConstructorParameter())
184                        parameters[i] = facet.getFacetValue(_factoryParameters, parameterTypes[i]);
185                    else if (_contributingModule.containsService(parameterTypes[i]))
186                        parameters[i] = _contributingModule.getService(parameterTypes[i]);
187                    else
188                        continue outer;
189                }
190    
191                return ConstructorUtils.invoke(candidate, parameters);
192            }
193    
194            throw new ApplicationRuntimeException(ServiceMessages.unableToFindAutowireConstructor(),
195                    _parameter.getLocation(), null);
196        }
197    
198        private List getOrderedServiceConstructors(Class serviceClass)
199        {
200            List orderedInterfaceConstructors = new ArrayList();
201    
202            Constructor[] constructors = serviceClass.getDeclaredConstructors();
203    
204            outer: for (int i = 0; i < constructors.length; i++)
205            {
206                if (!Modifier.isPublic(constructors[i].getModifiers()))
207                    continue;
208    
209                Class[] parameterTypes = constructors[i].getParameterTypes();
210    
211                if (parameterTypes.length > 0)
212                {
213                    Set seenTypes = new HashSet();
214    
215                    for (int j = 0; j < parameterTypes.length; j++)
216                    {
217                        if (!parameterTypes[j].isInterface() || seenTypes.contains(parameterTypes[j]))
218                            continue outer;
219    
220                        seenTypes.add(parameterTypes[j]);
221                    }
222                }
223    
224                orderedInterfaceConstructors.add(constructors[i]);
225            }
226    
227            Collections.sort(orderedInterfaceConstructors, new Comparator()
228            {
229                public int compare(Object o1, Object o2)
230                {
231                    return ((Constructor) o2).getParameterTypes().length
232                            - ((Constructor) o1).getParameterTypes().length;
233                }
234            });
235    
236            return orderedInterfaceConstructors;
237        }
238    
239        private void invokeInitializer(Object service)
240        {
241            String methodName = _parameter.getInitializeMethod();
242    
243            boolean allowMissing = HiveMind.isBlank(methodName);
244    
245            String searchMethodName = allowMissing ? "initializeService" : methodName;
246    
247            try
248            {
249                findAndInvokeInitializerMethod(service, searchMethodName, allowMissing);
250            }
251            catch (InvocationTargetException ex)
252            {
253                Throwable cause = ex.getTargetException();
254    
255                error(ServiceMessages.unableToInitializeService(_serviceId, searchMethodName, service
256                        .getClass(), cause), _parameter.getLocation(), cause);
257            }
258            catch (Exception ex)
259            {
260                error(ServiceMessages.unableToInitializeService(_serviceId, searchMethodName, service
261                        .getClass(), ex), _parameter.getLocation(), ex);
262            }
263        }
264    
265        private void findAndInvokeInitializerMethod(Object service, String methodName,
266                boolean allowMissing) throws IllegalAccessException, InvocationTargetException,
267                NoSuchMethodException
268        {
269            Class serviceClass = service.getClass();
270    
271            try
272            {
273                Method m = serviceClass.getMethod(methodName, null);
274    
275                m.invoke(service, null);
276            }
277            catch (NoSuchMethodException ex)
278            {
279                if (allowMissing)
280                    return;
281    
282                throw ex;
283            }
284        }
285    
286        private void registerForEvents(Object result)
287        {
288            List eventRegistrations = _parameter.getEventRegistrations();
289    
290            if (eventRegistrations.isEmpty())
291                return;
292    
293            EventLinker linker = new EventLinkerImpl(_factoryParameters.getErrorLog());
294    
295            Iterator i = eventRegistrations.iterator();
296            while (i.hasNext())
297            {
298                EventRegistration er = (EventRegistration) i.next();
299    
300                // Will log any errors to the errorHandler
301    
302                linker.addEventListener(er.getProducer(), er.getEventSetName(), result, er
303                        .getLocation());
304            }
305        }
306    
307        private void setProperties(Object service)
308        {
309            List properties = _parameter.getProperties();
310            int count = properties.size();
311    
312            // Track the writeable properties, removing names as they are wired or autowired.
313    
314            Set writeableProperties = new HashSet(PropertyUtils.getWriteableProperties(service));
315    
316            for (int i = 0; i < count; i++)
317            {
318                BuilderFacet facet = (BuilderFacet) properties.get(i);
319    
320                String propertyName = wireProperty(service, facet);
321    
322                if (propertyName != null)
323                    writeableProperties.remove(propertyName);
324            }
325    
326            if (_parameter.getAutowireServices())
327                autowireServices(service, writeableProperties);
328    
329        }
330    
331        /**
332         * Wire (or auto-wire) the property; return the name of the property actually set (if a property
333         * is set, which is not always the case).
334         */
335        private String wireProperty(Object service, BuilderFacet facet)
336        {
337            String propertyName = facet.getPropertyName();
338    
339            try
340            {
341                // Autowire the property (if possible).
342    
343                String autowirePropertyName = facet.autowire(service, _factoryParameters);
344    
345                if (autowirePropertyName != null)
346                    return autowirePropertyName;
347    
348                // There will be a facet for log, messages, service-id, etc. even if no
349                // property name is specified, so we skip it here. In many cases, those
350                // facets will have just done an autowire.
351    
352                if (propertyName == null)
353                    return null;
354    
355                Class targetType = PropertyUtils.getPropertyType(service, propertyName);
356    
357                Object value = facet.getFacetValue(_factoryParameters, targetType);
358    
359                PropertyUtils.write(service, propertyName, value);
360    
361                if (_log.isDebugEnabled())
362                    _log.debug("Set property " + propertyName + " to " + value);
363    
364                return propertyName;
365            }
366            catch (Exception ex)
367            {
368                error(ex.getMessage(), facet.getLocation(), ex);
369    
370                return null;
371            }
372        }
373    
374        private void autowireServices(Object service, Collection propertyNames)
375        {
376            Iterator i = propertyNames.iterator();
377            while (i.hasNext())
378            {
379                String propertyName = (String) i.next();
380    
381                autowireProperty(service, propertyName);
382            }
383        }
384    
385        private void autowireProperty(Object service, String propertyName)
386        {
387            Class propertyType = PropertyUtils.getPropertyType(service, propertyName);
388    
389            try
390            {
391                // Ignore properties for which there are no corresponding
392                // service points...
393                if( _contributingModule.containsService( propertyType ) )
394                {
395                    Object collaboratingService = _contributingModule.getService(propertyType);
396                    PropertyUtils.write(service, propertyName, collaboratingService);
397                
398                    if (_log.isDebugEnabled())
399                    {
400                        _log.debug("Autowired service property " + propertyName + " to "
401                            + collaboratingService);
402                    }
403                }
404            }
405            catch (Exception ex)
406            {
407                getErrorHandler().error(
408                        _log,
409                        ServiceMessages.autowirePropertyFailure(propertyName, _serviceId, ex),
410                        _parameter.getLocation(),
411                        ex);
412            }
413        }
414    
415        private ErrorHandler getErrorHandler()
416        {
417            return _contributingModule.getErrorHandler();
418        }
419    
420    }