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 }