Author: nilsga Date: Sat Aug 11 07:31:18 2007 New Revision: 564926 URL: http://svn.apache.org/viewvc?view=rev&rev=564926 Log: WW-2104 Added experimental support for intermediate dispatcher servlet allowing for wrapping the request to the JSP in a StrutsWrapperRequest
Added: struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/DispatcherServlet.java Modified: struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletResult.java struts/struts2/trunk/plugins/portlet/src/main/resources/struts-plugin.xml struts/struts2/trunk/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java Modified: struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java?view=diff&rev=564926&r1=564925&r2=564926 ============================================================================== --- struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java (original) +++ struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java Sat Aug 11 07:31:18 2007 @@ -20,6 +20,8 @@ */ package org.apache.struts2.portlet; +import org.apache.struts2.portlet.dispatcher.DispatcherServlet; + /** * Interface defining some constants used in the Struts portlet implementation * @@ -113,4 +115,9 @@ * Key for session attribute indicating the location of the render direct action. */ String RENDER_DIRECT_LOCATION = "struts.portlet.renderDirectLocation"; + + /** + * Key for the dispatch instruction for the [EMAIL PROTECTED] DispatcherServlet} + */ + String DISPATCH_TO = "struts.portlet.dispatchTo"; } Added: struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/DispatcherServlet.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/DispatcherServlet.java?view=auto&rev=564926 ============================================================================== --- struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/DispatcherServlet.java (added) +++ struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/dispatcher/DispatcherServlet.java Sat Aug 11 07:31:18 2007 @@ -0,0 +1,57 @@ +/* + * $Id: $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.portlet.dispatcher; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.struts2.dispatcher.StrutsRequestWrapper; +import org.apache.struts2.portlet.PortletActionConstants; + +import com.opensymphony.xwork2.util.TextUtils; + +public class DispatcherServlet extends HttpServlet implements PortletActionConstants { + + private static final long serialVersionUID = -266147033645951967L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String dispatchTo = (String) request.getAttribute(DISPATCH_TO); + HttpServletRequest wrapper = wrapRequestIfNecessary(request); + if(TextUtils.stringSet(dispatchTo)) { + request.getRequestDispatcher(dispatchTo).include(wrapper, response); + } + } + + private HttpServletRequest wrapRequestIfNecessary(HttpServletRequest request) { + if(!(request instanceof StrutsRequestWrapper)) { + return new StrutsRequestWrapper(request); + } + else { + return request; + } + } + +} Modified: struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletResult.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletResult.java?view=diff&rev=564926&r1=564925&r2=564926 ============================================================================== --- struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletResult.java (original) +++ struts/struts2/trunk/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletResult.java Sat Aug 11 07:31:18 2007 @@ -25,7 +25,7 @@ import java.util.StringTokenizer; import javax.portlet.ActionResponse; -import javax.portlet.PortletConfig; +import javax.portlet.PortletContext; import javax.portlet.PortletException; import javax.portlet.PortletRequestDispatcher; import javax.portlet.RenderRequest; @@ -43,192 +43,175 @@ import org.apache.struts2.portlet.context.PortletActionContext; import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.util.TextUtils; /** * Result type that includes a JSP to render. - * + * */ public class PortletResult extends StrutsResultSupport implements PortletActionConstants { - private static final long serialVersionUID = 434251393926178567L; + private static final long serialVersionUID = 434251393926178567L; - /** - * Logger instance. - */ - private static final Log LOG = LogFactory.getLog(PortletResult.class); - - private String contentType = "text/html"; - - private String title; - - public PortletResult() { - super(); - } - - public PortletResult(String location) { - super(location); - } - - /** - * Execute the result. Obtains the - * [EMAIL PROTECTED] javax.portlet.PortletRequestDispatcher}from the - * [EMAIL PROTECTED] PortletActionContext}and includes the JSP. - * - * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation) - */ - public void doExecute(String finalLocation, - ActionInvocation actionInvocation) throws Exception { - - if (PortletActionContext.isRender()) { - executeRenderResult(finalLocation); - } else if (PortletActionContext.isEvent()) { - executeActionResult(finalLocation, actionInvocation); - } else { - executeRegularServletResult(finalLocation, actionInvocation); - } - } - - /** - * Executes the regular servlet result. - * - * @param finalLocation - * @param actionInvocation - */ - private void executeRegularServletResult(String finalLocation, - ActionInvocation actionInvocation) throws ServletException, IOException { - ServletContext ctx = ServletActionContext.getServletContext(); - HttpServletRequest req = ServletActionContext.getRequest(); - HttpServletResponse res = ServletActionContext.getResponse(); - try { - ctx.getRequestDispatcher(finalLocation).include(req, res); - } catch (ServletException e) { - LOG.error("ServletException including " + finalLocation, e); - throw e; - } catch (IOException e) { - LOG.error("IOException while including result '" + finalLocation + "'", e); - throw e; - } - } - - /** - * Executes the action result. - * - * @param finalLocation - * @param invocation - */ - protected void executeActionResult(String finalLocation, - ActionInvocation invocation) { - LOG.debug("Executing result in Event phase"); - ActionResponse res = PortletActionContext.getActionResponse(); - LOG.debug("Setting event render parameter: " + finalLocation); - if (finalLocation.indexOf('?') != -1) { - convertQueryParamsToRenderParams(res, finalLocation - .substring(finalLocation.indexOf('?') + 1)); - finalLocation = finalLocation.substring(0, finalLocation - .indexOf('?')); - } - if (finalLocation.endsWith(".action")) { - // View is rendered with a view action...luckily... - finalLocation = finalLocation.substring(0, finalLocation - .lastIndexOf(".")); - res.setRenderParameter(ACTION_PARAM, finalLocation); - } else { - // View is rendered outside an action...uh oh... - res.setRenderParameter(ACTION_PARAM, "renderDirect"); - Map sessionMap = invocation.getInvocationContext().getSession(); - sessionMap.put(RENDER_DIRECT_LOCATION, finalLocation); - } - res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext - .getRequest().getPortletMode().toString()); - } - - /** - * Converts the query params to render params. - * - * @param response - * @param queryParams - */ - protected static void convertQueryParamsToRenderParams( - ActionResponse response, String queryParams) { - StringTokenizer tok = new StringTokenizer(queryParams, "&"); - while (tok.hasMoreTokens()) { - String token = tok.nextToken(); - String key = token.substring(0, token.indexOf('=')); - String value = token.substring(token.indexOf('=') + 1); - response.setRenderParameter(key, value); - } - } - - /** - * Executes the render result. - * - * @param finalLocation - * @throws PortletException - * @throws IOException - */ - protected void executeRenderResult(final String finalLocation) throws PortletException, IOException { - LOG.debug("Executing result in Render phase"); - PortletConfig cfg = PortletActionContext.getPortletConfig(); - RenderRequest req = PortletActionContext.getRenderRequest(); - RenderResponse res = PortletActionContext.getRenderResponse(); - LOG.debug("PortletConfig: " + cfg); - LOG.debug("RenderRequest: " + req); - LOG.debug("RenderResponse: " + res); - res.setContentType(contentType); - if (TextUtils.stringSet(title)) { - res.setTitle(title); - } - LOG.debug("Location: " + finalLocation); - PortletRequestDispatcher dispatcher = cfg.getPortletContext().getRequestDispatcher(finalLocation); - if(dispatcher == null) { - throw new PortletException("Could not locate dispatcher for '" + finalLocation + "'"); - } - new IncludeTemplate() { - protected void when(PortletException e) { - LOG.error("PortletException while dispatching to '" + finalLocation + "'"); - } - protected void when(IOException e) { - LOG.error("IOException while dispatching to '" + finalLocation + "'"); - } - }.include(dispatcher, req, res); - } - - /** - * Sets the content type. - * - * @param contentType The content type to set. - */ - public void setContentType(String contentType) { - this.contentType = contentType; - } - - /** - * Sets the title. - * - * @param title The title to set. - */ - public void setTitle(String title) { - this.title = title; - } - - static class IncludeTemplate { - protected void include(PortletRequestDispatcher dispatcher, RenderRequest req, RenderResponse res) throws PortletException, IOException{ - try { - dispatcher.include(req, res); - } - catch(PortletException e) { - when(e); - throw e; - } - catch(IOException e) { - when(e); - throw e; - } - } + private boolean useDispatcherServlet; - protected void when(PortletException e) {} - - protected void when(IOException e) {} - } + /** + * Logger instance. + */ + private static final Log LOG = LogFactory.getLog(PortletResult.class); + + private String contentType = "text/html"; + + private String title; + + public PortletResult() { + super(); + } + + public PortletResult(String location) { + super(location); + } + + /** + * Execute the result. Obtains the + * [EMAIL PROTECTED] javax.portlet.PortletRequestDispatcher}from the + * [EMAIL PROTECTED] PortletActionContext}and includes the JSP. + * + * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation) + */ + public void doExecute(String finalLocation, ActionInvocation actionInvocation) throws Exception { + + if (PortletActionContext.isRender()) { + executeRenderResult(finalLocation); + } else if (PortletActionContext.isEvent()) { + executeActionResult(finalLocation, actionInvocation); + } else { + executeRegularServletResult(finalLocation, actionInvocation); + } + } + + /** + * Executes the regular servlet result. + * + * @param finalLocation + * @param actionInvocation + */ + private void executeRegularServletResult(String finalLocation, ActionInvocation actionInvocation) + throws ServletException, IOException { + ServletContext ctx = ServletActionContext.getServletContext(); + HttpServletRequest req = ServletActionContext.getRequest(); + HttpServletResponse res = ServletActionContext.getResponse(); + try { + ctx.getRequestDispatcher(finalLocation).include(req, res); + } catch (ServletException e) { + LOG.error("ServletException including " + finalLocation, e); + throw e; + } catch (IOException e) { + LOG.error("IOException while including result '" + finalLocation + "'", e); + throw e; + } + } + + /** + * Executes the action result. + * + * @param finalLocation + * @param invocation + */ + protected void executeActionResult(String finalLocation, ActionInvocation invocation) { + LOG.debug("Executing result in Event phase"); + ActionResponse res = PortletActionContext.getActionResponse(); + LOG.debug("Setting event render parameter: " + finalLocation); + if (finalLocation.indexOf('?') != -1) { + convertQueryParamsToRenderParams(res, finalLocation.substring(finalLocation.indexOf('?') + 1)); + finalLocation = finalLocation.substring(0, finalLocation.indexOf('?')); + } + if (finalLocation.endsWith(".action")) { + // View is rendered with a view action...luckily... + finalLocation = finalLocation.substring(0, finalLocation.lastIndexOf(".")); + res.setRenderParameter(ACTION_PARAM, finalLocation); + } else { + // View is rendered outside an action...uh oh... + res.setRenderParameter(ACTION_PARAM, "renderDirect"); + Map sessionMap = invocation.getInvocationContext().getSession(); + sessionMap.put(RENDER_DIRECT_LOCATION, finalLocation); + } + res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext.getRequest().getPortletMode() + .toString()); + } + + /** + * Converts the query params to render params. + * + * @param response + * @param queryParams + */ + protected static void convertQueryParamsToRenderParams(ActionResponse response, String queryParams) { + StringTokenizer tok = new StringTokenizer(queryParams, "&"); + while (tok.hasMoreTokens()) { + String token = tok.nextToken(); + String key = token.substring(0, token.indexOf('=')); + String value = token.substring(token.indexOf('=') + 1); + response.setRenderParameter(key, value); + } + } + + /** + * Executes the render result. + * + * @param finalLocation + * @throws PortletException + * @throws IOException + */ + protected void executeRenderResult(final String finalLocation) throws PortletException, IOException { + LOG.debug("Executing result in Render phase"); + PortletContext ctx = PortletActionContext.getPortletContext(); + RenderRequest req = PortletActionContext.getRenderRequest(); + RenderResponse res = PortletActionContext.getRenderResponse(); + res.setContentType(contentType); + if (TextUtils.stringSet(title)) { + res.setTitle(title); + } + LOG.debug("Location: " + finalLocation); + if (useDispatcherServlet) { + req.setAttribute(DISPATCH_TO, finalLocation); + PortletRequestDispatcher dispatcher = ctx.getNamedDispatcher("dispatcherServlet"); + if(dispatcher == null) { + throw new PortletException("Could not locate dispatcherServlet. Please configure it in your web.xml file"); + } + dispatcher.include(req, res); + } else { + PortletRequestDispatcher dispatcher = ctx.getRequestDispatcher(finalLocation); + if (dispatcher == null) { + throw new PortletException("Could not locate dispatcher for '" + finalLocation + "'"); + } + dispatcher.include(req, res); + } + } + + /** + * Sets the content type. + * + * @param contentType + * The content type to set. + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * Sets the title. + * + * @param title + * The title to set. + */ + public void setTitle(String title) { + this.title = title; + } + + @Inject("struts.portlet.useDispatcherServlet") + public void setUseDispatcherServlet(String useDispatcherServlet) { + this.useDispatcherServlet = "true".equalsIgnoreCase(useDispatcherServlet); + } } Modified: struts/struts2/trunk/plugins/portlet/src/main/resources/struts-plugin.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/portlet/src/main/resources/struts-plugin.xml?view=diff&rev=564926&r1=564925&r2=564926 ============================================================================== --- struts/struts2/trunk/plugins/portlet/src/main/resources/struts-plugin.xml (original) +++ struts/struts2/trunk/plugins/portlet/src/main/resources/struts-plugin.xml Sat Aug 11 07:31:18 2007 @@ -30,6 +30,7 @@ <bean type="org.apache.struts2.components.UrlRenderer" name="portlet" class="org.apache.struts2.components.PortletUrlRenderer"/> <constant name="struts.urlRenderer" value="portlet" /> + <constant name="struts.portlet.useDispatcherServlet" value="false" /> <package name="struts-portlet-default" extends="struts-default"> @@ -42,16 +43,24 @@ <interceptors> <interceptor name="portletAware" class="org.apache.struts2.portlet.interceptor.PortletAwareInterceptor"/> <interceptor name="portletState" class="org.apache.struts2.portlet.interceptor.PortletStateInterceptor"/> + <!-- Default stack for operating in portlet environment --> <interceptor-stack name="portletDefaultStack"> <interceptor-ref name="portletState"/> <interceptor-ref name="defaultStack"/> - <interceptor-ref name="portletAware" /> + <interceptor-ref name="portletAware"/> </interceptor-stack> - + <!-- Extension of the default portlet stack which also includes the token interceptor --> + <interceptor-stack name="portletDefaultStackWithToken"> + <interceptor-ref name="portletState"/> + <interceptor-ref name="token"/> + <interceptor-ref name="defaultStack"/> + <interceptor-ref name="portletAware"/> + </interceptor-stack> + </interceptors> <default-interceptor-ref name="portletDefaultStack"/> - + <action name="renderDirect" class="org.apache.struts2.portlet.dispatcher.DirectRenderFromEventAction"> <result name="success">${location}</result> </action> Modified: struts/struts2/trunk/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java?view=diff&rev=564926&r1=564925&r2=564926 ============================================================================== --- struts/struts2/trunk/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java (original) +++ struts/struts2/trunk/plugins/portlet/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java Sat Aug 11 07:31:18 2007 @@ -35,6 +35,7 @@ import junit.textui.TestRunner; import org.apache.struts2.StrutsConstants; +import org.apache.struts2.StrutsStatics; import org.apache.struts2.portlet.PortletActionConstants; import org.jmock.Mock; import org.jmock.cglib.MockObjectTestCase; @@ -56,19 +57,15 @@ public void setUp() throws Exception { super.setUp(); mockInvocation = mock(ActionInvocation.class); - mockConfig = mock(PortletConfig.class); mockCtx = mock(PortletContext.class); - mockConfig.stubs().method(ANYTHING); - mockConfig.stubs().method("getPortletContext").will(returnValue(mockCtx.proxy())); - Map paramMap = new HashMap(); Map sessionMap = new HashMap(); Map context = new HashMap(); context.put(ActionContext.SESSION, sessionMap); context.put(ActionContext.PARAMETERS, paramMap); - context.put(PortletActionConstants.PORTLET_CONFIG, mockConfig.proxy()); + context.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, mockCtx.proxy()); ActionContext.setContext(new ActionContext(context)); @@ -84,7 +81,6 @@ RenderRequest req = (RenderRequest)mockRequest.proxy(); RenderResponse res = (RenderResponse)mockResponse.proxy(); PortletRequestDispatcher rd = (PortletRequestDispatcher)mockRd.proxy(); - PortletConfig cfg = (PortletConfig)mockConfig.proxy(); PortletContext ctx = (PortletContext)mockCtx.proxy(); ActionInvocation inv = (ActionInvocation)mockInvocation.proxy(); @@ -92,14 +88,13 @@ mockRd.expects(once()).method("include").with(params); mockCtx.expects(once()).method("getRequestDispatcher").with(eq("/WEB-INF/pages/testPage.jsp")).will(returnValue(rd)); mockResponse.expects(once()).method("setContentType").with(eq("text/html")); - mockConfig.expects(once()).method("getPortletContext").will(returnValue(ctx)); mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW)); ActionContext ctxMap = ActionContext.getContext(); ctxMap.put(PortletActionConstants.RESPONSE, res); ctxMap.put(PortletActionConstants.REQUEST, req); - ctxMap.put(PortletActionConstants.PORTLET_CONFIG, cfg); + ctxMap.put(StrutsStatics.SERVLET_CONTEXT, ctx); ctxMap.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE); PortletResult result = new PortletResult(); @@ -208,20 +203,18 @@ RenderRequest req = (RenderRequest)mockRequest.proxy(); RenderResponse res = (RenderResponse)mockResponse.proxy(); PortletRequestDispatcher rd = (PortletRequestDispatcher)mockRd.proxy(); - PortletConfig cfg = (PortletConfig)mockConfig.proxy(); PortletContext ctx = (PortletContext)mockCtx.proxy(); Constraint[] params = new Constraint[]{same(req), same(res)}; mockRd.expects(once()).method("include").with(params); mockCtx.expects(once()).method("getRequestDispatcher").with(eq("/WEB-INF/pages/testPage.jsp")).will(returnValue(rd)); - mockConfig.expects(once()).method("getPortletContext").will(returnValue(ctx)); mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW)); ActionContext ctxMap = ActionContext.getContext(); ctxMap.put(PortletActionConstants.RESPONSE, res); ctxMap.put(PortletActionConstants.REQUEST, req); - ctxMap.put(PortletActionConstants.PORTLET_CONFIG, cfg); + ctxMap.put(StrutsStatics.SERVLET_CONTEXT, ctx); ctxMap.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE); mockResponse.expects(atLeastOnce()).method("setTitle").with(eq("testTitle"));