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 *****************************************************************************/
009package org.picocontainer.injectors;
010
011import org.picocontainer.*;
012import org.picocontainer.lifecycle.NullLifecycleStrategy;
013import org.picocontainer.monitors.NullComponentMonitor;
014
015import java.lang.reflect.InvocationTargetException;
016import java.lang.reflect.Method;
017import java.security.AccessController;
018import java.security.PrivilegedAction;
019import java.util.Properties;
020
021/**
022 * A Reinjector allows methods on pre-instantiated classes to be invoked,
023 * with appropriately scoped parameters.
024 */
025public 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}