Save This Page
Home » xwork-2.1.5 » com.opensymphony » xwork2 » [javadoc | source]
    1   /*
    2   * Copyright (c) 2002-2006 by OpenSymphony
    3   * All rights reserved.
    4   */
    5   package com.opensymphony.xwork2;
    6   
    7   import com.opensymphony.xwork2.inject.Inject;
    8   import com.opensymphony.xwork2.util.TextParseUtil;
    9   import com.opensymphony.xwork2.util.ValueStack;
   10   import com.opensymphony.xwork2.util.logging.Logger;
   11   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   12   
   13   import java.util;
   14   
   15   
   16   /**
   17   * <!-- START SNIPPET: description -->
   18   *
   19   * This result invokes an entire other action, complete with it's own interceptor stack and result.
   20   *
   21   * <!-- END SNIPPET: description -->
   22   *
   23   * <b>This result type takes the following parameters:</b>
   24   *
   25   * <!-- START SNIPPET: params -->
   26   *
   27   * <ul>
   28   *
   29   * <li><b>actionName (default)</b> - the name of the action that will be chained to</li>
   30   *
   31   * <li><b>namespace</b> - used to determine which namespace the Action is in that we're chaining. If namespace is null,
   32   * this defaults to the current namespace</li>
   33   *
   34   * <li><b>method</b> - used to specify another method on target action to be invoked.
   35   * If null, this defaults to execute method</li>
   36   *
   37   * <li><b>skipActions</b> - (optional) the list of comma separated action names for the
   38   * actions that could be chained to</li>
   39   *
   40   * </ul>
   41   *
   42   * <!-- END SNIPPET: params -->
   43   *
   44   * <b>Example:</b>
   45   *
   46   * <pre><!-- START SNIPPET: example -->
   47   * &lt;package name="public" extends="struts-default"&gt;
   48   *     &lt;!-- Chain creatAccount to login, using the default parameter --&gt;
   49   *     &lt;action name="createAccount" class="..."&gt;
   50   *         &lt;result type="chain"&gt;login&lt;/result&gt;
   51   *     &lt;/action&gt;
   52   *
   53   *     &lt;action name="login" class="..."&gt;
   54   *         &lt;!-- Chain to another namespace --&gt;
   55   *         &lt;result type="chain"&gt;
   56   *             &lt;param name="actionName"&gt;dashboard&lt;/param&gt;
   57   *             &lt;param name="namespace"&gt;/secure&lt;/param&gt;
   58   *         &lt;/result&gt;
   59   *     &lt;/action&gt;
   60   * &lt;/package&gt;
   61   *
   62   * &lt;package name="secure" extends="struts-default" namespace="/secure"&gt;
   63   *     &lt;action name="dashboard" class="..."&gt;
   64   *         &lt;result&gt;dashboard.jsp&lt;/result&gt;
   65   *     &lt;/action&gt;
   66   * &lt;/package&gt;
   67   * <!-- END SNIPPET: example --></pre>
   68   *
   69   * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
   70   */
   71   public class ActionChainResult implements Result {
   72   
   73       private static final Logger LOG = LoggerFactory.getLogger(ActionChainResult.class);
   74   
   75       /**
   76        * The result parameter name to set the name of the action to chain to.
   77        */
   78       public static final String DEFAULT_PARAM = "actionName";
   79   
   80       /**
   81        * The action context key to save the chain history.
   82        */
   83       private static final String CHAIN_HISTORY = "CHAIN_HISTORY";
   84   
   85       /**
   86        * The result parameter name to set the name of the action to chain to.
   87        */
   88       public static final String SKIP_ACTIONS_PARAM = "skipActions";
   89   
   90   
   91       private ActionProxy proxy;
   92       private String actionName;
   93       
   94       private String namespace;
   95   
   96       private String methodName;
   97   
   98       /**
   99        * The list of actions to skip.
  100        */
  101       private String skipActions;
  102   
  103       private ActionProxyFactory actionProxyFactory;
  104   
  105       public ActionChainResult() {
  106           super();
  107       }
  108   
  109       public ActionChainResult(String namespace, String actionName, String methodName) {
  110           this.namespace = namespace;
  111           this.actionName = actionName;
  112           this.methodName = methodName;
  113       }
  114   
  115       public ActionChainResult(String namespace, String actionName, String methodName, String skipActions) {
  116           this.namespace = namespace;
  117           this.actionName = actionName;
  118           this.methodName = methodName;
  119           this.skipActions = skipActions;
  120       }
  121   
  122   
  123       /**
  124        * @param actionProxyFactory the actionProxyFactory to set
  125        */
  126       @Inject
  127       public void setActionProxyFactory(ActionProxyFactory actionProxyFactory) {
  128           this.actionProxyFactory = actionProxyFactory;
  129       }
  130   
  131       /**
  132        * Set the action name.
  133        *
  134        * @param actionName The action name.
  135        */
  136       public void setActionName(String actionName) {
  137           this.actionName = actionName;
  138       }
  139   
  140       /**
  141        * sets the namespace of the Action that we're chaining to.  if namespace
  142        * is null, this defaults to the current namespace.
  143        *
  144        * @param namespace the name of the namespace we're chaining to
  145        */
  146       public void setNamespace(String namespace) {
  147           this.namespace = namespace;
  148       }
  149   
  150       /**
  151        * Set the list of actions to skip.
  152        * To test if an action should not throe an infinite recursion,
  153        * only the action name is used, not the namespace.
  154        *
  155        * @param actions The list of action name separated by a white space.
  156        */
  157       public void setSkipActions(String actions) {
  158           this.skipActions = actions;
  159       }
  160   
  161   
  162       public void setMethod(String method) {
  163           this.methodName = method;
  164       }
  165   
  166       public ActionProxy getProxy() {
  167           return proxy;
  168       }
  169   
  170       /**
  171        * Get the XWork chain history.
  172        * The stack is a list of <code>namespace/action!method</code> keys.
  173        */
  174       public static LinkedList<String> getChainHistory() {
  175           LinkedList<String> chainHistory = (LinkedList<String>) ActionContext.getContext().get(CHAIN_HISTORY);
  176           //  Add if not exists
  177           if (chainHistory == null) {
  178               chainHistory = new LinkedList<String>();
  179               ActionContext.getContext().put(CHAIN_HISTORY, chainHistory);
  180           }
  181   
  182           return chainHistory;
  183       }
  184   
  185       /**
  186        * @param invocation the DefaultActionInvocation calling the action call stack
  187        */
  188       public void execute(ActionInvocation invocation) throws Exception {
  189           // if the finalNamespace wasn't explicitly defined, assume the current one
  190           if (this.namespace == null) {
  191               this.namespace = invocation.getProxy().getNamespace();
  192           }
  193   
  194           ValueStack stack = ActionContext.getContext().getValueStack();
  195           String finalNamespace = TextParseUtil.translateVariables(namespace, stack);
  196           String finalActionName = TextParseUtil.translateVariables(actionName, stack);
  197           String finalMethodName = this.methodName != null
  198                   ? TextParseUtil.translateVariables(this.methodName, stack)
  199                   : null;
  200   
  201           if (isInChainHistory(finalNamespace, finalActionName, finalMethodName)) {
  202               addToHistory(finalNamespace, finalActionName, finalMethodName);
  203               throw new XWorkException("Infinite recursion detected: "
  204                       + ActionChainResult.getChainHistory().toString());
  205           }
  206   
  207           if (ActionChainResult.getChainHistory().isEmpty() && invocation != null && invocation.getProxy() != null) {
  208               addToHistory(finalNamespace, invocation.getProxy().getActionName(), invocation.getProxy().getMethod());
  209           }
  210           addToHistory(finalNamespace, finalActionName, finalMethodName);
  211   
  212           HashMap<String, Object> extraContext = new HashMap<String, Object>();
  213           extraContext.put(ActionContext.VALUE_STACK, ActionContext.getContext().getValueStack());
  214           extraContext.put(ActionContext.PARAMETERS, ActionContext.getContext().getParameters());
  215           extraContext.put(CHAIN_HISTORY, ActionChainResult.getChainHistory());
  216   
  217           if (LOG.isDebugEnabled()) {
  218               LOG.debug("Chaining to action " + finalActionName);
  219           }
  220   
  221           proxy = actionProxyFactory.createActionProxy(finalNamespace, finalActionName, finalMethodName, extraContext);
  222           proxy.execute();
  223       }
  224   
  225       @Override public boolean equals(Object o) {
  226           if (this == o) return true;
  227           if (o == null || getClass() != o.getClass()) return false;
  228   
  229           final ActionChainResult that = (ActionChainResult) o;
  230   
  231           if (actionName != null ? !actionName.equals(that.actionName) : that.actionName != null) return false;
  232           if (methodName != null ? !methodName.equals(that.methodName) : that.methodName != null) return false;
  233           if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
  234   
  235           return true;
  236       }
  237   
  238       @Override public int hashCode() {
  239           int result;
  240           result = (actionName != null ? actionName.hashCode() : 0);
  241           result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
  242           result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
  243           return result;
  244       }
  245   
  246       private boolean isInChainHistory(String namespace, String actionName, String methodName) {
  247           LinkedList<? extends String> chainHistory = ActionChainResult.getChainHistory();
  248   
  249           if (chainHistory == null) {
  250               return false;
  251           } else {
  252               //  Actions to skip
  253               Set<String> skipActionsList = new HashSet<String>();
  254               if (skipActions != null && skipActions.length() > 0) {
  255                   ValueStack stack = ActionContext.getContext().getValueStack();
  256                   String finalSkipActions = TextParseUtil.translateVariables(this.skipActions, stack);
  257                   skipActionsList.addAll(TextParseUtil.commaDelimitedStringToSet(finalSkipActions));
  258               }
  259               if (!skipActionsList.contains(actionName)) {
  260                   //  Get if key is in the chain history
  261                   return chainHistory.contains(makeKey(namespace, actionName, methodName));
  262               }
  263   
  264               return false;
  265           }
  266       }
  267   
  268       private void addToHistory(String namespace, String actionName, String methodName) {
  269           List<String> chainHistory = ActionChainResult.getChainHistory();
  270           chainHistory.add(makeKey(namespace, actionName, methodName));
  271       }
  272   
  273       private String makeKey(String namespace, String actionName, String methodName) {
  274           if (null == methodName) {
  275               return namespace + "/" + actionName;
  276           }
  277   
  278           return namespace + "/" + actionName + "!" + methodName;
  279       }
  280   }

Save This Page
Home » xwork-2.1.5 » com.opensymphony » xwork2 » [javadoc | source]