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 * Original code by * 009 *****************************************************************************/ 010 package org.picocontainer.defaults; 011 012 import org.picocontainer.ComponentMonitor; 013 import org.picocontainer.Parameter; 014 import org.picocontainer.PicoContainer; 015 import org.picocontainer.PicoIntrospectionException; 016 import org.picocontainer.PicoVisitor; 017 018 import java.lang.reflect.Constructor; 019 import java.lang.reflect.InvocationTargetException; 020 import java.lang.reflect.Modifier; 021 022 /** 023 * This ComponentAdapter will instantiate a new object for each call to 024 * {@link org.picocontainer.ComponentAdapter#getComponentInstance(PicoContainer)}. 025 * That means that when used with a PicoContainer, getComponentInstance will 026 * return a new object each time. 027 * 028 * @author Aslak Hellesøy 029 * @author Paul Hammant 030 * @author Jörg Schaible 031 * @author Mauro Talevi 032 * @version $Revision: 2788 $ 033 * @since 1.0 034 */ 035 public abstract class InstantiatingComponentAdapter extends AbstractComponentAdapter 036 implements LifecycleStrategy { 037 /** The cycle guard for the verification. */ 038 protected transient Guard verifyingGuard; 039 /** The parameters to use for initialization. */ 040 protected transient Parameter[] parameters; 041 /** Flag indicating instanciation of non-public classes. */ 042 protected boolean allowNonPublicClasses; 043 044 /** The cycle guard for the verification. */ 045 protected static abstract class Guard extends ThreadLocalCyclicDependencyGuard { 046 protected PicoContainer guardedContainer; 047 protected void setArguments(PicoContainer container) { 048 this.guardedContainer = container; 049 } 050 } 051 052 /** The strategy used to control the lifecycle */ 053 protected LifecycleStrategy lifecycleStrategy; 054 055 /** 056 * Constructs a new ComponentAdapter for the given key and implementation. 057 * @param componentKey the search key for this implementation 058 * @param componentImplementation the concrete implementation 059 * @param parameters the parameters to use for the initialization 060 * @param allowNonPublicClasses flag to allow instantiation of non-public classes 061 * @param monitor the component monitor used by this ComponentAdapter 062 * @param lifecycleStrategy the lifecycle strategy used by this ComponentAdapter 063 * @throws AssignabilityRegistrationException if the key is a type and the implementation cannot be assigned to 064 * @throws NotConcreteRegistrationException if the implementation is not a concrete class 065 * @throws NullPointerException if one of the parameters is <code>null</code> 066 */ 067 protected InstantiatingComponentAdapter(Object componentKey, Class componentImplementation, Parameter[] parameters, boolean allowNonPublicClasses, 068 ComponentMonitor monitor, LifecycleStrategy lifecycleStrategy) { 069 super(componentKey, componentImplementation, monitor); 070 checkConcrete(); 071 if (parameters != null) { 072 for (int i = 0; i < parameters.length; i++) { 073 if(parameters[i] == null) { 074 throw new NullPointerException("Parameter " + i + " is null"); 075 } 076 } 077 } 078 this.parameters = parameters; 079 this.allowNonPublicClasses = allowNonPublicClasses; 080 this.lifecycleStrategy = lifecycleStrategy; 081 } 082 083 /** 084 * Constructs a new ComponentAdapter for the given key and implementation. 085 * @param componentKey the search key for this implementation 086 * @param componentImplementation the concrete implementation 087 * @param parameters the parameters to use for the initialization 088 * @param allowNonPublicClasses flag to allow instantiation of non-public classes 089 * @param monitor the component monitor used by this ComponentAdapter 090 * @throws AssignabilityRegistrationException if the key is a type and the implementation cannot be assigned to 091 * @throws NotConcreteRegistrationException if the implementation is not a concrete class 092 * @throws NullPointerException if one of the parameters is <code>null</code> 093 */ 094 protected InstantiatingComponentAdapter(Object componentKey, Class componentImplementation, 095 Parameter[] parameters, boolean allowNonPublicClasses, 096 ComponentMonitor monitor) { 097 this(componentKey, componentImplementation, parameters, allowNonPublicClasses, monitor, new DefaultLifecycleStrategy(monitor)); 098 } 099 100 /** 101 * Constructs a new ComponentAdapter for the given key and implementation. 102 * @param componentKey the search key for this implementation 103 * @param componentImplementation the concrete implementation 104 * @param parameters the parameters to use for the initialization 105 * @param allowNonPublicClasses flag to allow instantiation of non-public classes. 106 * @throws AssignabilityRegistrationException if the key is a type and the implementation cannot be assigned to. 107 * @throws NotConcreteRegistrationException if the implementation is not a concrete class. 108 * @throws NullPointerException if one of the parameters is <code>null</code> 109 */ 110 protected InstantiatingComponentAdapter(Object componentKey, Class componentImplementation, Parameter[] parameters, boolean allowNonPublicClasses) { 111 this(componentKey, componentImplementation, parameters, allowNonPublicClasses, new DelegatingComponentMonitor()); 112 } 113 114 private void checkConcrete() throws NotConcreteRegistrationException { 115 // Assert that the component class is concrete. 116 boolean isAbstract = (getComponentImplementation().getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT; 117 if (getComponentImplementation().isInterface() || isAbstract) { 118 throw new NotConcreteRegistrationException(getComponentImplementation()); 119 } 120 } 121 122 /** 123 * Create default parameters for the given types. 124 * 125 * @param parameters the parameter types 126 * @return the array with the default parameters. 127 */ 128 protected Parameter[] createDefaultParameters(Class[] parameters) { 129 Parameter[] componentParameters = new Parameter[parameters.length]; 130 for (int i = 0; i < parameters.length; i++) { 131 componentParameters[i] = ComponentParameter.DEFAULT; 132 } 133 return componentParameters; 134 } 135 136 public void verify(final PicoContainer container) throws PicoIntrospectionException { 137 if (verifyingGuard == null) { 138 verifyingGuard = new Guard() { 139 public Object run() { 140 final Constructor constructor = getGreediestSatisfiableConstructor(guardedContainer); 141 final Class[] parameterTypes = constructor.getParameterTypes(); 142 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes); 143 for (int i = 0; i < currentParameters.length; i++) { 144 currentParameters[i].verify(container, InstantiatingComponentAdapter.this, parameterTypes[i]); 145 } 146 return null; 147 } 148 }; 149 } 150 verifyingGuard.setArguments(container); 151 verifyingGuard.observe(getComponentImplementation()); 152 } 153 154 public void accept(PicoVisitor visitor) { 155 super.accept(visitor); 156 if (parameters != null) { 157 for (int i = 0; i < parameters.length; i++) { 158 parameters[i].accept(visitor); 159 } 160 } 161 } 162 163 public void start(Object component) { 164 lifecycleStrategy.start(component); 165 } 166 167 public void stop(Object component) { 168 lifecycleStrategy.stop(component); 169 } 170 171 public void dispose(Object component) { 172 lifecycleStrategy.dispose(component); 173 } 174 175 public boolean hasLifecycle(Class type) { 176 return lifecycleStrategy.hasLifecycle(type); 177 } 178 179 /** 180 * Instantiate an object with given parameters and respect the accessible flag. 181 * 182 * @param constructor the constructor to use 183 * @param parameters the parameters for the constructor 184 * @return the new object. 185 * @throws InstantiationException 186 * @throws IllegalAccessException 187 * @throws InvocationTargetException 188 */ 189 protected Object newInstance(Constructor constructor, Object[] parameters) throws InstantiationException, IllegalAccessException, InvocationTargetException { 190 if (allowNonPublicClasses) { 191 constructor.setAccessible(true); 192 } 193 return constructor.newInstance(parameters); 194 } 195 196 /** 197 * Find and return the greediest satisfiable constructor. 198 * 199 * @param container the PicoContainer to resolve dependencies. 200 * @return the found constructor. 201 * @throws PicoIntrospectionException 202 * @throws UnsatisfiableDependenciesException 203 * @throws AmbiguousComponentResolutionException 204 * @throws AssignabilityRegistrationException 205 * @throws NotConcreteRegistrationException 206 */ 207 protected abstract Constructor getGreediestSatisfiableConstructor(PicoContainer container) throws PicoIntrospectionException, UnsatisfiableDependenciesException, AmbiguousComponentResolutionException, AssignabilityRegistrationException, NotConcreteRegistrationException; 208 }