SLING-11486 - Added the support for a Check-for-Closed Test Writer
diff --git a/src/main/java/org/apache/sling/servlethelpers/MockSlingHttpServletResponse.java b/src/main/java/org/apache/sling/servlethelpers/MockSlingHttpServletResponse.java
index 2f6d9f7..d9fc329 100644
--- a/src/main/java/org/apache/sling/servlethelpers/MockSlingHttpServletResponse.java
+++ b/src/main/java/org/apache/sling/servlethelpers/MockSlingHttpServletResponse.java
@@ -39,6 +39,8 @@
 
     static final String CHARSET_SEPARATOR = ";charset=";
 
+    private boolean enableCheckForClosedWriter = false;
+
     private String contentType;
     private String characterEncoding;
     private int contentLength;
@@ -51,6 +53,10 @@
     private final ResponseBodySupport bodySupport = new ResponseBodySupport();
     private final CookieSupport cookieSupport = new CookieSupport();
 
+    public void setEnableCheckForClosedWriter(boolean enableCheckForClosedWriter) {
+        bodySupport.setEnableCheckForClosedWriter(enableCheckForClosedWriter);
+    }
+
     @Override
     public String getContentType() {
         if (this.contentType == null) {
diff --git a/src/main/java/org/apache/sling/servlethelpers/ResponseBodySupport.java b/src/main/java/org/apache/sling/servlethelpers/ResponseBodySupport.java
index e40bdfe..aaf8eda 100644
--- a/src/main/java/org/apache/sling/servlethelpers/ResponseBodySupport.java
+++ b/src/main/java/org/apache/sling/servlethelpers/ResponseBodySupport.java
@@ -23,6 +23,7 @@
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
+import java.util.Locale;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.WriteListener;
@@ -35,6 +36,7 @@
  */
 class ResponseBodySupport {
 
+    private boolean enableCheckForClosedWriter = false;
     private ByteArrayOutputStream outputStream;
     private ServletOutputStream servletOutputStream;
     private PrintWriter printWriter;
@@ -43,6 +45,12 @@
         reset();
     }
 
+    public void setEnableCheckForClosedWriter(boolean enableCheckForClosedWriter) {
+        this.enableCheckForClosedWriter = enableCheckForClosedWriter;
+        reset();
+    }
+
+
     public void reset() {
         outputStream = new ByteArrayOutputStream();
         servletOutputStream = null;
@@ -72,7 +80,8 @@
     public PrintWriter getWriter(String charset) {
         if (printWriter == null) {
             try {
-                printWriter = new PrintWriter(new OutputStreamWriter(getOutputStream(), defaultCharset(charset)));
+                PrintWriter printWriter1 = new PrintWriter(new OutputStreamWriter(getOutputStream(), defaultCharset(charset)));
+                printWriter = enableCheckForClosedWriter ? new CheckForClosedPrintWriter(printWriter1) : printWriter1;
             } catch (UnsupportedEncodingException ex) {
                 throw new RuntimeException("Unsupported encoding: " + defaultCharset(charset), ex);
             }
@@ -106,4 +115,232 @@
         return StringUtils.defaultString(charset, CharEncoding.UTF_8);
     }
 
+    private class CheckForClosedPrintWriter extends PrintWriter {
+
+        private final PrintWriter delegatee;
+
+        private boolean isClosed = false;
+
+        public CheckForClosedPrintWriter(PrintWriter delegatee) {
+            super(delegatee);
+            this.delegatee = delegatee;
+        }
+
+        private void checkClosed() {
+            if ( this.isClosed ) {
+                throw new WriterAlreadyClosedException();
+            }
+        }
+
+        @Override
+        public PrintWriter append(final char arg0) {
+            this.checkClosed();
+            return delegatee.append(arg0);
+        }
+
+        @Override
+        public PrintWriter append(final CharSequence arg0, final int arg1, final int arg2) {
+            this.checkClosed();
+            return delegatee.append(arg0, arg1, arg2);
+        }
+
+        @Override
+        public PrintWriter append(final CharSequence arg0) {
+            this.checkClosed();
+            return delegatee.append(arg0);
+        }
+
+        @Override
+        public boolean checkError() {
+            this.checkClosed();
+            return delegatee.checkError();
+        }
+
+        @Override
+        public void close() {
+            this.checkClosed();
+            this.isClosed = true;
+            delegatee.close();
+        }
+
+        @Override
+        public void flush() {
+            this.checkClosed();
+            delegatee.flush();
+        }
+
+        @Override
+        public PrintWriter format(final Locale arg0, final String arg1,
+                                  final Object... arg2) {
+            this.checkClosed();
+            return delegatee.format(arg0, arg1, arg2);
+        }
+
+        @Override
+        public PrintWriter format(final String arg0, final Object... arg1) {
+            this.checkClosed();
+            return delegatee.format(arg0, arg1);
+        }
+
+        @Override
+        public void print(final boolean arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public void print(final char arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public void print(final char[] arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public void print(final double arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public void print(final float arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public void print(final int arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public void print(final long arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public void print(final Object arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public void print(final String arg0) {
+            this.checkClosed();
+            delegatee.print(arg0);
+        }
+
+        @Override
+        public PrintWriter printf(final Locale arg0, final String arg1,
+        final Object... arg2) {
+            this.checkClosed();
+            return delegatee.printf(arg0, arg1, arg2);
+        }
+
+        @Override
+        public PrintWriter printf(final String arg0, final Object... arg1) {
+            this.checkClosed();
+            return delegatee.printf(arg0, arg1);
+        }
+
+        @Override
+        public void println() {
+            this.checkClosed();
+            delegatee.println();
+        }
+
+        @Override
+        public void println(final boolean arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void println(final char arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void println(final char[] arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void println(final double arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void println(final float arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void println(final int arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void println(final long arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void println(final Object arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void println(final String arg0) {
+            this.checkClosed();
+            delegatee.println(arg0);
+        }
+
+        @Override
+        public void write(final char[] arg0, final int arg1, final int arg2) {
+            this.checkClosed();
+            delegatee.write(arg0, arg1, arg2);
+        }
+
+        @Override
+        public void write(final char[] arg0) {
+            this.checkClosed();
+            delegatee.write(arg0);
+        }
+
+        @Override
+        public void write(final int arg0) {
+            this.checkClosed();
+            delegatee.write(arg0);
+        }
+
+        @Override
+        public void write(final String arg0, final int arg1, final int arg2) {
+            this.checkClosed();
+            delegatee.write(arg0, arg1, arg2);
+        }
+
+        @Override
+        public void write(final String arg0) {
+            this.checkClosed();
+            delegatee.write(arg0);
+        }
+
+    };
+
+    public class WriterAlreadyClosedException extends RuntimeException {
+    }
 }
diff --git a/src/main/java/org/apache/sling/servlethelpers/package-info.java b/src/main/java/org/apache/sling/servlethelpers/package-info.java
index 1a22083..2bcdb37 100644
--- a/src/main/java/org/apache/sling/servlethelpers/package-info.java
+++ b/src/main/java/org/apache/sling/servlethelpers/package-info.java
@@ -19,5 +19,5 @@
 /**
  * Mock implementation of selected Servlet-related Sling APIs.
  */
-@org.osgi.annotation.versioning.Version("1.8")
+@org.osgi.annotation.versioning.Version("1.9")
 package org.apache.sling.servlethelpers;
diff --git a/src/test/java/org/apache/sling/servlethelpers/MockSlingHttpServletResponseTest.java b/src/test/java/org/apache/sling/servlethelpers/MockSlingHttpServletResponseTest.java
index 1800b22..2ab0300 100644
--- a/src/test/java/org/apache/sling/servlethelpers/MockSlingHttpServletResponseTest.java
+++ b/src/test/java/org/apache/sling/servlethelpers/MockSlingHttpServletResponseTest.java
@@ -25,8 +25,11 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.when;
 
+import java.io.IOException;
+import java.io.Writer;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Locale;
@@ -38,6 +41,7 @@
 import org.apache.commons.lang3.CharEncoding;
 import org.apache.sling.api.adapter.AdapterManager;
 import org.apache.sling.api.adapter.SlingAdaptable;
+import org.apache.sling.servlethelpers.ResponseBodySupport.WriterAlreadyClosedException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -238,4 +242,17 @@
         assertNotEquals(result1,  result2);
     }
 
+    @Test
+    public void testCheckForClosedWriter() throws IOException {
+        response.setEnableCheckForClosedWriter(true);
+        Writer writer = response.getWriter();
+        // Close it twice and make sure the second one fails
+        writer.close();
+        try {
+            writer.close();
+            fail("Subsequent Closing of a Check-for-Closed Writer was not failing");
+        } catch(WriterAlreadyClosedException e) {
+            // Expected
+        }
+    }
 }