001: package com.opensymphony.webwork.components;
002:
003: import com.opensymphony.webwork.ServletActionContext;
004: import com.opensymphony.webwork.WebWorkException;
005: import com.opensymphony.webwork.dispatcher.DispatcherUtils;
006: import com.opensymphony.webwork.dispatcher.RequestMap;
007: import com.opensymphony.webwork.views.jsp.TagUtils;
008: import com.opensymphony.xwork.ActionContext;
009: import com.opensymphony.xwork.ActionProxy;
010: import com.opensymphony.xwork.ActionProxyFactory;
011: import com.opensymphony.xwork.util.OgnlValueStack;
012: import org.apache.commons.logging.Log;
013: import org.apache.commons.logging.LogFactory;
014:
015: import javax.servlet.ServletContext;
016: import javax.servlet.jsp.PageContext;
017: import javax.servlet.http.HttpServletRequest;
018: import javax.servlet.http.HttpServletResponse;
019: import java.io.Writer;
020: import java.io.IOException;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: /**
025: * <!-- START SNIPPET: javadoc -->
026: * <p>This tag enables developers to call actions directly from a JSP page by specifying the action name and an optional
027: * namespace. The body content of the tag is used to render the results from the Action. Any result processor defined
028: * for this action in xwork.xml will be ignored, <i>unless</i> the executeResult parameter is specified.</p>
029: * <!-- END SNIPPET: javadoc -->
030: *
031: * <!-- START SNIPPET: params -->
032: * <ul>
033: * <li>id (String) - the id (if specified) to put the action under stack's context.
034: * <li>name* (String) - name of the action to be executed (without the extension suffix eg. .action)</li>
035: * <li>namespace (String) - default to the namespace where this action tag is invoked</li>
036: * <li>executeResult (Boolean) - default is false. Decides wheather the result of this action is to be executed or not</li>
037: * <li>ignoreContextParams (Boolean) - default to false. Decides wheather the request parameters are to be included when the action is invoked</li>
038: * </ul>
039: * <!-- END SNIPPET: params -->
040: *
041: * <pre>
042: * <!-- START SNIPPET: javacode -->
043: * public class ActionTagAction extends ActionSupport {
044: *
045: * public String execute() throws Exception {
046: * return "done";
047: * }
048: *
049: * public String doDefault() throws Exception {
050: * ServletActionContext.getRequest().setAttribute("stringByAction", "This is a String put in by the action's doDefault()");
051: * return "done";
052: * }
053: * }
054: * <!-- END SNIPPET: javacode -->
055: * </pre>
056: *
057: * <pre>
058: * <!-- START SNIPPET: webworkxml -->
059: * <xwork>
060: * ....
061: * <action name="actionTagAction1" class="tmjee.testing.ActionTagAction">
062: * <result name="done">success.jsp</result>
063: * </action>
064: * <action name="actionTagAction2" class="tmjee.testing.ActionTagAction" method="default">
065: * <result name="done">success.jsp</result>
066: * </action>
067: * ....
068: * </xwork>
069: * <!-- END SNIPPET: webworkxml -->
070: * </pre>
071: *
072: * <pre>
073: * <!-- START SNIPPET: example -->
074: * <div>The following action tag will execute result and include it in this page</div>
075: * <br />
076: * <ww:action name="actionTagAction" executeResult="true" />
077: * <br />
078: * <div>The following action tag will do the same as above, but invokes method specialMethod in action</div>
079: * <br />
080: * <ww:action name="actionTagAction!specialMethod" executeResult="true" />
081: * <br />
082: * <div>The following action tag will not execute result, but put a String in request scope
083: * under an id "stringByAction" which will be retrieved using property tag</div>
084: * <ww:action name="actionTagAction!default" executeResult="false" />
085: * <ww:property value="#attr.stringByAction" />
086: * <!-- END SNIPPET: example -->
087: * </pre>
088: *
089: * @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
090: * @author tm_jee ( tm_jee (at) yahoo.co.uk )
091: * @author Rene Gielen
092: * @version $Revision: 2730 $
093: * @since 2.2
094: *
095: * @ww.tag name="action" tld-body-content="JSP" tld-tag-class="com.opensymphony.webwork.views.jsp.ActionTag"
096: * description="Execute an action from within a view"
097: */
098: public class ActionComponent extends Component {
099: private static final Log LOG = LogFactory
100: .getLog(ActionComponent.class);
101:
102: protected HttpServletResponse res;
103: protected HttpServletRequest req;
104:
105: protected ActionProxy proxy;
106: protected String name;
107: protected String namespace;
108: protected boolean executeResult;
109: protected boolean ignoreContextParams;
110: protected boolean flush;
111:
112: public ActionComponent(OgnlValueStack stack,
113: HttpServletRequest req, HttpServletResponse res) {
114: super (stack);
115: this .req = req;
116: this .res = res;
117: }
118:
119: public boolean end(Writer writer, String body) {
120: boolean end = super .end(writer, "", false);
121: try {
122: if (flush) {
123: try {
124: writer.flush();
125: } catch (IOException e) {
126: LOG.warn("Error while trying to flush writer ", e);
127: }
128: }
129: executeAction();
130:
131: if ((getId() != null) && (proxy != null)) {
132: getStack().setValue("#attr['" + getId() + "']",
133: proxy.getAction());
134: }
135:
136: } finally {
137: popComponentStack();
138: }
139: return end;
140: }
141:
142: private Map createExtraContext() {
143: Map parentParams = null;
144:
145: if (!ignoreContextParams) {
146: parentParams = new ActionContext(getStack().getContext())
147: .getParameters();
148: }
149:
150: Map newParams = (parentParams != null) ? new HashMap(
151: parentParams) : new HashMap();
152:
153: if (parameters != null) {
154: newParams.putAll(parameters);
155: }
156:
157: ActionContext ctx = new ActionContext(stack.getContext());
158: ServletContext servletContext = (ServletContext) ctx
159: .get(ServletActionContext.SERVLET_CONTEXT);
160: PageContext pageContext = (PageContext) ctx
161: .get(ServletActionContext.PAGE_CONTEXT);
162: Map session = ctx.getSession();
163: Map application = ctx.getApplication();
164:
165: DispatcherUtils.initialize(servletContext);
166: DispatcherUtils du = DispatcherUtils.getInstance();
167: Map extraContext = du.createContextMap(new RequestMap(req),
168: newParams, session, application, req, res,
169: servletContext);
170:
171: OgnlValueStack newStack = new OgnlValueStack(stack);
172: extraContext.put(ActionContext.VALUE_STACK, newStack);
173:
174: // add page context, such that ServletDispatcherResult will do an include
175: extraContext
176: .put(ServletActionContext.PAGE_CONTEXT, pageContext);
177:
178: return extraContext;
179: }
180:
181: public ActionProxy getProxy() {
182: return proxy;
183: }
184:
185: /**
186: * Execute the requested action. If no namespace is provided, we'll
187: * attempt to derive a namespace using buildNamespace(). The ActionProxy
188: * and the namespace will be saved into the instance variables proxy and
189: * namespace respectively.
190: *
191: * @see com.opensymphony.webwork.views.jsp.TagUtils#buildNamespace
192: */
193: private void executeAction() {
194: String actualName = findString(name, "name",
195: "Action name is required. Example: updatePerson");
196:
197: if (actualName == null) {
198: String message = "Unable to find value for name " + name;
199: LOG.error(message);
200: throw new WebWorkException(message);
201: }
202:
203: // handle "name!method" convention.
204: final String actionName;
205: final String methodName;
206:
207: int exclamation = actualName.lastIndexOf("!");
208: if (exclamation != -1) {
209: actionName = actualName.substring(0, exclamation);
210: methodName = actualName.substring(exclamation + 1);
211: } else {
212: actionName = actualName;
213: methodName = null;
214: }
215:
216: String namespace;
217:
218: if (this .namespace == null) {
219: namespace = TagUtils.buildNamespace(getStack(), req);
220: } else {
221: namespace = findString(this .namespace);
222: }
223:
224: // get the old value stack from the request
225: OgnlValueStack stack = getStack();
226: // execute at this point, after params have been set
227: try {
228: proxy = ActionProxyFactory.getFactory().createActionProxy(
229: namespace, actionName, createExtraContext(),
230: executeResult, true);
231: if (null != methodName) {
232: proxy.setMethod(methodName);
233: }
234: // set the new stack into the request for the taglib to use
235: req.setAttribute(
236: ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy
237: .getInvocation().getStack());
238: proxy.execute();
239:
240: } catch (Exception e) {
241: String message = "Could not execute action: " + namespace
242: + "/" + actualName;
243: LOG.error(message, e);
244: } finally {
245: // set the old stack back on the request
246: req.setAttribute(
247: ServletActionContext.WEBWORK_VALUESTACK_KEY, stack);
248: }
249:
250: if ((getId() != null) && (proxy != null)) {
251: final Map context = stack.getContext();
252: context.put(getId(), proxy.getAction());
253: }
254: }
255:
256: /**
257: * the id (if speficied) to put the action under stack's context.
258: * @ww.tagattribute required="false" type="String"
259: */
260: public void setId(String id) {
261: super .setId(id);
262: }
263:
264: /**
265: * name of the action to be executed (without the extension suffix eg. .action)
266: * @ww.tagattribute required="true" type="String"
267: */
268: public void setName(String name) {
269: this .name = name;
270: }
271:
272: /**
273: * namespace for action to call
274: * @ww.tagattribute required="false" type="String" default="namespace from where tag is used"
275: */
276: public void setNamespace(String namespace) {
277: this .namespace = namespace;
278: }
279:
280: /**
281: * whether the result of this action (probably a view) should be executed/rendered
282: * @ww.tagattribute required="false" type="Boolean" default="false"
283: */
284: public void setExecuteResult(boolean executeResult) {
285: this .executeResult = executeResult;
286: }
287:
288: /**
289: * whether the request parameters are to be included when the action is invoked
290: * @ww.tagattribute required="false" type="Boolean" default="false"
291: */
292: public void setIgnoreContextParams(boolean ignoreContextParams) {
293: this .ignoreContextParams = ignoreContextParams;
294: }
295:
296: /**
297: * whether the writer should be flush upon end of the action tag, default to true.
298: * @ww.tagattribute required="false" type="Boolean" default="true"
299: */
300: public void setFlush(boolean flush) {
301: this.flush = flush;
302: }
303: }
|