diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/Finishable.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/Finishable.java
new file mode 100644
index 0000000..c86c116
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/Finishable.java
@@ -0,0 +1,31 @@
+// ***************************************************************************************************************************
+// * 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.juneau.encoders;
+
+import java.io.*;
+
+/**
+ * Interface that identifies an output stream has having a finish() method.
+ */
+public interface Finishable {
+
+ /**
+ * Finishes writing compressed data to the output stream without closing the underlying stream.
+ *
+ *
+ * Use this method when applying multiple filters in succession to the same output stream.
+ *
+ * @throws IOException
+ */
+ void finish() throws IOException;
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/GzipEncoder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/GzipEncoder.java
index 8ebad4c..1596af5 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/GzipEncoder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/GzipEncoder.java
@@ -22,13 +22,7 @@
@Override /* Encoder */
public OutputStream getOutputStream(OutputStream os) throws IOException {
- return new GZIPOutputStream(os) {
- @Override /* OutputStream */
- public final void close() throws IOException {
- finish();
- super.close();
- }
- };
+ return new FinishableGZIPOutputStream(os);
}
@Override /* Encoder */
@@ -43,4 +37,10 @@
public String[] getCodings() {
return new String[]{"gzip"};
}
+
+ private static class FinishableGZIPOutputStream extends GZIPOutputStream implements Finishable {
+ FinishableGZIPOutputStream(OutputStream out) throws IOException {
+ super(out);
+ }
+ }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/FinishablePrintWriter.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/FinishablePrintWriter.java
new file mode 100644
index 0000000..4af84f4
--- /dev/null
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/FinishablePrintWriter.java
@@ -0,0 +1,43 @@
+// ***************************************************************************************************************************
+// * 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.juneau.rest;
+
+import java.io.*;
+
+import org.apache.juneau.encoders.*;
+
+/**
+ * A wrapped {@link PrintWriter} with an added finish() method.
+ */
+public class FinishablePrintWriter extends PrintWriter implements Finishable {
+
+ final Finishable f;
+
+ FinishablePrintWriter(OutputStream out, String characterEncoding) throws IOException {
+ super(new OutputStreamWriter(out, characterEncoding));
+ f = (out instanceof Finishable ? (Finishable)out : null);
+ }
+
+
+ /**
+ * Calls {@link Finishable#finish()} on the underlying output stream.
+ *
+ *
+ * A no-op if the underlying output stream does not implement the {@link Finishable} interface.
+ */
+ @Override /* Finishable */
+ public void finish() throws IOException {
+ if (f != null)
+ f.finish();
+ }
+}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/FinishableServletOutputStream.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/FinishableServletOutputStream.java
new file mode 100644
index 0000000..87e2044
--- /dev/null
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/FinishableServletOutputStream.java
@@ -0,0 +1,78 @@
+// ***************************************************************************************************************************
+// * 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.juneau.rest;
+
+import java.io.*;
+
+import javax.servlet.*;
+
+import org.apache.juneau.encoders.*;
+
+/**
+ * A wrapped {@link ServletOutputStream} with an added finish() method.
+ */
+public class FinishableServletOutputStream extends ServletOutputStream implements Finishable {
+
+ final OutputStream os;
+ final ServletOutputStream sos;
+ final Finishable f;
+
+ FinishableServletOutputStream(OutputStream os) {
+ this.os = os;
+ this.sos = (os instanceof ServletOutputStream ? (ServletOutputStream)os : null);
+ this.f = (os instanceof Finishable ? (Finishable)os : null);
+ }
+
+ @Override /* OutputStream */
+ public final void write(byte[] b, int off, int len) throws IOException {
+ os.write(b, off, len);
+ }
+
+ @Override /* OutputStream */
+ public final void write(int b) throws IOException {
+ os.write(b);
+ }
+
+ @Override /* OutputStream */
+ public final void flush() throws IOException {
+ os.flush();
+ }
+
+ @Override /* OutputStream */
+ public final void close() throws IOException {
+ os.close();
+ }
+
+ @Override /* ServletOutputStream */
+ public boolean isReady() {
+ return sos == null ? true : sos.isReady();
+ }
+
+ @Override /* ServletOutputStream */
+ public void setWriteListener(WriteListener arg0) {
+ if (sos != null)
+ sos.setWriteListener(arg0);
+ }
+
+ /**
+ * Calls {@link Finishable#finish()} on the underlying output stream.
+ *
+ *
+ * A no-op if the underlying output stream does not implement the {@link Finishable} interface.
+ */
+ @Override /* Finishable */
+ public void finish() throws IOException {
+ if (f != null)
+ f.finish();
+ }
+}
\ No newline at end of file
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
index 21f196b..1228145 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -58,8 +58,9 @@
private Object output; // The POJO being sent to the output.
private boolean isNullOutput; // The output is null (as opposed to not being set at all)
private RequestProperties properties; // Response properties
- private ServletOutputStream os;
- private PrintWriter w;
+ private ServletOutputStream sos;
+ private FinishableServletOutputStream os;
+ private FinishablePrintWriter w;
private HtmlDocBuilder htmlDocBuilder;
/**
@@ -347,7 +348,7 @@
* @return A negotiated output stream.
* @throws IOException
*/
- public ServletOutputStream getNegotiatedOutputStream() throws IOException {
+ public FinishableServletOutputStream getNegotiatedOutputStream() throws IOException {
if (os == null) {
Encoder encoder = null;
EncoderGroup encoders = restJavaMethod.encoders;
@@ -372,46 +373,18 @@
setHeader("content-encoding", encoding);
}
}
- os = getOutputStream();
- if (encoder != null) {
- @SuppressWarnings("resource")
- final OutputStream os2 = encoder.getOutputStream(os);
- os = new ServletOutputStream(){
- @Override /* OutputStream */
- public final void write(byte[] b, int off, int len) throws IOException {
- os2.write(b, off, len);
- }
- @Override /* OutputStream */
- public final void write(int b) throws IOException {
- os2.write(b);
- }
- @Override /* OutputStream */
- public final void flush() throws IOException {
- os2.flush();
- }
- @Override /* OutputStream */
- public final void close() throws IOException {
- os2.close();
- }
- @Override /* ServletOutputStream */
- public boolean isReady() {
- return true;
- }
- @Override /* ServletOutputStream */
- public void setWriteListener(WriteListener arg0) {
- throw new NoSuchMethodError();
- }
- };
- }
+ @SuppressWarnings("resource")
+ ServletOutputStream sos = getOutputStream();
+ os = new FinishableServletOutputStream(encoder == null ? sos : encoder.getOutputStream(sos));
}
return os;
}
@Override /* ServletResponse */
public ServletOutputStream getOutputStream() throws IOException {
- if (os == null)
- os = super.getOutputStream();
- return os;
+ if (sos == null)
+ sos = super.getOutputStream();
+ return sos;
}
/**
@@ -420,7 +393,7 @@
* @return