You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by gk...@apache.org on 2008/03/15 15:22:13 UTC

svn commit: r637416 - in /cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl: ./ src/main/java/org/apache/cocoon/servletservice/ src/test/java/org/apache/cocoon/servletservice/

Author: gkossakowski
Date: Sat Mar 15 07:22:06 2008
New Revision: 637416

URL: http://svn.apache.org/viewvc?rev=637416&view=rev
Log:
COCOON-2150: Implemented class that buffers data of servlet response only if 404 as status code has been set beforehand.
Added test-cases covering basic functionality of that class.

Added:
    cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapper.java
    cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/HttpServlerResponseBufferingWrapperTestCase.java
Modified:
    cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/pom.xml

Modified: cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/pom.xml
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/pom.xml?rev=637416&r1=637415&r2=637416&view=diff
==============================================================================
--- cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/pom.xml (original)
+++ cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/pom.xml Sat Mar 15 07:22:06 2008
@@ -120,6 +120,12 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <version>1.1</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   
   <build>

Added: cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapper.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapper.java?rev=637416&view=auto
==============================================================================
--- cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapper.java (added)
+++ cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapper.java Sat Mar 15 07:22:06 2008
@@ -0,0 +1,270 @@
+/*
+ * 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.cocoon.servletservice;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+class HttpServletResponseBufferingWrapper extends HttpServletResponseWrapper {
+
+    static private int BUFFER_LIMIT = 1024 * 1024;
+    static private String ALREADY_COMMITTED_EXCEPTION = "The response has been already committed.";
+    private boolean bufferResponse;
+    private boolean committed;
+    private String message;
+    private int statusCode;
+    private boolean sendError;
+
+    private LimitingServletOutputStream outputStream;
+    private PrintWriter printWriter;
+
+    public HttpServletResponseBufferingWrapper(HttpServletResponse response) {
+        super(response);
+        resetBufferedResponse();
+    }
+
+    public void addCookie(Cookie cookie) {
+        if (isCommitted())
+            return;
+        super.addCookie(cookie);
+    }
+
+    public void sendError(int sc) throws IOException {
+        if (isCommitted())
+            throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+        if (sc != SC_NOT_FOUND)
+            super.sendError(sc);
+        else {
+            bufferResponse = true;
+            committed = true;
+            sendError = true;
+        }
+        statusCode = sc;
+    }
+
+    public void sendError(int sc, String msg) throws IOException {
+        if (isCommitted())
+            throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+        if (sc != SC_NOT_FOUND)
+            super.sendError(sc, msg);
+        else {
+            bufferResponse = true;
+            committed = true;
+            message = msg;
+            sendError = true;
+        }
+        statusCode = sc;
+    }
+
+    public void sendRedirect(String location) throws IOException {
+        if (isCommitted())
+            throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+        super.sendRedirect(location);
+        statusCode = HttpServletResponse.SC_TEMPORARY_REDIRECT;
+    }
+
+    public void setDateHeader(String name, long date) {
+        if (isCommitted())
+            return;
+        super.setDateHeader(name, date);
+    }
+
+    public void addDateHeader(String name, long date) {
+        if (isCommitted())
+            return;
+        super.addDateHeader(name, date);
+    }
+
+    public void setHeader(String name, String value) {
+        if (isCommitted())
+            return;
+        super.setHeader(name, value);
+    }
+
+    public void addHeader(String name, String value) {
+        if (isCommitted())
+            return;
+        super.addHeader(name, value);
+    }
+
+    public void setIntHeader(String name, int value) {
+        if (isCommitted())
+            return;
+        super.setIntHeader(name, value);
+    }
+
+    public void addIntHeader(String name, int value) {
+        if (isCommitted())
+            return;
+        super.addIntHeader(name, value);
+    }
+
+    public void setStatus(int sc) {
+        if (isCommitted())
+            return;
+        if (sc != SC_NOT_FOUND)
+            super.setStatus(sc);
+        else {
+            bufferResponse = true;
+        }
+        statusCode = sc;
+    }
+
+    public void setStatus(int sc, String sm) {
+        throw new UnsupportedOperationException(
+                "This method has been deprecated.");
+    }
+
+    public ServletOutputStream getOutputStream() throws IOException {
+        if (!bufferResponse) {
+            return super.getOutputStream();
+        } else {
+            if (outputStream == null)
+                outputStream = new LimitingServletOutputStream(BUFFER_LIMIT);
+            committed = true;
+            return outputStream;
+        }
+    }
+
+    public PrintWriter getWriter() throws IOException {
+        if (!bufferResponse)
+            return super.getWriter();
+        else {
+            if (this.outputStream != null)
+                throw new IllegalStateException(
+                        "Output buffer has been already obtained. You can use either output buffer or print writer at one time.");
+            if (this.printWriter == null)
+                this.printWriter = new PrintWriter(new OutputStreamWriter(
+                        getOutputStream(), getCharacterEncoding()));
+            return printWriter;
+        }
+    }
+
+    public void flushBuffer() throws IOException {
+        if (!bufferResponse)
+            super.flushBuffer();
+        else
+            committed = true;
+    }
+
+    public boolean isCommitted() {
+        return committed || super.isCommitted();
+    }
+
+    public void resetBuffer() {
+        if (isCommitted())
+            throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+        if (!bufferResponse)
+            super.resetBuffer();
+        else if (outputStream != null)
+            outputStream.reset();
+    }
+
+    public void reset() {
+        if (isCommitted())
+            throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+        bufferResponse = false;
+        message = null;
+    }
+
+    public void flushBufferedResponse() throws IOException {
+        if (bufferResponse) {
+            if (sendError) {
+                try {
+                    if (message != null)
+                        super.sendError(SC_NOT_FOUND, message);
+                    else
+                        super.setStatus(SC_NOT_FOUND);
+                } catch (IOException e) {
+                    // this should never occur
+                    throw new IllegalStateException(
+                            "FATAL ERROR! This situation should never occur because it's a job of "
+                                    + getClass().getName() + " class to "
+                                    + "prevent such situation.");
+                }
+            } else {
+                if (message != null)
+                    super.setStatus(SC_NOT_FOUND, message);
+                else
+                    super.setStatus(SC_NOT_FOUND);
+            }
+
+            if (this.printWriter != null) {
+                if (this.printWriter.checkError())
+                    throw new IOException(
+                            "Error occured while writing to printWriter.");
+                this.printWriter.close();
+            }
+
+            outputStream.writeTo(super.getOutputStream());
+        }
+    }
+
+    public void resetBufferedResponse() {
+        if (super.isCommitted())
+            throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+        if (bufferResponse) {
+            message = null;
+            bufferResponse = false;
+            committed = false;
+            sendError = false;
+        }
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+
+    private class LimitingServletOutputStream extends ServletOutputStream {
+
+        private int writeLimit;
+        private ByteArrayOutputStream outputStream;
+
+        public LimitingServletOutputStream(int writeLimit) {
+            this.writeLimit = writeLimit;
+            reset();
+        }
+
+        public void write(int b) throws IOException {
+            if (this.outputStream.size() < this.writeLimit)
+                this.outputStream.write(b);
+            else
+                throw new RuntimeException(
+                        "The buffering limit (" + writeLimit+ ") has been reached. If you encounter this exception it means that you to "
+                                + "write a big response body for response that has error code set as status code. This is always a bad "
+                                + "idea and in such case you should reconsider your design.");
+        }
+
+        public void reset() {
+            this.outputStream = new ByteArrayOutputStream(writeLimit);
+        }
+
+        public void writeTo(OutputStream outputStream) throws IOException {
+            this.outputStream.writeTo(outputStream);
+        }
+
+    }
+
+}

Added: cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/HttpServlerResponseBufferingWrapperTestCase.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/HttpServlerResponseBufferingWrapperTestCase.java?rev=637416&view=auto
==============================================================================
--- cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/HttpServlerResponseBufferingWrapperTestCase.java (added)
+++ cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/HttpServlerResponseBufferingWrapperTestCase.java Sat Mar 15 07:22:06 2008
@@ -0,0 +1,107 @@
+/*
+ * 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.cocoon.servletservice;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.easymock.MockControl;
+
+public class HttpServlerResponseBufferingWrapperTestCase extends TestCase {
+    
+    public void testHeadersPassing() {
+        MockControl control = MockControl.createControl(HttpServletResponse.class);
+        HttpServletResponse response = (HttpServletResponse)control.getMock();
+        response.setHeader("test", "foo");
+        response.isCommitted();
+        control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+        control.replay();
+        HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+        responseWrapper.setHeader("test", "foo");
+        control.verify();
+    }
+    
+    public void testStatusCodePassing() {
+        MockControl control = MockControl.createControl(HttpServletResponse.class);
+        HttpServletResponse response = (HttpServletResponse)control.getMock();
+        response.setStatus(HttpServletResponse.SC_OK);
+        response.isCommitted();
+        control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+        control.replay();
+        HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+        responseWrapper.setStatus(HttpServletResponse.SC_OK);
+        control.verify();
+    }
+    
+    public void testNoBuffering() throws IOException {
+        MockControl control = MockControl.createControl(HttpServletResponse.class);
+        HttpServletResponse response = (HttpServletResponse)control.getMock();
+        response.setStatus(HttpServletResponse.SC_OK);
+        response.isCommitted();
+        control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+        response.getOutputStream();
+        control.setReturnValue(new ServletOutputStream() {
+            public void write(int b) throws IOException { }
+        });
+        control.replay();
+        HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+        responseWrapper.setStatus(HttpServletResponse.SC_OK);
+        responseWrapper.getOutputStream();
+        control.verify();
+    }
+    
+    public void testBuffering() throws IOException {
+        MockControl control = MockControl.createControl(HttpServletResponse.class);
+        HttpServletResponse response = (HttpServletResponse)control.getMock();
+        response.isCommitted();
+        control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+        control.replay();
+        HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+        responseWrapper.setStatus(HttpServletResponse.SC_NOT_FOUND);
+        responseWrapper.getOutputStream();
+        control.verify();
+    }
+    
+    public void testBufferOverflow() throws IOException {
+        MockControl control = MockControl.createControl(HttpServletResponse.class);
+        HttpServletResponse response = (HttpServletResponse)control.getMock();
+        response.isCommitted();
+        control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+        control.replay();
+        HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+        responseWrapper.setStatus(HttpServletResponse.SC_NOT_FOUND);
+        OutputStream outputStream = responseWrapper.getOutputStream();
+        boolean catchedException = false;
+        byte[] b = new byte[1024*1024];
+        for (int i = 0; i < 2; i++) {
+            try {
+                outputStream.write(b);
+            } catch (RuntimeException e) {
+                //here we check whether we caught right exception. Is there any better method?
+                catchedException = e.getMessage().contains("limit");
+            }
+        }
+        control.verify();
+        assertTrue("Did not catch exception of overflowed buffer.", catchedException);
+    }
+    
+}