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
+
+
+