Skip to content

Commit

Permalink
Initial Response output buffering support
Browse files Browse the repository at this point in the history
  • Loading branch information
lincolnthree committed Jul 23, 2012
1 parent 011a002 commit 7c70cd9
Show file tree
Hide file tree
Showing 10 changed files with 431 additions and 46 deletions.
@@ -0,0 +1,20 @@
package org.ocpsoft.rewrite.servlet.config;

import java.io.InputStream;

import javax.servlet.ServletResponse;

/**
* A piece of work to be done on the fully buffered {@link ServletResponse#getOutputStream()} before flushing to the
* client, once the control of the application has been returned to Rewrite.
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
*/
public interface OutputBuffer
{
/**
* Perform any manipulation of the fully buffered {@link ServletResponse#getOutputStream()} contents.
*/
InputStream execute(InputStream input);
}
Expand Up @@ -15,11 +15,14 @@
*/
package org.ocpsoft.rewrite.servlet.config;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import org.ocpsoft.rewrite.context.EvaluationContext;
import org.ocpsoft.rewrite.servlet.http.event.HttpServletRewrite;
import org.ocpsoft.rewrite.servlet.impl.HttpRewriteWrappedResponse;

/**
* Responsible for adding various properties such as headers and cookies to the {@link HttpServletResponse}
Expand All @@ -28,6 +31,28 @@
*/
public abstract class Response extends HttpOperation
{
/**
* Add the given {@link OutputBuffer} instances to the current {@link ServletResponse}. This will activate response
* buffering on the current {@link ServletRequest} - meaning that generated output will not be sent to the client
* until the entire request has completed and all registered {@link OutputBuffer} instances have been executed.
*
* @param bufferedResponseToLowercase2
*
* @throws IllegalStateException When output has already been written to the client.
*/
public static Response withOutputBufferedBy(final OutputBuffer... buffers) throws IllegalStateException
{
return new Response() {
@Override
public void performHttp(HttpServletRewrite event, EvaluationContext context)
{
for (OutputBuffer buffer : buffers) {
HttpRewriteWrappedResponse.getInstance(event.getRequest()).addBufferStage(buffer);
}
}
};
}

/**
* Create an {@link org.ocpsoft.rewrite.config.Operation} that adds a header to the {@link HttpServletResponse}
*/
Expand Down Expand Up @@ -71,7 +96,8 @@ public void performHttp(final HttpServletRewrite event, final EvaluationContext
}

/**
* Create an {@link org.ocpsoft.rewrite.config.Operation} that adds a {@link Cookie} to the {@link HttpServletResponse}
* Create an {@link org.ocpsoft.rewrite.config.Operation} that adds a {@link Cookie} to the
* {@link HttpServletResponse}
*/
public static Response addCookie(final Cookie cookie)
{
Expand Down Expand Up @@ -100,7 +126,5 @@ public String toString()
{
return "Response";
}



}
@@ -0,0 +1,48 @@
package org.ocpsoft.rewrite.servlet.impl;

import org.ocpsoft.rewrite.event.Rewrite;
import org.ocpsoft.rewrite.servlet.http.event.HttpServletRewrite;
import org.ocpsoft.rewrite.servlet.spi.RewriteLifecycleListener;

public class DefaultRewriteLifecycleListener implements RewriteLifecycleListener<HttpServletRewrite>
{

@Override
public boolean handles(Rewrite payload)
{
return payload instanceof HttpServletRewrite;
}

@Override
public int priority()
{
return Integer.MAX_VALUE;
}

@Override
public void afterInboundLifecycle(HttpServletRewrite event)
{
HttpRewriteWrappedResponse.getInstance(event.getRequest()).flushBufferedStreams();
}

@Override
public void beforeInboundLifecycle(HttpServletRewrite event)
{}

@Override
public void beforeInboundRewrite(HttpServletRewrite event)
{}

@Override
public void afterInboundRewrite(HttpServletRewrite event)
{}

@Override
public void beforeOutboundRewrite(HttpServletRewrite event)
{}

@Override
public void afterOutboundRewrite(HttpServletRewrite event)
{}

}
Expand Up @@ -16,9 +16,17 @@

package org.ocpsoft.rewrite.servlet.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;

import javax.servlet.ServletContext;
Expand All @@ -29,11 +37,16 @@
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.ocpsoft.common.util.Streams;
import org.ocpsoft.rewrite.event.Rewrite;
import org.ocpsoft.rewrite.exception.RewriteException;
import org.ocpsoft.rewrite.servlet.RewriteLifecycleContext;
import org.ocpsoft.rewrite.servlet.config.OutputBuffer;
import org.ocpsoft.rewrite.servlet.event.BaseRewrite.Flow;
import org.ocpsoft.rewrite.servlet.http.event.HttpOutboundServletRewrite;
import org.ocpsoft.rewrite.servlet.spi.RewriteLifecycleListener;
import org.ocpsoft.rewrite.servlet.wrapper.BufferedResponseToLowercase1;
import org.ocpsoft.rewrite.servlet.wrapper.BufferedResponseToLowercase2;
import org.ocpsoft.rewrite.spi.RewriteProvider;

/**
Expand All @@ -42,13 +55,157 @@
public class HttpRewriteWrappedResponse extends HttpServletResponseWrapper
{
private final HttpServletRequest request;
private final static String INSTANCE_KEY = HttpRewriteWrappedResponse.class.getName() + "_instance";

public static HttpRewriteWrappedResponse getInstance(HttpServletRequest request)
{
return (HttpRewriteWrappedResponse) request.getAttribute(INSTANCE_KEY);
}

public HttpRewriteWrappedResponse(final HttpServletRequest request, final HttpServletResponse response)
{
super(response);
this.request = request;

if (getInstance(request) == null) {
request.setAttribute(INSTANCE_KEY, this);
}
}

/*
* Buffering Facilities
*/
private ByteArrayOutputStream stream = new ByteArrayOutputStream();
private PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(stream,
Charset.forName(getCharacterEncoding())), true);
private List<OutputBuffer> bufferedStages = new ArrayList<OutputBuffer>();

