1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.components;
19
20 import java.io.IOException;
21 import java.io.Writer;
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import javax.servlet.ServletContext;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28 import javax.servlet.jsp.PageContext;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.struts2.ServletActionContext;
33 import org.apache.struts2.StrutsException;
34 import org.apache.struts2.dispatcher.Dispatcher;
35 import org.apache.struts2.dispatcher.RequestMap;
36 import org.apache.struts2.views.jsp.TagUtils;
37
38 import com.opensymphony.xwork2.ActionContext;
39 import com.opensymphony.xwork2.ActionProxy;
40 import com.opensymphony.xwork2.ActionProxyFactory;
41 import com.opensymphony.xwork2.config.Configuration;
42 import com.opensymphony.xwork2.util.ValueStack;
43 import com.opensymphony.xwork2.util.ValueStackFactory;
44
45 /***
46 * <!-- START SNIPPET: javadoc -->
47 * <p>This tag enables developers to call actions directly from a JSP page by specifying the action name and an optional
48 * namespace. The body content of the tag is used to render the results from the Action. Any result processor defined
49 * for this action in struts.xml will be ignored, <i>unless</i> the executeResult parameter is specified.</p>
50 * <!-- END SNIPPET: javadoc -->
51 *
52 * <!-- START SNIPPET: params -->
53 * <ul>
54 * <li>id (String) - the id (if specified) to put the action under stack's context.
55 * <li>name* (String) - name of the action to be executed (without the extension suffix eg. .action)</li>
56 * <li>namespace (String) - default to the namespace where this action tag is invoked</li>
57 * <li>executeResult (Boolean) - default is false. Decides wheather the result of this action is to be executed or not</li>
58 * <li>ignoreContextParams (Boolean) - default to false. Decides wheather the request parameters are to be included when the action is invoked</li>
59 * </ul>
60 * <!-- END SNIPPET: params -->
61 *
62 * <pre>
63 * <!-- START SNIPPET: javacode -->
64 * public class ActionTagAction extends ActionSupport {
65 *
66 * public String execute() throws Exception {
67 * return "done";
68 * }
69 *
70 * public String doDefault() throws Exception {
71 * ServletActionContext.getRequest().setAttribute("stringByAction", "This is a String put in by the action's doDefault()");
72 * return "done";
73 * }
74 * }
75 * <!-- END SNIPPET: javacode -->
76 * </pre>
77 *
78 * <pre>
79 * <!-- START SNIPPET: strutsxml -->
80 * <xwork>
81 * ....
82 * <action name="actionTagAction1" class="tmjee.testing.ActionTagAction">
83 * <result name="done">success.jsp</result>
84 * </action>
85 * <action name="actionTagAction2" class="tmjee.testing.ActionTagAction" method="default">
86 * <result name="done">success.jsp</result>
87 * </action>
88 * ....
89 * </xwork>
90 * <!-- END SNIPPET: strutsxml -->
91 * </pre>
92 *
93 * <pre>
94 * <!-- START SNIPPET: example -->
95 * <div>The following action tag will execute result and include it in this page</div>
96 * <br />
97 * <s:action name="actionTagAction" executeResult="true" />
98 * <br />
99 * <div>The following action tag will do the same as above, but invokes method specialMethod in action</div>
100 * <br />
101 * <s:action name="actionTagAction!specialMethod" executeResult="true" />
102 * <br />
103 * <div>The following action tag will not execute result, but put a String in request scope
104 * under an id "stringByAction" which will be retrieved using property tag</div>
105 * <s:action name="actionTagAction!default" executeResult="false" />
106 * <s:property value="#attr.stringByAction" />
107 * <!-- END SNIPPET: example -->
108 * </pre>
109 *
110 * @s.tag name="action" tld-body-content="JSP" tld-tag-class="org.apache.struts2.views.jsp.ActionTag"
111 * description="Execute an action from within a view"
112 */
113 public class ActionComponent extends Component {
114 private static final Log LOG = LogFactory.getLog(ActionComponent.class);
115
116 protected HttpServletResponse res;
117 protected HttpServletRequest req;
118
119 protected ActionProxy proxy;
120 protected String name;
121 protected String namespace;
122 protected boolean executeResult;
123 protected boolean ignoreContextParams;
124
125 public ActionComponent(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
126 super(stack);
127 this.req = req;
128 this.res = res;
129 }
130
131 public boolean end(Writer writer, String body) {
132 boolean end = super.end(writer, "", false);
133 try {
134 try {
135 writer.flush();
136 } catch (IOException e) {
137 LOG.warn("error while trying to flush writer ", e);
138 }
139 executeAction();
140
141 if ((getId() != null) && (proxy != null)) {
142 getStack().setValue("#attr['" + getId() + "']",
143 proxy.getAction());
144 }
145 } finally {
146 popComponentStack();
147 }
148 return end;
149 }
150
151 private Map createExtraContext() {
152 Map parentParams = null;
153
154 if (!ignoreContextParams) {
155 parentParams = new ActionContext(getStack().getContext()).getParameters();
156 }
157
158 Map newParams = (parentParams != null) ? new HashMap(parentParams) : new HashMap();
159
160 if (parameters != null) {
161 newParams.putAll(parameters);
162 }
163
164 ActionContext ctx = new ActionContext(stack.getContext());
165 ServletContext servletContext = (ServletContext) ctx.get(ServletActionContext.SERVLET_CONTEXT);
166 PageContext pageContext = (PageContext) ctx.get(ServletActionContext.PAGE_CONTEXT);
167 Map session = ctx.getSession();
168 Map application = ctx.getApplication();
169
170 Dispatcher du = Dispatcher.getInstance();
171 Map extraContext = du.createContextMap(new RequestMap(req),
172 newParams,
173 session,
174 application,
175 req,
176 res,
177 servletContext);
178
179 ValueStack newStack = ValueStackFactory.getFactory().createValueStack(stack);
180 extraContext.put(ActionContext.VALUE_STACK, newStack);
181
182
183 extraContext.put(ServletActionContext.PAGE_CONTEXT, pageContext);
184
185 return extraContext;
186 }
187
188 public ActionProxy getProxy() {
189 return proxy;
190 }
191
192 /***
193 * Execute the requested action. If no namespace is provided, we'll
194 * attempt to derive a namespace using buildNamespace(). The ActionProxy
195 * and the namespace will be saved into the instance variables proxy and
196 * namespace respectively.
197 *
198 * @see org.apache.struts2.views.jsp.TagUtils#buildNamespace
199 */
200 private void executeAction() {
201 String actualName = findString(name, "name", "Action name is required. Example: updatePerson");
202
203 if (actualName == null) {
204 throw new StrutsException("Unable to find value for name " + name);
205 }
206
207
208 final String actionName;
209 final String methodName;
210
211 int exclamation = actualName.lastIndexOf("!");
212 if (exclamation != -1) {
213 actionName = actualName.substring(0, exclamation);
214 methodName = actualName.substring(exclamation + 1);
215 } else {
216 actionName = actualName;
217 methodName = null;
218 }
219
220 String namespace;
221
222 if (this.namespace == null) {
223 namespace = TagUtils.buildNamespace(getStack(), req);
224 } else {
225 namespace = findString(this.namespace);
226 }
227
228
229 ValueStack stack = getStack();
230
231 try {
232 Configuration config = Dispatcher.getInstance().getConfigurationManager().getConfiguration();
233 proxy = ActionProxyFactory.getFactory().createActionProxy(config, namespace, actionName, createExtraContext(), executeResult, true);
234 if (null != methodName) {
235 proxy.setMethod(methodName);
236 }
237
238 req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
239 proxy.execute();
240
241 } catch (Exception e) {
242 String message = "Could not execute action: " + namespace + "/" + actualName;
243 LOG.error(message, e);
244 } finally {
245
246 req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
247 }
248
249 if ((getId() != null) && (proxy != null)) {
250 final Map context = stack.getContext();
251 context.put(getId(), proxy.getAction());
252 }
253 }
254
255 /***
256 * the id (if speficied) to put the action under stack's context.
257 * @s.tagattribute required="false" type="String"
258 */
259 public void setId(String id) {
260 super.setId(id);
261 }
262
263 /***
264 * name of the action to be executed (without the extension suffix eg. .action)
265 * @s.tagattribute required="true" type="String"
266 */
267 public void setName(String name) {
268 this.name = name;
269 }
270
271 /***
272 * namespace for action to call
273 * @s.tagattribute required="false" type="String" default="namespace from where tag is used"
274 */
275 public void setNamespace(String namespace) {
276 this.namespace = namespace;
277 }
278
279 /***
280 * whether the result of this action (probably a view) should be executed/rendered
281 * @s.tagattribute required="false" type="Boolean" default="false"
282 */
283 public void setExecuteResult(boolean executeResult) {
284 this.executeResult = executeResult;
285 }
286
287 /***
288 * whether the request parameters are to be included when the action is invoked
289 * @s.tagattribute required="false" type="Boolean" default="false"
290 */
291 public void setIgnoreContextParams(boolean ignoreContextParams) {
292 this.ignoreContextParams = ignoreContextParams;
293 }
294 }