001 /***************************************************************************** 002 * Copyright (c) PicoContainer Organization. All rights reserved. * 003 * ------------------------------------------------------------------------- * 004 * The software in this package is published under the terms of the BSD * 005 * style license a copy of which has been included with this distribution in * 006 * the LICENSE.txt file. * 007 * * 008 *****************************************************************************/ 009 010 package org.picocontainer.injectors; 011 012 import java.lang.reflect.InvocationTargetException; 013 import java.lang.reflect.Method; 014 import java.lang.reflect.Type; 015 016 import org.picocontainer.ComponentMonitor; 017 import org.picocontainer.LifecycleStrategy; 018 import org.picocontainer.Parameter; 019 import org.picocontainer.PicoCompositionException; 020 import org.picocontainer.PicoContainer; 021 022 /** 023 * Injection will happen through a single method for the component. 024 * 025 * Most likely it is a method called 'inject', though that can be overridden. 026 * 027 * @author Paul Hammant 028 * @author Aslak Hellesøy 029 * @author Jon Tirsén 030 * @author Zohar Melamed 031 * @author Jörg Schaible 032 * @author Mauro Talevi 033 */ 034 @SuppressWarnings("serial") 035 public class MethodInjector<T> extends SingleMemberInjector<T> { 036 private transient ThreadLocalCyclicDependencyGuard instantiationGuard; 037 private final String methodName; 038 039 /** 040 * Creates a MethodInjector 041 * 042 * @param componentKey the search key for this implementation 043 * @param componentImplementation the concrete implementation 044 * @param parameters the parameters to use for the initialization 045 * @param monitor the component monitor used by this addAdapter 046 * @param lifecycleStrategy the component lifecycle strategy used by this addAdapter 047 * @param methodName the method name 048 * @param useNames use argument names when looking up dependencies 049 * @throws AbstractInjector.NotConcreteRegistrationException 050 * if the implementation is not a concrete class. 051 * @throws NullPointerException if one of the parameters is <code>null</code> 052 */ 053 public MethodInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, 054 LifecycleStrategy lifecycleStrategy, String methodName, boolean useNames) throws AbstractInjector.NotConcreteRegistrationException { 055 super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, useNames); 056 this.methodName = methodName; 057 } 058 059 protected Method getInjectorMethod() { 060 Method[] methods = new Method[0]; 061 try { 062 methods = super.getComponentImplementation().getMethods(); 063 } catch (AmbiguousComponentResolutionException e) { 064 e.setComponent(getComponentImplementation()); 065 throw e; 066 } 067 for (Method method : methods) { 068 if (method.getName().equals(methodName)) { 069 return method; 070 } 071 } 072 return null; 073 } 074 075 public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException { 076 if (instantiationGuard == null) { 077 instantiationGuard = new ThreadLocalCyclicDependencyGuard() { 078 public Object run() { 079 Method method = getInjectorMethod(); 080 T inst = null; 081 ComponentMonitor componentMonitor = currentMonitor(); 082 try { 083 componentMonitor.instantiating(container, MethodInjector.this, null); 084 long startTime = System.currentTimeMillis(); 085 Object[] parameters = null; 086 inst = getComponentImplementation().newInstance(); 087 if (method != null) { 088 parameters = getMemberArguments(guardedContainer, method); 089 invokeMethod(method, parameters, inst, container); 090 } 091 componentMonitor.instantiated(container, MethodInjector.this, 092 null, inst, parameters, System.currentTimeMillis() - startTime); 093 return inst; 094 } catch (InstantiationException e) { 095 return caughtInstantiationException(componentMonitor, null, e, container); 096 } catch (IllegalAccessException e) { 097 return caughtIllegalAccessException(componentMonitor, method, inst, e); 098 099 } 100 } 101 }; 102 } 103 instantiationGuard.setGuardedContainer(container); 104 return (T) instantiationGuard.observe(getComponentImplementation()); 105 } 106 107 protected Object[] getMemberArguments(PicoContainer container, final Method method) { 108 return super.getMemberArguments(container, method, method.getParameterTypes(), getBindings(method.getParameterAnnotations())); 109 } 110 111 public void decorateComponentInstance(final PicoContainer container, Type into, final T instance) { 112 if (instantiationGuard == null) { 113 instantiationGuard = new ThreadLocalCyclicDependencyGuard() { 114 public Object run() { 115 Method method = getInjectorMethod(); 116 Object inst = null; 117 Object[] parameters = null; 118 parameters = getMemberArguments(guardedContainer, method); 119 return invokeMethod(method, parameters, instance, container); 120 } 121 }; 122 } 123 instantiationGuard.setGuardedContainer(container); 124 instantiationGuard.observe(getComponentImplementation()); 125 126 } 127 128 private Object invokeMethod(Method method, Object[] parameters, T instance, PicoContainer container) { 129 try { 130 return method.invoke(instance, parameters); 131 } catch (IllegalAccessException e) { 132 return caughtIllegalAccessException(currentMonitor(), method, instance, e); 133 } catch (InvocationTargetException e) { 134 currentMonitor().instantiationFailed(container, MethodInjector.this, null, e); 135 if (e.getTargetException() instanceof RuntimeException) { 136 throw (RuntimeException) e.getTargetException(); 137 } else if (e.getTargetException() instanceof Error) { 138 throw (Error) e.getTargetException(); 139 } 140 } 141 return null; 142 } 143 144 145 @Override 146 public void verify(final PicoContainer container) throws PicoCompositionException { 147 if (verifyingGuard == null) { 148 verifyingGuard = new ThreadLocalCyclicDependencyGuard() { 149 public Object run() { 150 final Method method = getInjectorMethod(); 151 final Class[] parameterTypes = method.getParameterTypes(); 152 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes); 153 for (int i = 0; i < currentParameters.length; i++) { 154 currentParameters[i].verify(container, MethodInjector.this, parameterTypes[i], 155 new ParameterNameBinding(getParanamer(), getComponentImplementation(), method, i), useNames(), 156 getBindings(method.getParameterAnnotations())[i]); 157 } 158 return null; 159 } 160 }; 161 } 162 verifyingGuard.setGuardedContainer(container); 163 verifyingGuard.observe(getComponentImplementation()); 164 } 165 166 public String getDescriptor() { 167 return "MethodInjector-"; 168 } 169 170 }