/* * Copyright 2006 Alexandru Popescu * * 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.directwebremoting.webwork; import java.io.UnsupportedEncodingException; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; import org.directwebremoting.util.FakeHttpServletResponse; import org.directwebremoting.util.LocalUtil; import com.opensymphony.webwork.ServletActionContext; import com.opensymphony.webwork.dispatcher.DispatcherUtils; import com.opensymphony.webwork.dispatcher.mapper.ActionMapping; import com.opensymphony.xwork.ActionContext; import com.opensymphony.xwork.ActionInvocation; import com.opensymphony.xwork.ActionProxy; import com.opensymphony.xwork.ActionProxyFactory; import com.opensymphony.xwork.Result; import com.opensymphony.xwork.config.ConfigurationException; import com.opensymphony.xwork.util.OgnlValueStack; import com.opensymphony.xwork.util.XWorkContinuationConfig; /** * This class represents the entry point to all WebWork action invocations. It identifies the * action to be invoked, prepares the action invocation context and finally wraps the * result. * You can configure an <code>IDWRActionProcessor</code> through a context-wide initialization parameter * <code>dwrActionProcessor</code> that whose methods will be invoked around action invocation. * * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a> */ public class DWRAction { /** * @param servletContext * @throws ServletException */ private DWRAction(ServletContext servletContext) throws ServletException { DispatcherUtils.initialize(servletContext); wwDispatcher = DispatcherUtils.getInstance(); actionProcessor = loadActionProcessor(servletContext.getInitParameter(DWRACTIONPROCESSOR_INIT_PARAM)); } /** * */ protected AjaxResult doExecute(ActionDefinition actionDefinition, Map<String, String> params, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) throws ServletException { FakeHttpServletResponse actionResponse = new FakeHttpServletResponse(); if (null != actionProcessor) { actionProcessor.preProcess(request, response, actionResponse, params); } wwDispatcher.prepare(request, actionResponse); ActionInvocation invocation = invokeAction(wwDispatcher, request, actionResponse, servletContext, actionDefinition, params); AjaxResult result; if (actionDefinition.isExecuteResult()) { // HINT: we have output string result = getTextResult(actionResponse); } else { result = new DefaultAjaxDataResult(invocation.getAction()); } if (null != actionProcessor) { actionProcessor.postProcess(request, response, actionResponse, result); } return result; } /** * */ @SuppressWarnings("unchecked") protected ActionInvocation invokeAction(DispatcherUtils du, HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionDefinition actionDefinition, Map<String, String> params) throws ServletException { ActionMapping mapping = getActionMapping(actionDefinition, params); Map<String, Object> extraContext = du.createContextMap(request, response, mapping, context); // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action OgnlValueStack stack = (OgnlValueStack) request.getAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY); if (null != stack) { extraContext.put(ActionContext.VALUE_STACK, new OgnlValueStack(stack)); } try { prepareContinuationAction(request, extraContext); ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(actionDefinition.getNamespace(), actionDefinition.getAction(), extraContext, actionDefinition.isExecuteResult(), false); proxy.setMethod(actionDefinition.getMethod()); request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy.getInvocation().getStack()); // if the ActionMapping says to go straight to a result, do it! if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { proxy.execute(); } return proxy.getInvocation(); } catch (ConfigurationException ce) { throw new ServletException("Cannot invoke action '" + actionDefinition.getAction() + "' in namespace '" + actionDefinition.getNamespace() + "'", ce); } catch (Exception e) { throw new ServletException("Cannot invoke action '" + actionDefinition.getAction() + "' in namespace '" + actionDefinition.getNamespace() + "'", e); } finally { // If there was a previous value stack then set it back onto the request if (null != stack) { request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, stack); } } } /** * */ @SuppressWarnings("unchecked") protected void prepareContinuationAction(HttpServletRequest request, Map<String, Object> extraContext) { String id = request.getParameter(XWorkContinuationConfig.CONTINUE_PARAM); if (null != id) { // remove the continue key from the params - we don't want to bother setting // on the value stack since we know it won't work. Besides, this breaks devMode! Map<String, String> params = (Map<String, String>) extraContext.get(ActionContext.PARAMETERS); params.remove(XWorkContinuationConfig.CONTINUE_PARAM); // and now put the key in the context to be picked up later by XWork extraContext.put(XWorkContinuationConfig.CONTINUE_KEY, id); } } /** * */ protected ActionMapping getActionMapping(ActionDefinition actionDefinition, Map<String, String> params) { ActionMapping actionMapping = new ActionMapping(actionDefinition.getAction(), actionDefinition.getNamespace(), actionDefinition.getMethod(), params); return actionMapping; } /** * */ protected AjaxTextResult getTextResult(FakeHttpServletResponse response) { DefaultAjaxTextResult result = new DefaultAjaxTextResult(); String text = null; try { text = response.getContentAsString(); } catch (UnsupportedEncodingException uee) { log.warn("Cannot retrieve text output as string", uee); } if (null == text) { try { text = response.getCharacterEncoding() != null ? new String(response.getContentAsByteArray(), response.getCharacterEncoding()) : new String(response.getContentAsByteArray()); } catch (UnsupportedEncodingException uee) { log.warn("Cannot retrieve text output as encoded byte array", uee); text = new String(response.getContentAsByteArray()); } } result.setText(text); return result; } /** * Entry point for all action invocations. * @param actionDefinition the identification information for the action * @param params action invocation parameters * @param request original request * @param response original response * @param servletContext current <code>ServletContext</code> * @return an <code>AjaxResult</code> wrapping invocation result * @throws ServletException thrown if the initialization or invocation of the action fails */ public static AjaxResult execute(ActionDefinition actionDefinition, Map<String, String> params, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) throws ServletException { initialize(servletContext); return instance.doExecute(actionDefinition, params, request, response, servletContext); } /** * Performs the one time initialization of the singleton <code>DWRAction</code>. * @param servletContext * @throws ServletException thrown in case the singleton initialization fails */ private static void initialize(ServletContext servletContext) throws ServletException { synchronized(DWRAction.class) { if (null == instance) { instance = new DWRAction(servletContext); } } } /** * Tries to instantiate an <code>IDWRActionProcessor</code> if defined in web.xml. * @param actionProcessorClassName * @return an instance of <code>IDWRActionProcessor</code> if the init-param is defined or <code>null</code> * @throws ServletException thrown if the <code>IDWRActionProcessor</code> cannot be loaded and instantiated */ private static IDWRActionProcessor loadActionProcessor(String actionProcessorClassName) throws ServletException { if (null == actionProcessorClassName || "".equals(actionProcessorClassName)) { return null; } IDWRActionProcessor reply = LocalUtil.classNewInstance("DWRActionProcessor", actionProcessorClassName, IDWRActionProcessor.class); if (reply == null) { throw new ServletException("Cannot load DWRActionProcessor class '" + actionProcessorClassName + "'"); } return reply; } /** * The log stream */ private static final Log log = LogFactory.getLog(DWRAction.class); private static final String DWRACTIONPROCESSOR_INIT_PARAM = "dwrActionProcessor"; private static DWRAction instance; private DispatcherUtils wwDispatcher; private IDWRActionProcessor actionProcessor; }