001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.xwork;
006:
007: import com.opensymphony.xwork.interceptor.component.ComponentInterceptor;
008: import com.opensymphony.xwork.util.OgnlValueStack;
009: import com.opensymphony.xwork.util.TextParseUtil;
010: import org.apache.commons.logging.Log;
011: import org.apache.commons.logging.LogFactory;
012:
013: import java.util.HashMap;
014: import java.util.HashSet;
015: import java.util.Set;
016:
017: /**
018: * <!-- START SNIPPET: description -->
019: *
020: * This result invokes an entire other action, complete with it's own interceptor stack and result.
021: *
022: * <!-- END SNIPPET: description -->
023: *
024: * <b>This result type takes the following parameters:</b>
025: *
026: * <!-- START SNIPPET: params -->
027: *
028: * <ul>
029: *
030: * <li><b>actionName (default)</b> - the name of the action that will be chained to</li>
031: *
032: * <li><b>namespace</b> - used to determine which namespace the Action is in that we're chaining. If namespace is null,
033: * this defaults to the current namespace</li>
034: *
035: * <li><b>method</b> - used to specify another method on target action to be invoked.
036: * If null, this defaults to execute method</li>
037: *
038: * </ul>
039: *
040: * <!-- END SNIPPET: params -->
041: *
042: * <b>Example:</b>
043: *
044: * <pre><!-- START SNIPPET: example -->
045: * <package name="public" extends="webwork-default">
046: * <!-- Chain creatAccount to login, using the default parameter -->
047: * <action name="createAccount" class="...">
048: * <result type="chain">login</result>
049: * </action>
050: *
051: * <action name="login" class="...">
052: * <!-- Chain to another namespace -->
053: * <result type="chain">
054: * <param name="actionName">dashboard</param>
055: * <param name="namespace">/secure</param>
056: * </result>
057: * </action>
058: * </package>
059: *
060: * <package name="secure" extends="webwork-default" namespace="/secure">
061: * <action name="dashboard" class="...">
062: * <result>dashboard.jsp</result>
063: * </action>
064: * </package>
065: * <!-- END SNIPPET: example --></pre>
066: *
067: * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
068: */
069: public class ActionChainResult implements Result {
070:
071: private static final Log log = LogFactory
072: .getLog(ActionChainResult.class);
073: public static final String DEFAULT_PARAM = "actionName";
074: private static final String CHAIN_HISTORY = "CHAIN_HISTORY";
075:
076: private ActionProxy proxy;
077: private String actionName;
078:
079: private String namespace;
080:
081: private String methodName;
082:
083: public void setActionName(String actionName) {
084: this .actionName = actionName;
085: }
086:
087: public void setNamespace(String namespace) {
088: this .namespace = namespace;
089: }
090:
091: public void setMethod(String method) {
092: this .methodName = method;
093: }
094:
095: public ActionProxy getProxy() {
096: return proxy;
097: }
098:
099: public boolean equals(Object o) {
100: if (this == o) {
101: return true;
102: }
103:
104: if (!(o instanceof ActionChainResult)) {
105: return false;
106: }
107:
108: final ActionChainResult actionChainResult = (ActionChainResult) o;
109:
110: if ((actionName != null) ? (!actionName
111: .equals(actionChainResult.actionName))
112: : (actionChainResult.actionName != null)) {
113: return false;
114: }
115:
116: return true;
117: }
118:
119: /**
120: * @param invocation the DefaultActionInvocation calling the action call stack
121: */
122: public void execute(ActionInvocation invocation) throws Exception {
123: // if the finalNamespace wasn't explicitly defined, assume the current one
124: if (this .namespace == null) {
125: this .namespace = invocation.getProxy().getNamespace();
126: }
127:
128: OgnlValueStack stack = ActionContext.getContext()
129: .getValueStack();
130: String finalNamespace = TextParseUtil.translateVariables(
131: namespace, stack);
132: String finalActionName = TextParseUtil.translateVariables(
133: actionName, stack);
134: String finalMethodName = this .methodName != null ? TextParseUtil
135: .translateVariables(this .methodName, stack)
136: : null;
137:
138: if (isInChainHistory(finalNamespace, finalActionName,
139: finalMethodName)) {
140: throw new XworkException("infinite recursion detected");
141: }
142:
143: addToHistory(finalNamespace, finalActionName, finalMethodName);
144:
145: HashMap extraContext = new HashMap();
146: extraContext.put(ActionContext.VALUE_STACK, ActionContext
147: .getContext().getValueStack());
148: extraContext.put(ActionContext.PARAMETERS, ActionContext
149: .getContext().getParameters());
150: extraContext.put(ComponentInterceptor.COMPONENT_MANAGER,
151: ActionContext.getContext().get(
152: ComponentInterceptor.COMPONENT_MANAGER));
153: extraContext.put(CHAIN_HISTORY, ActionContext.getContext().get(
154: CHAIN_HISTORY));
155:
156: if (log.isDebugEnabled()) {
157: log.debug("Chaining to action " + finalActionName);
158: }
159:
160: proxy = ActionProxyFactory.getFactory().createActionProxy(
161: finalNamespace, finalActionName, extraContext);
162: if (null != finalMethodName) {
163: proxy.setMethod(finalMethodName);
164: }
165: proxy.execute();
166: }
167:
168: public int hashCode() {
169: return ((actionName != null) ? actionName.hashCode() : 0);
170: }
171:
172: private boolean isInChainHistory(String namespace,
173: String actionName, String methodName) {
174: Set chainHistory = (Set) ActionContext.getContext().get(
175: CHAIN_HISTORY);
176:
177: if (chainHistory == null) {
178: return false;
179: } else {
180: return chainHistory.contains(makeKey(namespace, actionName,
181: methodName));
182: }
183: }
184:
185: private void addToHistory(String namespace, String actionName,
186: String methodName) {
187: Set chainHistory = (Set) ActionContext.getContext().get(
188: CHAIN_HISTORY);
189:
190: if (chainHistory == null) {
191: chainHistory = new HashSet();
192: }
193:
194: ActionContext.getContext().put(CHAIN_HISTORY, chainHistory);
195:
196: chainHistory.add(makeKey(namespace, actionName, methodName));
197: }
198:
199: private String makeKey(String namespace, String actionName,
200: String methodName) {
201: if (null == methodName) {
202: return namespace + "/" + actionName;
203: }
204:
205: return namespace + "/" + actionName + "!" + methodName;
206: }
207: }
|