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 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 /**
054 * Gets the <code>Action</code> specified by the mapping type from a
055 * PicoContainer. The action will be instantiated if necessary, and its
056 * dependencies will be injected. The action will be instantiated via a
057 * special PicoContainer that just contains actions. If this container
058 * already exists in the request attribute, this method will use it. If no
059 * such container exists, this method will create a new Pico container and
060 * place it in the request. The parent container will either be the request
061 * container, or if that container can not be found the session container,
062 * or if that container can not be found, the application container. If no
063 * parent container can be found, a <code>PicoCompositionException</code>
064 * will be thrown. The action path specified in the mapping is used as the
065 * component key for the action.
066 *
067 * @param request the Http servlet request.
068 * @param mapping the Struts mapping object, whose type property tells us
069 * what Action class is required.
070 * @param servlet the Struts <code>ActionServlet</code>.
071 * @return the <code>Action</code> instance.
072 * @throws PicoCompositionException if the mapping type does not specify a
073 * valid action.
074 * @throws PicoCompositionException if no request, session, or application
075 * scoped Pico container can be found.
076 */
077 public Action getAction(HttpServletRequest request, ActionMapping mapping, ActionServlet servlet)
078 throws PicoCompositionException {
079
080 MutablePicoContainer actionsContainer = currentRequestContainer.get();
081 Object actionKey = mapping.getPath();
082 Class<?> actionType = getActionClass(mapping.getType());
083
084 Action action = (Action) actionsContainer.getComponent(actionKey);
085 if (action == null) {
086 actionsContainer.addComponent(actionKey, actionType);
087 action = (Action) actionsContainer.getComponent(actionKey);
088 }
089
090 action.setServlet(servlet);
091 return action;
092 }
093
094 public Class<?> getActionClass(String className) throws PicoCompositionException {
095 try {
096 return loadClass(className);
097 } catch (ClassNotFoundException e) {
098 throw new PicoCompositionException("Action class '" + className + "' not found", e);
099 }
100 }
101
102 protected Class<?> loadClass(String className) throws ClassNotFoundException {
103 if (classCache.containsKey(className)) {
104 return (Class<?>) classCache.get(className);
105 } else {
106 Class<?> result = Thread.currentThread().getContextClassLoader().loadClass(className);
107 classCache.put(className, result);
108 return result;
109 }
110 }
111
112 }