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.struts;
009
010 import java.util.HashMap;
011 import java.util.Map;
012
013 import javax.servlet.http.HttpServletRequest;
014
015 import org.apache.struts.action.Action;
016 import org.apache.struts.action.ActionMapping;
017 import org.apache.struts.action.ActionServlet;
018 import org.picocontainer.MutablePicoContainer;
019 import org.picocontainer.PicoCompositionException;
020 import org.picocontainer.web.PicoServletContainerFilter;
021
022 /**
023 * Uses PicoContainer to produce Actions and inject dependencies into them. If
024 * you have your own <code>RequestProcessor</code> implementation, you can use
025 * an <code>ActionFactory</code> in your
026 * <code>RequestProcessor.processActionCreate</code> method to Picofy your
027 * Actions.
028 *
029 * @author Stephen Molitor
030 * @author Mauro Talevi
031 */
032 public final class PicoActionFactory {
033
034 @SuppressWarnings("serial")
035 public static class ServletFilter extends PicoServletContainerFilter {
036
037 private static ThreadLocal<MutablePicoContainer> currentRequestContainer = new ThreadLocal<MutablePicoContainer>();
038 private static ThreadLocal<MutablePicoContainer> currentSessionContainer = new ThreadLocal<MutablePicoContainer>();
039 private static ThreadLocal<MutablePicoContainer> currentAppContainer = new ThreadLocal<MutablePicoContainer>();
040
041 protected void setAppContainer(MutablePicoContainer container) {
042 currentAppContainer.set(container);
043 }
044
045 protected void setRequestContainer(MutablePicoContainer container) {
046 currentRequestContainer.set(container);
047 }
048
049 protected void setSessionContainer(MutablePicoContainer container) {
050 currentSessionContainer.set(container);
051 }
052
053 private static MutablePicoContainer getRequestContainerForThread() {
054 return currentRequestContainer.get();
055 }
056 }
057
058 private final Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
059
060 /**
061 * Gets the <code>Action</code> specified by the mapping type from a
062 * PicoContainer. The action will be instantiated if necessary, and its
063 * dependencies will be injected. The action will be instantiated via a
064 * special PicoContainer that just contains actions. If this container
065 * already exists in the request attribute, this method will use it. If no
066 * such container exists, this method will create a new Pico container and
067 * place it in the request. The parent container will either be the request
068 * container, or if that container can not be found the session container,
069 * or if that container can not be found, the application container. If no
070 * parent container can be found, a <code>PicoCompositionException</code>
071 * will be thrown. The action path specified in the mapping is used as the
072 * component key for the action.
073 *
074 * @param request the Http servlet request.
075 * @param mapping the Struts mapping object, whose type property tells us
076 * what Action class is required.
077 * @param servlet the Struts <code>ActionServlet</code>.
078 * @return the <code>Action</code> instance.
079 * @throws PicoCompositionException if the mapping type does not specify a
080 * valid action.
081 * @throws PicoCompositionException if no request, session, or application
082 * scoped Pico container can be found.
083 */
084 public Action getAction(HttpServletRequest request, ActionMapping mapping, ActionServlet servlet)
085 throws PicoCompositionException {
086
087 MutablePicoContainer actionsContainer = ServletFilter.getRequestContainerForThread();
088 Object actionKey = mapping.getPath();
089 Class<?> actionType = getActionClass(mapping.getType());
090
091 Action action = (Action) actionsContainer.getComponent(actionKey);
092 if (action == null) {
093 actionsContainer.addComponent(actionKey, actionType);
094 action = (Action) actionsContainer.getComponent(actionKey);
095 }
096
097 action.setServlet(servlet);
098 return action;
099 }
100
101 public Class<?> getActionClass(String className) throws PicoCompositionException {
102 try {
103 return loadClass(className);
104 } catch (ClassNotFoundException e) {
105 throw new PicoCompositionException("Action class '" + className + "' not found", e);
106 }
107 }
108
109 protected Class<?> loadClass(String className) throws ClassNotFoundException {
110 if (classCache.containsKey(className)) {
111 return (Class<?>) classCache.get(className);
112 } else {
113 Class<?> result = Thread.currentThread().getContextClassLoader().loadClass(className);
114 classCache.put(className, result);
115 return result;
116 }
117 }
118
119 }