001: /*
002: * Copyright 2006 Alexandru Popescu
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.directwebremoting.webwork;
017:
018: import java.io.UnsupportedEncodingException;
019: import java.util.Map;
020:
021: import javax.servlet.ServletContext;
022: import javax.servlet.ServletException;
023: import javax.servlet.http.HttpServletRequest;
024: import javax.servlet.http.HttpServletResponse;
025:
026: import org.apache.commons.logging.LogFactory;
027: import org.apache.commons.logging.Log;
028: import org.directwebremoting.util.FakeHttpServletResponse;
029: import org.directwebremoting.util.LocalUtil;
030:
031: import com.opensymphony.webwork.ServletActionContext;
032: import com.opensymphony.webwork.dispatcher.DispatcherUtils;
033: import com.opensymphony.webwork.dispatcher.mapper.ActionMapping;
034: import com.opensymphony.xwork.ActionContext;
035: import com.opensymphony.xwork.ActionInvocation;
036: import com.opensymphony.xwork.ActionProxy;
037: import com.opensymphony.xwork.ActionProxyFactory;
038: import com.opensymphony.xwork.Result;
039: import com.opensymphony.xwork.config.ConfigurationException;
040: import com.opensymphony.xwork.util.OgnlValueStack;
041: import com.opensymphony.xwork.util.XWorkContinuationConfig;
042:
043: /**
044: * This class represents the entry point to all WebWork action invocations. It identifies the
045: * action to be invoked, prepares the action invocation context and finally wraps the
046: * result.
047: * You can configure an <code>IDWRActionProcessor</code> through a context-wide initialization parameter
048: * <code>dwrActionProcessor</code> that whose methods will be invoked around action invocation.
049: *
050: * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
051: */
052: public class DWRAction {
053: /**
054: * @param servletContext
055: * @throws ServletException
056: */
057: private DWRAction(ServletContext servletContext)
058: throws ServletException {
059: DispatcherUtils.initialize(servletContext);
060: wwDispatcher = DispatcherUtils.getInstance();
061: actionProcessor = loadActionProcessor(servletContext
062: .getInitParameter(DWRACTIONPROCESSOR_INIT_PARAM));
063: }
064:
065: /**
066: *
067: */
068: protected AjaxResult doExecute(ActionDefinition actionDefinition,
069: Map<String, String> params, HttpServletRequest request,
070: HttpServletResponse response, ServletContext servletContext)
071: throws ServletException {
072: FakeHttpServletResponse actionResponse = new FakeHttpServletResponse();
073:
074: if (null != actionProcessor) {
075: actionProcessor.preProcess(request, response,
076: actionResponse, params);
077: }
078:
079: wwDispatcher.prepare(request, actionResponse);
080:
081: ActionInvocation invocation = invokeAction(wwDispatcher,
082: request, actionResponse, servletContext,
083: actionDefinition, params);
084:
085: AjaxResult result;
086: if (actionDefinition.isExecuteResult()) {
087: // HINT: we have output string
088: result = getTextResult(actionResponse);
089: } else {
090: result = new DefaultAjaxDataResult(invocation.getAction());
091: }
092:
093: if (null != actionProcessor) {
094: actionProcessor.postProcess(request, response,
095: actionResponse, result);
096: }
097:
098: return result;
099: }
100:
101: /**
102: *
103: */
104: @SuppressWarnings("unchecked")
105: protected ActionInvocation invokeAction(DispatcherUtils du,
106: HttpServletRequest request, HttpServletResponse response,
107: ServletContext context, ActionDefinition actionDefinition,
108: Map<String, String> params) throws ServletException {
109: ActionMapping mapping = getActionMapping(actionDefinition,
110: params);
111: Map<String, Object> extraContext = du.createContextMap(request,
112: response, mapping, context);
113:
114: // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
115: OgnlValueStack stack = (OgnlValueStack) request
116: .getAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY);
117: if (null != stack) {
118: extraContext.put(ActionContext.VALUE_STACK,
119: new OgnlValueStack(stack));
120: }
121:
122: try {
123: prepareContinuationAction(request, extraContext);
124:
125: ActionProxy proxy = ActionProxyFactory.getFactory()
126: .createActionProxy(actionDefinition.getNamespace(),
127: actionDefinition.getAction(), extraContext,
128: actionDefinition.isExecuteResult(), false);
129: proxy.setMethod(actionDefinition.getMethod());
130: request.setAttribute(
131: ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy
132: .getInvocation().getStack());
133:
134: // if the ActionMapping says to go straight to a result, do it!
135: if (mapping.getResult() != null) {
136: Result result = mapping.getResult();
137: result.execute(proxy.getInvocation());
138: } else {
139: proxy.execute();
140: }
141:
142: return proxy.getInvocation();
143: } catch (ConfigurationException ce) {
144: throw new ServletException("Cannot invoke action '"
145: + actionDefinition.getAction() + "' in namespace '"
146: + actionDefinition.getNamespace() + "'", ce);
147: } catch (Exception e) {
148: throw new ServletException("Cannot invoke action '"
149: + actionDefinition.getAction() + "' in namespace '"
150: + actionDefinition.getNamespace() + "'", e);
151: } finally {
152: // If there was a previous value stack then set it back onto the request
153: if (null != stack) {
154: request.setAttribute(
155: ServletActionContext.WEBWORK_VALUESTACK_KEY,
156: stack);
157: }
158: }
159: }
160:
161: /**
162: *
163: */
164: @SuppressWarnings("unchecked")
165: protected void prepareContinuationAction(
166: HttpServletRequest request, Map<String, Object> extraContext) {
167: String id = request
168: .getParameter(XWorkContinuationConfig.CONTINUE_PARAM);
169: if (null != id) {
170: // remove the continue key from the params - we don't want to bother setting
171: // on the value stack since we know it won't work. Besides, this breaks devMode!
172: Map<String, String> params = (Map<String, String>) extraContext
173: .get(ActionContext.PARAMETERS);
174: params.remove(XWorkContinuationConfig.CONTINUE_PARAM);
175:
176: // and now put the key in the context to be picked up later by XWork
177: extraContext.put(XWorkContinuationConfig.CONTINUE_KEY, id);
178: }
179: }
180:
181: /**
182: *
183: */
184: protected ActionMapping getActionMapping(
185: ActionDefinition actionDefinition,
186: Map<String, String> params) {
187: ActionMapping actionMapping = new ActionMapping(
188: actionDefinition.getAction(), actionDefinition
189: .getNamespace(), actionDefinition.getMethod(),
190: params);
191: return actionMapping;
192: }
193:
194: /**
195: *
196: */
197: protected AjaxTextResult getTextResult(
198: FakeHttpServletResponse response) {
199: DefaultAjaxTextResult result = new DefaultAjaxTextResult();
200:
201: String text = null;
202: try {
203: text = response.getContentAsString();
204: } catch (UnsupportedEncodingException uee) {
205: log.warn("Cannot retrieve text output as string", uee);
206: }
207:
208: if (null == text) {
209: try {
210: text = response.getCharacterEncoding() != null ? new String(
211: response.getContentAsByteArray(), response
212: .getCharacterEncoding())
213: : new String(response.getContentAsByteArray());
214: } catch (UnsupportedEncodingException uee) {
215: log
216: .warn(
217: "Cannot retrieve text output as encoded byte array",
218: uee);
219: text = new String(response.getContentAsByteArray());
220: }
221: }
222:
223: result.setText(text);
224: return result;
225: }
226:
227: /**
228: * Entry point for all action invocations.
229: * @param actionDefinition the identification information for the action
230: * @param params action invocation parameters
231: * @param request original request
232: * @param response original response
233: * @param servletContext current <code>ServletContext</code>
234: * @return an <code>AjaxResult</code> wrapping invocation result
235: * @throws ServletException thrown if the initialization or invocation of the action fails
236: */
237: public static AjaxResult execute(ActionDefinition actionDefinition,
238: Map<String, String> params, HttpServletRequest request,
239: HttpServletResponse response, ServletContext servletContext)
240: throws ServletException {
241: initialize(servletContext);
242:
243: return instance.doExecute(actionDefinition, params, request,
244: response, servletContext);
245: }
246:
247: /**
248: * Performs the one time initialization of the singleton <code>DWRAction</code>.
249: * @param servletContext
250: * @throws ServletException thrown in case the singleton initialization fails
251: */
252: private static void initialize(ServletContext servletContext)
253: throws ServletException {
254: synchronized (DWRAction.class) {
255: if (null == instance) {
256: instance = new DWRAction(servletContext);
257: }
258: }
259: }
260:
261: /**
262: * Tries to instantiate an <code>IDWRActionProcessor</code> if defined in web.xml.
263: * @param actionProcessorClassName
264: * @return an instance of <code>IDWRActionProcessor</code> if the init-param is defined or <code>null</code>
265: * @throws ServletException thrown if the <code>IDWRActionProcessor</code> cannot be loaded and instantiated
266: */
267: private static IDWRActionProcessor loadActionProcessor(
268: String actionProcessorClassName) throws ServletException {
269: if (null == actionProcessorClassName
270: || "".equals(actionProcessorClassName)) {
271: return null;
272: }
273:
274: IDWRActionProcessor reply = LocalUtil.classNewInstance(
275: "DWRActionProcessor", actionProcessorClassName,
276: IDWRActionProcessor.class);
277: if (reply == null) {
278: throw new ServletException(
279: "Cannot load DWRActionProcessor class '"
280: + actionProcessorClassName + "'");
281: }
282:
283: return reply;
284: }
285:
286: /**
287: * The log stream
288: */
289: private static final Log log = LogFactory.getLog(DWRAction.class);
290:
291: private static final String DWRACTIONPROCESSOR_INIT_PARAM = "dwrActionProcessor";
292:
293: private static DWRAction instance;
294:
295: private DispatcherUtils wwDispatcher;
296:
297: private IDWRActionProcessor actionProcessor;
298: }
|