/*
* Copyright (C) 2014 Intel Corporation
* All rights reserved.
*/
package com.intel.mtwilson.servlet;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
/**
* On Tomcat 7 the ServletRequest we get is
* org.apache.catalina.connector.RequestFacade which
* does not implement markSupported.
*
* On Glassfish 4 the ServletRequest we get is also
* org.apache.catalina.connector.RequestFacade which
* does not implement markSupported.
*
* @author jbuhacoff
*/
public class RepeatableRequestFilter implements Filter {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RepeatableRequestFilter.class);
@Override
public void init(FilterConfig fc) throws ServletException {
log.debug("init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.debug("ServletRequest class {}", request.getClass().getName());
if (request instanceof HttpServletRequest) {
log.debug("Wrapping ServletRequest with RepeatableServletRequest");
chain.doFilter(new RepeatableServletRequest((HttpServletRequest) request), response);
} else {
log.debug("Not wrapping ServletRequest; continuing filter chain");
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
log.debug("destroy");
}
public static class RepeatableServletRequest extends HttpServletRequestWrapper {
private ByteArrayServletInputStream in = null;
public RepeatableServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (in == null) {
try (ServletInputStream stream = super.getInputStream()) {
log.debug("ServletInputStream markSupported? {}", stream.markSupported());
byte[] entity = IOUtils.toByteArray(stream);
in = new ByteArrayServletInputStream(entity);
}
}
if( in.isFinished() ) {
in.reset();
}
return in;
}
}
// tentative, not being used right now
public static class CopyableByteArrayInputStream extends ByteArrayInputStream {
/**
* The original offset as specified by the constructors; in contrast the
* "pos" variable changes so after constructing the instance and reading
* just one byte it's not possible to know what the original offset was.
*/
protected int offset;
public CopyableByteArrayInputStream(byte[] buf) {
super(buf);
this.offset = 0;
}
public CopyableByteArrayInputStream(byte[] buf, int offset, int length) {
super(buf, offset, length);
this.offset = offset;
}
/**
* Unlike reset() which resets the current read position to the last
* marked position or to zero, this reset(pos) method resets the current
* read position to the specified index. The mark is not affected so if
* you mark() at pos=5, then reset(0), then reset(), the final reset()
* will move the current position back to index 5. Also note that if you
* use the (buf,offset,length) constructor then using this reset(pos)
* method it's possible to reset the current index to an index lower
* than the originally provided offset.
*
* @param pos
*/
public void reset(int pos) {
this.pos = pos;
}
public byte[] copy() {
byte[] copy = new byte[count];
System.arraycopy(buf, offset, copy, 0, count - offset);
return copy;
}
}
/**
* Wraps ByteArrayInputStream and extends ServletInputStream
*/
public static class ByteArrayServletInputStream extends ServletInputStream {
private ByteArrayInputStream in;
public ByteArrayServletInputStream(byte[] entity) {
in = new ByteArrayInputStream(entity);
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void close() throws IOException {
in.close();
}
@Override
public synchronized void mark(int readlimit) {
in.mark(readlimit);
}
@Override
public boolean markSupported() {
return in.markSupported();
}
@Override
public long skip(long n) throws IOException {
return in.skip(n);
}
@Override
public boolean isFinished() {
return in.available() == 0;
}
@Override
public boolean isReady() {
return in.available() > 0;
}
@Override
public void setReadListener(ReadListener listener) {
throw new UnsupportedOperationException();
}
@Override
public int read() throws IOException {
return in.read();
}
@Override
public int read(byte[] b) throws IOException {
return in.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return in.read(b, off, len);
}
@Override
public synchronized void reset() throws IOException {
in.reset();
}
}
}