| /* |
| * $Id$ |
| * |
| * Copyright 2006 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.struts2.components; |
| |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import javax.servlet.ServletContext; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.servlet.jsp.PageContext; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.struts2.ServletActionContext; |
| import org.apache.struts2.StrutsException; |
| import org.apache.struts2.dispatcher.Dispatcher; |
| import org.apache.struts2.dispatcher.RequestMap; |
| import org.apache.struts2.views.jsp.TagUtils; |
| |
| import com.opensymphony.xwork2.ActionContext; |
| import com.opensymphony.xwork2.ActionProxy; |
| import com.opensymphony.xwork2.ActionProxyFactory; |
| import com.opensymphony.xwork2.config.Configuration; |
| import com.opensymphony.xwork2.util.OgnlValueStack; |
| |
| /** |
| * <!-- START SNIPPET: javadoc --> |
| * <p>This tag enables developers to call actions directly from a JSP page by specifying the action name and an optional |
| * namespace. The body content of the tag is used to render the results from the Action. Any result processor defined |
| * for this action in struts.xml will be ignored, <i>unless</i> the executeResult parameter is specified.</p> |
| * <!-- END SNIPPET: javadoc --> |
| * |
| * <!-- START SNIPPET: params --> |
| * <ul> |
| * <li>id (String) - the id (if specified) to put the action under stack's context. |
| * <li>name* (String) - name of the action to be executed (without the extension suffix eg. .action)</li> |
| * <li>namespace (String) - default to the namespace where this action tag is invoked</li> |
| * <li>executeResult (Boolean) - default is false. Decides wheather the result of this action is to be executed or not</li> |
| * <li>ignoreContextParams (Boolean) - default to false. Decides wheather the request parameters are to be included when the action is invoked</li> |
| * </ul> |
| * <!-- END SNIPPET: params --> |
| * |
| * <pre> |
| * <!-- START SNIPPET: javacode --> |
| * public class ActionTagAction extends ActionSupport { |
| * |
| * public String execute() throws Exception { |
| * return "done"; |
| * } |
| * |
| * public String doDefault() throws Exception { |
| * ServletActionContext.getRequest().setAttribute("stringByAction", "This is a String put in by the action's doDefault()"); |
| * return "done"; |
| * } |
| * } |
| * <!-- END SNIPPET: javacode --> |
| * </pre> |
| * |
| * <pre> |
| * <!-- START SNIPPET: strutsxml --> |
| * <xwork> |
| * .... |
| * <action name="actionTagAction1" class="tmjee.testing.ActionTagAction"> |
| * <result name="done">success.jsp</result> |
| * </action> |
| * <action name="actionTagAction2" class="tmjee.testing.ActionTagAction" method="default"> |
| * <result name="done">success.jsp</result> |
| * </action> |
| * .... |
| * </xwork> |
| * <!-- END SNIPPET: strutsxml --> |
| * </pre> |
| * |
| * <pre> |
| * <!-- START SNIPPET: example --> |
| * <div>The following action tag will execute result and include it in this page</div> |
| * <br /> |
| * <s:action name="actionTagAction" executeResult="true" /> |
| * <br /> |
| * <div>The following action tag will do the same as above, but invokes method specialMethod in action</div> |
| * <br /> |
| * <s:action name="actionTagAction!specialMethod" executeResult="true" /> |
| * <br /> |
| * <div>The following action tag will not execute result, but put a String in request scope |
| * under an id "stringByAction" which will be retrieved using property tag</div> |
| * <s:action name="actionTagAction!default" executeResult="false" /> |
| * <s:property value="#attr.stringByAction" /> |
| * <!-- END SNIPPET: example --> |
| * </pre> |
| * |
| * @s.tag name="action" tld-body-content="JSP" tld-tag-class="org.apache.struts2.views.jsp.ActionTag" |
| * description="Execute an action from within a view" |
| */ |
| public class ActionComponent extends Component { |
| private static final Log LOG = LogFactory.getLog(ActionComponent.class); |
| |
| protected HttpServletResponse res; |
| protected HttpServletRequest req; |
| |
| protected ActionProxy proxy; |
| protected String name; |
| protected String namespace; |
| protected boolean executeResult; |
| protected boolean ignoreContextParams; |
| |
| public ActionComponent(OgnlValueStack stack, HttpServletRequest req, HttpServletResponse res) { |
| super(stack); |
| this.req = req; |
| this.res = res; |
| } |
| |
| public boolean end(Writer writer, String body) { |
| boolean end = super.end(writer, "", false); |
| try { |
| try { |
| writer.flush(); |
| } catch (IOException e) { |
| LOG.warn("error while trying to flush writer ", e); |
| } |
| executeAction(); |
| |
| if ((getId() != null) && (proxy != null)) { |
| getStack().setValue("#attr['" + getId() + "']", |
| proxy.getAction()); |
| } |
| } finally { |
| popComponentStack(); |
| } |
| return end; |
| } |
| |
| private Map createExtraContext() { |
| Map parentParams = null; |
| |
| if (!ignoreContextParams) { |
| parentParams = new ActionContext(getStack().getContext()).getParameters(); |
| } |
| |
| Map newParams = (parentParams != null) ? new HashMap(parentParams) : new HashMap(); |
| |
| if (parameters != null) { |
| newParams.putAll(parameters); |
| } |
| |
| ActionContext ctx = new ActionContext(stack.getContext()); |
| ServletContext servletContext = (ServletContext) ctx.get(ServletActionContext.SERVLET_CONTEXT); |
| PageContext pageContext = (PageContext) ctx.get(ServletActionContext.PAGE_CONTEXT); |
| Map session = ctx.getSession(); |
| Map application = ctx.getApplication(); |
| |
| Dispatcher du = Dispatcher.getInstance(); |
| Map extraContext = du.createContextMap(new RequestMap(req), |
| newParams, |
| session, |
| application, |
| req, |
| res, |
| servletContext); |
| |
| OgnlValueStack newStack = new OgnlValueStack(stack); |
| extraContext.put(ActionContext.VALUE_STACK, newStack); |
| |
| // add page context, such that ServletDispatcherResult will do an include |
| extraContext.put(ServletActionContext.PAGE_CONTEXT, pageContext); |
| |
| return extraContext; |
| } |
| |
| public ActionProxy getProxy() { |
| return proxy; |
| } |
| |
| /** |
| * Execute the requested action. If no namespace is provided, we'll |
| * attempt to derive a namespace using buildNamespace(). The ActionProxy |
| * and the namespace will be saved into the instance variables proxy and |
| * namespace respectively. |
| * |
| * @see org.apache.struts2.views.jsp.TagUtils#buildNamespace |
| */ |
| private void executeAction() { |
| String actualName = findString(name, "name", "Action name is required. Example: updatePerson"); |
| |
| if (actualName == null) { |
| throw new StrutsException("Unable to find value for name " + name); |
| } |
| |
| // handle "name!method" convention. |
| final String actionName; |
| final String methodName; |
| |
| int exclamation = actualName.lastIndexOf("!"); |
| if (exclamation != -1) { |
| actionName = actualName.substring(0, exclamation); |
| methodName = actualName.substring(exclamation + 1); |
| } else { |
| actionName = actualName; |
| methodName = null; |
| } |
| |
| String namespace; |
| |
| if (this.namespace == null) { |
| namespace = TagUtils.buildNamespace(getStack(), req); |
| } else { |
| namespace = findString(this.namespace); |
| } |
| |
| // get the old value stack from the request |
| OgnlValueStack stack = getStack(); |
| // execute at this point, after params have been set |
| try { |
| Configuration config = Dispatcher.getInstance().getConfigurationManager().getConfiguration(); |
| proxy = ActionProxyFactory.getFactory().createActionProxy(config, namespace, actionName, createExtraContext(), executeResult, true); |
| if (null != methodName) { |
| proxy.setMethod(methodName); |
| } |
| // set the new stack into the request for the taglib to use |
| req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); |
| proxy.execute(); |
| |
| } catch (Exception e) { |
| String message = "Could not execute action: " + namespace + "/" + actualName; |
| LOG.error(message, e); |
| } finally { |
| // set the old stack back on the request |
| req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); |
| } |
| |
| if ((getId() != null) && (proxy != null)) { |
| final Map context = stack.getContext(); |
| context.put(getId(), proxy.getAction()); |
| } |
| } |
| |
| /** |
| * the id (if speficied) to put the action under stack's context. |
| * @s.tagattribute required="false" type="String" |
| */ |
| public void setId(String id) { |
| super.setId(id); |
| } |
| |
| /** |
| * name of the action to be executed (without the extension suffix eg. .action) |
| * @s.tagattribute required="true" type="String" |
| */ |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| /** |
| * namespace for action to call |
| * @s.tagattribute required="false" type="String" default="namespace from where tag is used" |
| */ |
| public void setNamespace(String namespace) { |
| this.namespace = namespace; |
| } |
| |
| /** |
| * whether the result of this action (probably a view) should be executed/rendered |
| * @s.tagattribute required="false" type="Boolean" default="false" |
| */ |
| public void setExecuteResult(boolean executeResult) { |
| this.executeResult = executeResult; |
| } |
| |
| /** |
| * whether the request parameters are to be included when the action is invoked |
| * @s.tagattribute required="false" type="Boolean" default="false" |
| */ |
| public void setIgnoreContextParams(boolean ignoreContextParams) { |
| this.ignoreContextParams = ignoreContextParams; |
| } |
| } |