diff --git a/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java b/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java index e8e2caa87..3453d8977 100644 --- a/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java +++ b/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java @@ -117,6 +117,7 @@ public class FreemarkerResult extends StrutsResultSupport { */ protected String location; private String pContentType = "text/html"; + private static final String PARENT_TEMPLATE_WRITER = FreemarkerResult.class.getName() + ".parentWriter"; public FreemarkerResult() { super(); @@ -158,9 +159,10 @@ public class FreemarkerResult extends StrutsResultSupport { this.configuration = getConfiguration(); this.wrapper = getObjectWrapper(); + ActionContext ctx = invocation.getInvocationContext(); + HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST); + if (!locationArg.startsWith("/")) { - ActionContext ctx = invocation.getInvocationContext(); - HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST); String base = ResourceUtil.getResourceBase(req); locationArg = base + "/" + locationArg; } @@ -174,14 +176,27 @@ public class FreemarkerResult extends StrutsResultSupport { // Process the template Writer writer = getWriter(); if (isWriteIfCompleted() || configuration.getTemplateExceptionHandler() == TemplateExceptionHandler.RETHROW_HANDLER) { - CharArrayWriter charArrayWriter = new CharArrayWriter(); + CharArrayWriter parentCharArrayWriter = (CharArrayWriter) req.getAttribute(PARENT_TEMPLATE_WRITER); + boolean isTopTemplate = false; + if (isTopTemplate = (parentCharArrayWriter == null)) { + //this is the top template + parentCharArrayWriter = new CharArrayWriter(); + //set it in the request because when the "action" tag is used a new VS and ActionContext is created + req.setAttribute(PARENT_TEMPLATE_WRITER, parentCharArrayWriter); + } + try { - template.process(model, charArrayWriter); - charArrayWriter.flush(); - charArrayWriter.writeTo(writer); + template.process(model, parentCharArrayWriter); + + if (isTopTemplate) { + parentCharArrayWriter.flush(); + parentCharArrayWriter.writeTo(writer); + } } finally { - if (charArrayWriter != null) - charArrayWriter.close(); + if (isTopTemplate && parentCharArrayWriter != null) { + req.removeAttribute(PARENT_TEMPLATE_WRITER); + parentCharArrayWriter.close(); + } } } else { template.process(model, writer); diff --git a/core/src/test/java/org/apache/struts2/views/freemarker/FreeMarkerResultTest.java b/core/src/test/java/org/apache/struts2/views/freemarker/FreeMarkerResultTest.java index 59c8c4a59..1d9c6595f 100644 --- a/core/src/test/java/org/apache/struts2/views/freemarker/FreeMarkerResultTest.java +++ b/core/src/test/java/org/apache/struts2/views/freemarker/FreeMarkerResultTest.java @@ -30,15 +30,26 @@ import junit.framework.TestCase; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsStatics; import org.apache.struts2.StrutsTestCase; +import org.apache.struts2.dispatcher.Dispatcher; +import org.apache.struts2.dispatcher.mapper.ActionMapper; +import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.views.jsp.StrutsMockHttpServletResponse; import org.apache.struts2.views.jsp.StrutsMockServletContext; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockServletContext; +import org.springframework.core.io.DefaultResourceLoader; +import org.easymock.EasyMock; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.mock.MockActionInvocation; import com.opensymphony.xwork2.util.ValueStack; import com.opensymphony.xwork2.util.ValueStackFactory; +import javax.servlet.ServletContext; + +import freemarker.template.TemplateExceptionHandler; +import freemarker.template.Configuration; + /** * Test case for FreeMarkerResult. * @@ -55,6 +66,58 @@ public class FreeMarkerResultTest extends StrutsTestCase { private FreemarkerManager mgr; private MockHttpServletRequest request; + public void testActionThatThrowsExceptionTag() throws Exception { + //get fm config to use it in mock servlet context + FreemarkerManager freemarkerManager = container.getInstance(FreemarkerManager.class); + Configuration freemarkerConfig = freemarkerManager.getConfiguration(ServletActionContext.getServletContext()); + freemarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + + ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); + File file = new File(FreeMarkerResultTest.class.getResource("callActionFreeMarker2.ftl").toURI()); + EasyMock.expect(servletContext.getRealPath("/tutorial/org/apache/struts2/views/freemarker/callActionFreeMarker.ftl")).andReturn(file.getAbsolutePath()); + file = new File(FreeMarkerResultTest.class.getResource("nested.ftl").toURI()); + EasyMock.expect(servletContext.getRealPath("/tutorial/org/apache/struts2/views/freemarker/nested.ftl")).andReturn(file.getAbsolutePath()); + EasyMock.expect(servletContext.getAttribute(FreemarkerManager.CONFIG_SERVLET_CONTEXT_KEY)).andReturn(freemarkerConfig).anyTimes(); + EasyMock.replay(servletContext); + + freemarkerConfig.setServletContextForTemplateLoading(servletContext, null); + ServletActionContext.setServletContext(servletContext); + + + request.setRequestURI("/tutorial/test2.action"); + Dispatcher dispatcher = Dispatcher.getInstance(); + ActionMapping mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping( + request, dispatcher.getConfigurationManager()); + dispatcher.serviceAction(request, response, servletContext, mapping); + assertEquals("beforenestedafter", stringWriter.toString()); + } + + public void testActionThatSucceedsTag() throws Exception { + //get fm config to use it in mock servlet context + FreemarkerManager freemarkerManager = container.getInstance(FreemarkerManager.class); + Configuration freemarkerConfig = freemarkerManager.getConfiguration(ServletActionContext.getServletContext()); + freemarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + + ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); + File file = new File(FreeMarkerResultTest.class.getResource("callActionFreeMarker2.ftl").toURI()); + EasyMock.expect(servletContext.getRealPath("/tutorial/org/apache/struts2/views/freemarker/callActionFreeMarker2.ftl")).andReturn(file.getAbsolutePath()); + file = new File(FreeMarkerResultTest.class.getResource("nested.ftl").toURI()); + EasyMock.expect(servletContext.getRealPath("/tutorial/org/apache/struts2/views/freemarker/nested.ftl")).andReturn(file.getAbsolutePath()); + EasyMock.expect(servletContext.getAttribute(FreemarkerManager.CONFIG_SERVLET_CONTEXT_KEY)).andReturn(freemarkerConfig).anyTimes(); + EasyMock.replay(servletContext); + + freemarkerConfig.setServletContextForTemplateLoading(servletContext, null); + ServletActionContext.setServletContext(servletContext); + + + request.setRequestURI("/tutorial/test5.action"); + Dispatcher dispatcher = Dispatcher.getInstance(); + ActionMapping mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping( + request, dispatcher.getConfigurationManager()); + dispatcher.serviceAction(request, response, servletContext, mapping); + assertEquals("beforenestedafter", stringWriter.toString()); + } + public void testWriteIfCompleted() throws Exception { FreemarkerResult result = new FreemarkerResult(); result.setLocation("someFreeMarkerFile.ftl"); diff --git a/core/src/test/resources/org/apache/struts2/views/freemarker/callActionFreeMarker.ftl b/core/src/test/resources/org/apache/struts2/views/freemarker/callActionFreeMarker.ftl new file mode 100644 index 000000000..a36b3bd79 --- /dev/null +++ b/core/src/test/resources/org/apache/struts2/views/freemarker/callActionFreeMarker.ftl @@ -0,0 +1,23 @@ +<#-- +/* + * $Id: someFreeMarkerFile.ftl 590812 2007-10-31 20:32:54Z apetrelli $ + * + * 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. + */ +--> +before<@s.action namespace="/tutorial" name="test4" executeResult="true" rethrowException="true"/>after \ No newline at end of file diff --git a/core/src/test/resources/org/apache/struts2/views/freemarker/callActionFreeMarker2.ftl b/core/src/test/resources/org/apache/struts2/views/freemarker/callActionFreeMarker2.ftl new file mode 100644 index 000000000..de83b535f --- /dev/null +++ b/core/src/test/resources/org/apache/struts2/views/freemarker/callActionFreeMarker2.ftl @@ -0,0 +1,23 @@ +<#-- +/* + * $Id: someFreeMarkerFile.ftl 590812 2007-10-31 20:32:54Z apetrelli $ + * + * 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. + */ +--> +before<@s.action namespace="/tutorial" name="test3" executeResult="true" rethrowException="true"/>after \ No newline at end of file diff --git a/core/src/test/resources/org/apache/struts2/views/freemarker/nested.ftl b/core/src/test/resources/org/apache/struts2/views/freemarker/nested.ftl new file mode 100644 index 000000000..f8fc66dcf --- /dev/null +++ b/core/src/test/resources/org/apache/struts2/views/freemarker/nested.ftl @@ -0,0 +1,23 @@ +<#-- +/* + * $Id: someFreeMarkerFile.ftl 590812 2007-10-31 20:32:54Z apetrelli $ + * + * 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. + */ +--> +nested \ No newline at end of file diff --git a/core/src/test/resources/struts.xml b/core/src/test/resources/struts.xml index 2d75c86f0..6ec158e8b 100644 --- a/core/src/test/resources/struts.xml +++ b/core/src/test/resources/struts.xml @@ -40,6 +40,29 @@ sitegraph/guess-input.ftl + + + + org/apache/struts2/views/freemarker/callActionFreeMarker.ftl + + + + + org/apache/struts2/views/freemarker/nested.ftl + + + + + org/apache/struts2/views/freemarker/nested.ftl + + + + + + org/apache/struts2/views/freemarker/callActionFreeMarker2.ftl + + +