Commits

tm_jee  committed 25daa4d

WW-1458
- Support Freemarker template_exception_handler=rethrow
(Thanks to Martin and Jasper)

git-svn-id: http://svn.opensymphony.com/svn/webwork/trunk@2911573baa09-0c28-0410-bef9-dab3c582ae83

  • Participants
  • Parent commits fa43a91

Comments (0)

Files changed (2)

File src/java/com/opensymphony/webwork/views/freemarker/FreemarkerResult.java

 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
+import java.io.CharArrayWriter;
 import java.io.IOException;
 import java.io.Writer;
 import java.util.Locale;
     protected Configuration configuration;
     protected ObjectWrapper wrapper;
 
+    protected boolean writeCompleted = false;
+    
     /*
      * webwork results are constructed for each result execeution
      *
     public String getContentType() {
         return pContentType;
     }
+    
+    public void setWriteCompleted(boolean writeCompleted) {
+    	this.writeCompleted = writeCompleted;
+    }
+    
+    /**
+     * Allow customization of either (when true) to write result to response stream/writer
+     * only when everything is ok (without exception) or otherwise. This is usefull 
+     * when using Freemarker's "rethrow" exception handler, where we don't want
+     * partial of the page to be writen and then exception occurred and we get 
+     * freemarker's "rethrow" exception handler to take over but its too late since 
+     * part of the response has already been 'commited' to the stream/writer.
+     * 
+     * @return boolean
+     */
+    public boolean getWriteCompleted() {
+    	return writeCompleted;
+    }
+    
 
     /**
      * Execute this result, using the specified template location.
         this.invocation = invocation;
         this.configuration = getConfiguration();
         this.wrapper = getObjectWrapper();
-
+        
         if (!location.startsWith("/")) {
             ActionContext ctx = invocation.getInvocationContext();
             HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
                     // This can happen on some application servers such as WebLogic 8.1
                     useOutputStream = true;
                 }
-                if (useOutputStream) {
+                if (useOutputStream) { 
+                	// If we are here, we don't have the issue of WW-1458, since 
+                	// we are already writing through a temporary buffer.
+                	
                     // Use a StringWriter as a buffer to write the template output to
                     writer = new java.io.StringWriter();
                     template.process(model, writer);
+                    writer.flush();
+                    
                     // Then write the contents of the writer to the OutputStream
                     java.io.OutputStream os = ServletActionContext.getResponse().getOutputStream();
                     os.write(writer.toString().getBytes());
                 }
                 else {
                     // Process the template with the normal writer since it was available
-                    template.process(model, writer);
+                	
+                	// WW-1458
+                	// Allow customization of either (when true) to write result to response stream/writer
+                    // only when everything is ok (without exception) or otherwise. This is usefull 
+                    // when using Freemarker's "rethrow" exception handler, where we don't want
+                    // partial of the page to be writen and then exception occurred and we get 
+                    // freemarker's "rethrow" exception handler to take over but its too late since 
+                    // part of the response has already been 'commited' to the stream/writer.
+                	if (configuration.getTemplateExceptionHandler() == TemplateExceptionHandler.RETHROW_HANDLER || 
+                			getWriteCompleted()) {
+                		CharArrayWriter tempBuffer = new CharArrayWriter();
+                		template.process(model, tempBuffer);
+                		tempBuffer.flush();
+                		
+                		tempBuffer.writeTo(writer);
+                	}
+                	else {
+                		template.process(model, writer);
+                	}
                 }
             } finally {
                 // Give subclasses a chance to hook into postprocessing

File src/test/com/opensymphony/webwork/views/freemarker/FreemarkerResultTest.java

+/*
+ * Copyright (c) 2002-2007 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.views.freemarker;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+import com.opensymphony.webwork.WebWorkTestCase;
+import com.opensymphony.xwork.mock.MockActionInvocation;
+
+import freemarker.template.Configuration;
+import freemarker.template.SimpleHash;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ * @author tmjee
+ * @version $Date$ $Id$
+ */
+public class FreemarkerResultTest extends WebWorkTestCase {
+
+	private boolean postTemplateProcessCalled = false;
+	private boolean preTemplateProcessCalled = false;
+	
+	/**
+	 * This is just a very simple sanity test just to make sure that with 
+	 * writeCompleted=true, we still get the normal output writen.
+	 * @throws Exception
+	 */
+	public void testFreemarkerResultMakeSureWeGetTheDesiredResultWhenWriteCompleteIsTrue() throws Exception {
+		preTemplateProcessCalled = false;
+		postTemplateProcessCalled = false;
+		final StringWriter writer = new StringWriter();
+		
+		FreemarkerResult result = new FreemarkerResult() {
+			protected Configuration getConfiguration() throws TemplateException {
+				return new Configuration() {
+					public Template getTemplate(String arg0, Locale locale) throws IOException {
+						return new Template("test.ftl", new StringReader("testing")) {
+						};
+					}
+				};
+			}
+			
+			protected Writer getWriter() throws IOException {
+				return writer;
+			}
+			
+			protected boolean preTemplateProcess(Template template, TemplateModel model) throws IOException, TemplateException {
+				preTemplateProcessCalled = true;
+				return true;
+			}
+			
+			protected void postTemplateProcess(Template template, TemplateModel data) throws IOException {
+				postTemplateProcessCalled = true;
+			}
+			
+			protected TemplateModel createModel() throws TemplateModelException {
+				return new SimpleHash();
+			}
+		};
+		
+		result.setWriteCompleted(true);
+		result.doExecute("/test.ftl", new MockActionInvocation());
+		
+		assertEquals(writer.getBuffer().toString(), "testing");
+		assertTrue(preTemplateProcessCalled);
+		assertTrue(postTemplateProcessCalled);
+	}
+	
+	/**
+	 * This is just a very simple sanity test just to make sure that with 
+	 * writeCompleted=false, we still get the normal output writen.
+	 * @throws Exception
+	 */
+	public void testFreemarkerResultMakeSureWeGetTheDesiredResultWhenWriteCompleteIsFalse() throws Exception {
+		preTemplateProcessCalled = false;
+		postTemplateProcessCalled = false;
+		final StringWriter writer = new StringWriter();
+		
+		FreemarkerResult result = new FreemarkerResult() {
+			protected Configuration getConfiguration() throws TemplateException {
+				return new Configuration() {
+					public Template getTemplate(String arg0, Locale locale) throws IOException {
+						return new Template("test.ftl", new StringReader("testing")) {
+						};
+					}
+				};
+			}
+			
+			protected Writer getWriter() throws IOException {
+				return writer;
+			}
+			
+			protected boolean preTemplateProcess(Template template, TemplateModel model) throws IOException, TemplateException {
+				preTemplateProcessCalled = true;
+				return true;
+			}
+			
+			protected void postTemplateProcess(Template template, TemplateModel data) throws IOException {
+				postTemplateProcessCalled = true;
+			}
+			
+			protected TemplateModel createModel() throws TemplateModelException {
+				return new SimpleHash();
+			}
+		};
+		
+		result.setWriteCompleted(false);
+		result.doExecute("/test.ftl", new MockActionInvocation());
+		
+		assertEquals(writer.getBuffer().toString(), "testing");
+		assertTrue(preTemplateProcessCalled);
+		assertTrue(postTemplateProcessCalled);
+	}
+}