001    /*******************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved. 
003     * ---------------------------------------------------------------------------
004     * The software in this package is published under the terms of the BSD style
005     * license a copy of which has been included with this distribution in the
006     * LICENSE.txt file.
007     ******************************************************************************/
008    package org.picocontainer.web.webwork2;
009    
010    import java.util.HashMap;
011    import java.util.Map;
012    
013    import org.picocontainer.DefaultPicoContainer;
014    import org.picocontainer.MutablePicoContainer;
015    import org.picocontainer.PicoCompositionException;
016    import org.picocontainer.PicoContainer;
017    import org.picocontainer.web.PicoServletContainerFilter;
018    
019    import com.opensymphony.xwork.ObjectFactory;
020    
021    /**
022     * <p>
023     * XWork ObjectFactory which uses a PicoContainer to create component instances.
024     * </p>
025     * 
026     * @author Cyrille Le Clerc
027     * @author Jonas Engman
028     * @author Mauro Talevi
029     * @author Gr&eacute;gory Joseph
030     * @author Konstatin Pribluda 
031     */
032    public class PicoObjectFactory extends ObjectFactory {
033    
034        private static ThreadLocal<MutablePicoContainer> currentRequestContainer = new ThreadLocal<MutablePicoContainer>();
035        private static ThreadLocal<MutablePicoContainer> currentSessionContainer = new ThreadLocal<MutablePicoContainer>();
036        private static ThreadLocal<MutablePicoContainer> currentAppContainer = new ThreadLocal<MutablePicoContainer>();
037    
038        @SuppressWarnings("serial")
039        public static class ServletFilter extends PicoServletContainerFilter {
040            protected void setAppContainer(MutablePicoContainer container) {
041                currentAppContainer.set(container);
042            }
043            protected void setRequestContainer(MutablePicoContainer container) {
044                currentRequestContainer.set(container);
045            }
046            protected void setSessionContainer(MutablePicoContainer container) {
047                currentSessionContainer.set(container);
048            }
049        }
050    
051        private final Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
052    
053        public boolean isNoArgConstructorRequired() {
054            return false;
055        }
056    
057        @SuppressWarnings("unchecked")
058        public Object buildBean(Class clazz, Map extraContext) throws Exception {
059            return buildBean(clazz);
060        }
061    
062        @SuppressWarnings("unchecked")
063        public Object buildBean(String className, Map extraContext) throws Exception {
064            return buildBean(className);
065        }
066    
067    
068        @SuppressWarnings("unchecked")
069        public Class getClassInstance(String className) {
070            return getActionClass(className);
071        }
072    
073        /**
074         * Instantiates an action using the PicoContainer found in the request scope.
075         * if action or bean is not registered explicitely, new instance will be provided
076         * on every invocation.
077         * 
078         * @see com.opensymphony.xwork.ObjectFactory#buildBean(java.lang.Class)
079         */
080        public Object buildBean(Class<?> actionClass) throws Exception {
081            PicoContainer actionsContainer = currentRequestContainer.get();
082            Object action = actionsContainer.getComponent(actionClass);
083    
084            if (action == null) {
085                // The action wasn't registered. Attempt to instantiate it.
086                    // use child container to prevent weirdest errors
087                    MutablePicoContainer child = new DefaultPicoContainer(actionsContainer);
088                    
089                child.addComponent(actionClass);
090                action = child.getComponent(actionClass);
091            }
092            return action;
093        }
094    
095        /**
096         * As {@link ObjectFactory#buildBean(java.lang.String)}does not delegate to
097         * {@link ObjectFactory#buildBean(java.lang.Class)} but directly calls
098         * <code>clazz.newInstance()</code>, overwrite this method to call
099         * <code>buildBean()</code>
100         *
101         * @see com.opensymphony.xwork.ObjectFactory#buildBean(java.lang.String)
102         */
103        public Object buildBean(String className) throws Exception {
104            return buildBean(getActionClass(className));
105        }
106    
107        public Class<?> getActionClass(String className) throws PicoCompositionException {
108            try {
109                return loadClass(className);
110            } catch (ClassNotFoundException e) {
111                throw new PicoCompositionException("Action class '" + className + "' not found", e);
112            }
113        }
114    
115        protected Class<?> loadClass(String className) throws ClassNotFoundException {
116            if (classCache.containsKey(className)) {
117                return (Class<?>) classCache.get(className);
118            } else {
119                Class<?> result = Thread.currentThread().getContextClassLoader().loadClass(className);
120                classCache.put(className, result);
121                return result;
122            }
123        }  
124    
125    }