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    package org.picocontainer.injectors;
010    
011    import org.picocontainer.*;
012    import org.picocontainer.lifecycle.NullLifecycleStrategy;
013    import org.picocontainer.monitors.NullComponentMonitor;
014    
015    import java.lang.reflect.InvocationTargetException;
016    import java.lang.reflect.Method;
017    import java.security.AccessController;
018    import java.security.PrivilegedAction;
019    import java.util.Properties;
020    
021    /**
022     * A Reinjector allows methods on pre-instantiated classes to be invoked,
023     * with appropriately scoped parameters.
024     */
025    public class Reinjector {
026        
027        private final PicoContainer parent;
028        private final ComponentMonitor monitor;
029        private static NullLifecycleStrategy NO_LIFECYCLE = new NullLifecycleStrategy();
030        private static Properties NO_PROPERTIES = new Properties();
031    
032        /**
033         * Make a reinjector with a parent container from which to pull components to be reinjected to.
034         * With this constructor, a NullComponentMonitor is used.
035         * @param parentContainer the parent container
036         */
037        public Reinjector(PicoContainer parentContainer) {
038            this(parentContainer, parentContainer instanceof ComponentMonitorStrategy
039                    ? ((ComponentMonitorStrategy) parentContainer).currentMonitor()
040                    : new NullComponentMonitor());
041        }
042    
043        /**
044         * Make a reinjector with a parent container from which to pull components to be reinjected to
045         * @param parentContainer the parent container
046         * @param monitor the monitor to use for 'instantiating' events
047         */
048        public Reinjector(PicoContainer parentContainer, ComponentMonitor monitor) {
049            this.parent = parentContainer;
050            this.monitor = monitor;
051        }
052    
053        /**
054         * Reinjecting into a method.
055         * @param key the component-key from the parent set of components to inject into
056         * @param reinjectionMethod the reflection method to use for injection.
057         * @return the result of the reinjection-method invocation.
058         */
059        public Object reinject(Class<?> key, Method reinjectionMethod) {
060            return reinject(key, key, parent.getComponent(key), NO_PROPERTIES, new MethodInjection(reinjectionMethod));
061        }
062    
063        /**
064         * Reinjecting into a method.
065         * @param key the component-key from the parent set of components to inject into
066         * @param reinjectionMethodEnum the enum for the reflection method to use for injection.
067         * @return the result of the reinjection-method invocation.
068         */
069        public Object reinject(Class<?> key, Enum reinjectionMethodEnum) {
070            return reinject(key, key, parent.getComponent(key), NO_PROPERTIES, new MethodInjection(toMethod(reinjectionMethodEnum)));
071        }
072    
073        private Method toMethod(final Enum reinjectionMethodEnum) {
074            Object methodOrException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
075                public Object run() {
076                    try {
077                        return reinjectionMethodEnum.getClass().getMethod("toMethod").invoke(reinjectionMethodEnum);
078                    } catch (IllegalAccessException e) {
079                        return new PicoCompositionException("Illegal access to " + reinjectionMethodEnum.name());
080                    } catch (InvocationTargetException e) {
081                        return new PicoCompositionException("Invocation Target Exception " + reinjectionMethodEnum.name(), e.getCause());
082                    } catch (NoSuchMethodException e) {
083                        return new PicoCompositionException("Expected generated method toMethod() on enum");
084                    }
085                }
086            });
087            if (methodOrException instanceof Method) {
088                return (Method) methodOrException;
089            } else {
090                throw (PicoCompositionException) methodOrException;
091            }
092        }
093    
094        /**
095         * Reinjecting into a method.
096         * @param key the component-key from the parent set of components to inject into (key and impl are the same)
097         * @param reinjectionFactory the InjectionFactory to use for reinjection.
098         * @return the result of the reinjection-method invocation.
099         */
100        public Object reinject(Class<?> key, InjectionFactory reinjectionFactory) {
101            Object o = reinject(key, key, parent.getComponent(key), NO_PROPERTIES, reinjectionFactory);
102            return o;
103        }
104    
105        /**
106         * Reinjecting into a method.
107         * @param key the component-key from the parent set of components to inject into
108         * @param impl the implementation of the component that is going to result.
109         * @param reinjectionFactory the InjectionFactory to use for reinjection.
110         * @return
111         */
112        public Object reinject(Class<?> key, Class<?> impl, InjectionFactory reinjectionFactory) {
113            return reinject(key, impl, parent.getComponent(key), NO_PROPERTIES, reinjectionFactory);
114        }
115    
116        /**
117         * Reinjecting into a method.
118         * @param key the component-key from the parent set of components to inject into
119         * @param implementation the implementation of the component that is going to result.
120         * @param instance the object that has the provider method to be invoked
121         * @param reinjectionFactory the InjectionFactory to use for reinjection.
122         * @return the result of the reinjection-method invocation.
123         */
124        public Object reinject(Class<?> key, Class implementation, Object instance, InjectionFactory reinjectionFactory) {
125            return reinject(key, implementation, instance, NO_PROPERTIES, reinjectionFactory);
126        }
127    
128        /**
129         * Reinjecting into a method.
130         * @param key the component-key from the parent set of components to inject into
131         * @param implementation the implementation of the component that is going to result.
132         * @param instance the object that has the provider method to be invoked
133         * @param properties for reinjection
134         * @param reinjectionFactory the InjectionFactory to use for reinjection.
135         * @return the result of the reinjection-method invocation.
136         */
137        public Object reinject(Class<?> key, Class implementation, Object instance, Properties properties,
138                               InjectionFactory reinjectionFactory) {
139            Reinjection reinjection = new Reinjection(reinjectionFactory, parent);
140            org.picocontainer.Injector injector = (org.picocontainer.Injector) reinjection.createComponentAdapter(
141                    monitor, NO_LIFECYCLE, properties, key, implementation, null);
142            return injector.decorateComponentInstance(parent, null, instance);
143        }
144    
145    }