Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WW-5459 Deprecate and repackage ActionChainResult #1116

Merged
merged 1 commit into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 6 additions & 248 deletions core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,263 +18,21 @@
*/
package com.opensymphony.xwork2;

import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsException;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
* <!-- START SNIPPET: description -->
*
* This result invokes an entire other action, complete with it's own interceptor stack and result.
*
* <!-- END SNIPPET: description -->
*
* <b>This result type takes the following parameters:</b>
*
* <!-- START SNIPPET: params -->
*
* <ul>
*
* <li><b>actionName (default)</b> - the name of the action that will be chained to</li>
*
* <li><b>namespace</b> - used to determine which namespace the Action is in that we're chaining. If namespace is null,
* this defaults to the current namespace</li>
*
* <li><b>method</b> - used to specify another method on target action to be invoked.
* If null, this defaults to execute method</li>
*
* <li><b>skipActions</b> - (optional) the list of comma separated action names for the
* actions that could be chained to</li>
*
* </ul>
*
* <!-- END SNIPPET: params -->
*
* <b>Example:</b>
*
* <pre><!-- START SNIPPET: example -->
* &lt;package name="public" extends="struts-default"&gt;
* &lt;!-- Chain creatAccount to login, using the default parameter --&gt;
* &lt;action name="createAccount" class="..."&gt;
* &lt;result type="chain"&gt;login&lt;/result&gt;
* &lt;/action&gt;
*
* &lt;action name="login" class="..."&gt;
* &lt;!-- Chain to another namespace --&gt;
* &lt;result type="chain"&gt;
* &lt;param name="actionName"&gt;dashboard&lt;/param&gt;
* &lt;param name="namespace"&gt;/secure&lt;/param&gt;
* &lt;/result&gt;
* &lt;/action&gt;
* &lt;/package&gt;
*
* &lt;package name="secure" extends="struts-default" namespace="/secure"&gt;
* &lt;action name="dashboard" class="..."&gt;
* &lt;result&gt;dashboard.jsp&lt;/result&gt;
* &lt;/action&gt;
* &lt;/package&gt;
* <!-- END SNIPPET: example --></pre>
*
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
*/
public class ActionChainResult implements Result {

private static final Logger LOG = LogManager.getLogger(ActionChainResult.class);

/**
* The result parameter name to set the name of the action to chain to.
*/
public static final String DEFAULT_PARAM = "actionName";

/**
* The action context key to save the chain history.
*/
private static final String CHAIN_HISTORY = "CHAIN_HISTORY";

private ActionProxy proxy;
private String actionName;

private String namespace;

private String methodName;

/**
* The list of actions to skip.
*/
private String skipActions;

private ActionProxyFactory actionProxyFactory;
* @deprecated since 6.7.0, use {@link org.apache.struts2.result.ActionChainResult} instead.
*/
@Deprecated
public class ActionChainResult extends org.apache.struts2.result.ActionChainResult {

public ActionChainResult() {
super();
}

public ActionChainResult(String namespace, String actionName, String methodName) {
this.namespace = namespace;
this.actionName = actionName;
this.methodName = methodName;
super(namespace, actionName, methodName);
}

public ActionChainResult(String namespace, String actionName, String methodName, String skipActions) {
this.namespace = namespace;
this.actionName = actionName;
this.methodName = methodName;
this.skipActions = skipActions;
}

/**
* @param actionProxyFactory the actionProxyFactory to set
*/
@Inject
public void setActionProxyFactory(ActionProxyFactory actionProxyFactory) {
this.actionProxyFactory = actionProxyFactory;
}

/**
* Set the action name.
*
* @param actionName The action name.
*/
public void setActionName(String actionName) {
this.actionName = actionName;
}

/**
* sets the namespace of the Action that we're chaining to. if namespace
* is null, this defaults to the current namespace.
*
* @param namespace the name of the namespace we're chaining to
*/
public void setNamespace(String namespace) {
this.namespace = namespace;
}

/**
* Set the list of actions to skip.
* To test if an action should not throe an infinite recursion,
* only the action name is used, not the namespace.
*
* @param actions The list of action name separated by a white space.
*/
public void setSkipActions(String actions) {
this.skipActions = actions;
}

public void setMethod(String method) {
this.methodName = method;
}

public ActionProxy getProxy() {
return proxy;
}

/**
* Get the XWork chain history.
* The stack is a list of <code>namespace/action!method</code> keys.
*
* @return the chain history as string list
*/
public static LinkedList<String> getChainHistory() {
LinkedList<String> chainHistory = (LinkedList<String>) ActionContext.getContext().get(CHAIN_HISTORY);
// Add if not exists
if (chainHistory == null) {
chainHistory = new LinkedList<>();
ActionContext.getContext().put(CHAIN_HISTORY, chainHistory);
}

return chainHistory;
}

/**
* @param invocation the DefaultActionInvocation calling the action call stack
*/
public void execute(ActionInvocation invocation) throws Exception {
if (invocation == null) {
throw new IllegalArgumentException("Invocation cannot be null!");
}

String finalNamespace = namespace != null ? translateVariables(namespace) : invocation.getProxy()
.getNamespace();
String finalActionName = translateVariables(actionName);
String finalMethodName = methodName != null ? translateVariables(methodName) : null;

if (isInChainHistory(finalNamespace, finalActionName, finalMethodName)) {
addToHistory(finalNamespace, finalActionName, finalMethodName);
throw new StrutsException("Infinite recursion detected: " + ActionChainResult.getChainHistory());
}

if (ActionChainResult.getChainHistory().isEmpty() && invocation.getProxy() != null) {
addToHistory(finalNamespace, invocation.getProxy().getActionName(), invocation.getProxy().getMethod());
}
addToHistory(finalNamespace, finalActionName, finalMethodName);

Map<String, Object> extraContext = ActionContext.of()
.withValueStack(invocation.getInvocationContext().getValueStack())
.withParameters(invocation.getInvocationContext().getParameters())
.with(CHAIN_HISTORY, ActionChainResult.getChainHistory())
.getContextMap();

LOG.debug("Chaining to action {}", finalActionName);

proxy = actionProxyFactory.createActionProxy(finalNamespace, finalActionName, finalMethodName, extraContext);
proxy.execute();
}

protected String translateVariables(String text) {
return TextParseUtil.translateVariables(text, ActionContext.getContext().getValueStack());
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ActionChainResult that = (ActionChainResult) o;
return Objects.equals(actionName, that.actionName) && Objects.equals(methodName,
that.methodName) && Objects.equals(namespace, that.namespace);
}

@Override
public int hashCode() {
int result;
result = (actionName != null ? actionName.hashCode() : 0);
result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
return result;
}

private boolean isInChainHistory(String namespace, String actionName, String methodName) {
LinkedList<? extends String> chainHistory = ActionChainResult.getChainHistory();
Set<String> skipActionsList = new HashSet<>();
if (skipActions != null && skipActions.length() > 0) {
String finalSkipActions = translateVariables(skipActions);
skipActionsList.addAll(TextParseUtil.commaDelimitedStringToSet(finalSkipActions));
}
if (!skipActionsList.contains(actionName)) {
return chainHistory.contains(makeKey(namespace, actionName, methodName));
}
return false;
}

private void addToHistory(String namespace, String actionName, String methodName) {
List<String> chainHistory = ActionChainResult.getChainHistory();
chainHistory.add(makeKey(namespace, actionName, methodName));
}

private String makeKey(String namespace, String actionName, String methodName) {
return namespace + "/" + actionName + (methodName != null ? "!" + methodName : "");
super(namespace, actionName, methodName, skipActions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsException;
import org.apache.struts2.result.ActionChainResult;

import java.util.ArrayList;
import java.util.Iterator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
package com.opensymphony.xwork2.interceptor;

import com.opensymphony.xwork2.ActionChainResult;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.inject.Inject;
Expand All @@ -31,6 +30,7 @@
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.Unchainable;
import org.apache.struts2.result.ActionChainResult;

import java.util.ArrayList;
import java.util.Collection;
Expand Down Expand Up @@ -117,7 +117,7 @@
*
* @author mrdon
* @author tm_jee ( tm_jee(at)yahoo.co.uk )
* @see com.opensymphony.xwork2.ActionChainResult
* @see ActionChainResult
*
* @deprecated since 6.7.0, use {@link org.apache.struts2.interceptor.ChainingInterceptor} instead.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
*/
package org.apache.struts2;

import com.opensymphony.xwork2.ActionChainResult;
import org.apache.struts2.action.Action;
import org.apache.struts2.interceptor.PreResultListener;
import org.apache.struts2.result.ActionChainResult;
import org.apache.struts2.result.Result;
import org.apache.struts2.util.ValueStack;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
package org.apache.struts2.interceptor;

import com.opensymphony.xwork2.ActionChainResult;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.ProxyUtil;
Expand All @@ -29,6 +28,7 @@
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.Unchainable;
import org.apache.struts2.result.ActionChainResult;
import org.apache.struts2.result.Result;
import org.apache.struts2.util.ValueStack;

Expand Down
Loading