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        @SuppressWarnings("serial")
035        public static class ServletFilter extends PicoServletContainerFilter {
036            private static ThreadLocal<MutablePicoContainer> currentRequestContainer = new ThreadLocal<MutablePicoContainer>();
037            private static ThreadLocal<MutablePicoContainer> currentSessionContainer = new ThreadLocal<MutablePicoContainer>();
038            private static ThreadLocal<MutablePicoContainer> currentAppContainer = new ThreadLocal<MutablePicoContainer>();
039    
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            protected static MutablePicoContainer getRequestContainerForThread() {
051                return currentRequestContainer.get();
052            }
053            protected static MutablePicoContainer getSessionContainerForThread() {
054                return currentSessionContainer.get();
055            }
056            protected static MutablePicoContainer getApplicationContainerForThread() {
057                return currentAppContainer.get();
058            }
059        }
060    
061        private final Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
062    
063        public boolean isNoArgConstructorRequired() {
064            return false;
065        }
066    
067        @SuppressWarnings("unchecked")
068        public Object buildBean(Class clazz, Map extraContext) throws Exception {
069            return buildBean(clazz);
070        }
071    
072        @SuppressWarnings("unchecked")
073        public Object buildBean(String className, Map extraContext) throws Exception {
074            return buildBean(className);
075        }
076    
077    
078        @SuppressWarnings("unchecked")
079        public Class getClassInstance(String className) {
080            return getActionClass(className);
081        }
082    
083        /**
084         * Instantiates an action using the PicoContainer found in the request scope.
085         * if action or bean is not registered explicitely, new instance will be provided
086         * on every invocation.
087         * 
088         * @see com.opensymphony.xwork.ObjectFactory#buildBean(java.lang.Class)
089         */
090        public Object buildBean(Class<?> actionClass) throws Exception {
091            PicoContainer actionsContainer = ServletFilter.getRequestContainerForThread();
092            Object action = actionsContainer.getComponent(actionClass);
093    
094            if (action == null) {
095                // The action wasn't registered. Attempt to instantiate it.
096                    // use child container to prevent weirdest errors
097                    MutablePicoContainer child = new DefaultPicoContainer(actionsContainer);
098                    
099                child.addComponent(actionClass);
100                action = child.getComponent(actionClass);
101            }
102            return action;
103        }
104    
105        /**
106         * As {@link ObjectFactory#buildBean(java.lang.String)}does not delegate to
107         * {@link ObjectFactory#buildBean(java.lang.Class)} but directly calls
108         * <code>clazz.newInstance()</code>, overwrite this method to call
109         * <code>buildBean()</code>
110         *
111         * @see com.opensymphony.xwork.ObjectFactory#buildBean(java.lang.String)
112         */
113        public Object buildBean(String className) throws Exception {
114            return buildBean(getActionClass(className));
115        }
116    
117        public Class<?> getActionClass(String className) throws PicoCompositionException {
118            try {
119                return loadClass(className);
120            } catch (ClassNotFoundException e) {
121                throw new PicoCompositionException("Action class '" + className + "' not found", e);
122            }
123        }
124    
125        protected Class<?> loadClass(String className) throws ClassNotFoundException {
126            if (classCache.containsKey(className)) {
127                return (Class<?>) classCache.get(className);
128            } else {
129                Class<?> result = Thread.currentThread().getContextClassLoader().loadClass(className);
130                classCache.put(className, result);
131                return result;
132            }
133        }  
134    
135    }