public boolean isBufferingActive()
{
return !bufferedStages.isEmpty();
}

public void addBufferStage(OutputBuffer stage)
{
this.bufferedStages.add(stage);
}

public void flushBufferedStreams()
{
if (isBufferingActive())
{
try {
InputStream result = new ByteArrayInputStream(stream.toByteArray());
for (OutputBuffer stage : bufferedStages) {
result = stage.execute(result);
}
Streams.copy(result, getResponse().getOutputStream());
if (printWriter != null) {
printWriter.close();
}
if (stream != null) {
stream.close();
}
}
catch (IOException e) {
// TODO what should we really do here?
e.printStackTrace();
}
}
}

@Override
public String toString()
{
if (isBufferingActive())
{
try {
return stream.toString(getCharacterEncoding());
}
catch (UnsupportedEncodingException e) {
throw new RewriteException("Response accepted invalid character encoding " + getCharacterEncoding(), e);
}
}
else
return super.toString();
}

@Override
public PrintWriter getWriter()
{
if (isBufferingActive())
return printWriter;
else
try {
return super.getWriter();
}
catch (IOException e) {
throw new RewriteException(e);
}
}

@Override
public ServletOutputStream getOutputStream()
{
if (isBufferingActive())
return new ByteArrayServletOutputStream(stream);
else
try {
return super.getOutputStream();
}
catch (IOException e) {
throw new RewriteException(e);
}
}

@Override
public void setContentLength(int contentLength)
{
/*
* Prevent content-length being set as the page might be modified.
*/
if (!isBufferingActive())
super.setContentLength(contentLength);
}

@Override
public void flushBuffer() throws IOException
{
if (isBufferingActive())
stream.flush();
else
super.flushBuffer();
}

private class ByteArrayServletOutputStream extends ServletOutputStream
{
private ByteArrayOutputStream outputStream;

public ByteArrayServletOutputStream(ByteArrayOutputStream outputStream)
{
this.outputStream = outputStream;
}

public void write(int b)
{
outputStream.write(b);
}

public void write(byte[] bytes) throws IOException
{
outputStream.write(bytes);
}

public void write(byte[] bytes, int off, int len)
{
outputStream.write(bytes, off, len);
}
}

/*
* End buffering facilities
*/

public HttpServletRequest getRequest()
{
return request;
Expand Down Expand Up @@ -269,41 +426,6 @@ public String getCharacterEncoding()
return super.getCharacterEncoding();
}

@Override
public ServletOutputStream getOutputStream() throws IOException
{
// TODO Auto-generated method stub
return super.getOutputStream();
}

@Override
public PrintWriter getWriter() throws IOException
{
// TODO Auto-generated method stub
return super.getWriter();
}

@Override
public void setContentLength(int len)
{
// TODO Auto-generated method stub
super.setContentLength(len);
}

@Override
public void setContentType(String type)
{
// TODO Auto-generated method stub
super.setContentType(type);
}

@Override
public String getContentType()
{
// TODO Auto-generated method stub
return super.getContentType();
}

@Override
public void setBufferSize(int size)
{
Expand All @@ -318,13 +440,6 @@ public int getBufferSize()
return super.getBufferSize();
}

@Override
public void flushBuffer() throws IOException
{
// TODO Auto-generated method stub
super.flushBuffer();
}

@Override
public boolean isCommitted()
{
Expand Down Expand Up @@ -374,5 +489,4 @@ public boolean isWrapperFor(Class wrappedType)
return super.isWrapperFor(wrappedType);
}


}
@@ -0,0 +1 @@
org.ocpsoft.rewrite.servlet.impl.DefaultRewriteLifecycleListener
Empty file.

0 comments on commit 7c70cd9

Please sign in to comment.