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);
+ }
+
+}