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é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 }