Skip to content

Commit c17b324

Browse files
committed
Create delegate on InflaterInputStream and DeflaterOutputStream to call end on Inflater and Deflater
1 parent 6da18bd commit c17b324

File tree

3 files changed

+149
-2
lines changed

3 files changed

+149
-2
lines changed

core-common/src/main/java/org/glassfish/jersey/message/DeflateEncoder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public InputStream decode(String contentEncoding, InputStream encodedStream)
7777
return new InflaterInputStream(markSupportingStream);
7878
} else {
7979
// no zlib wrapper
80-
return new InflaterInputStream(markSupportingStream, new Inflater(true));
80+
return new ClosingInflaterInputStream(markSupportingStream, true);
8181
}
8282
}
8383

@@ -98,7 +98,7 @@ public OutputStream encode(String contentEncoding, OutputStream entityStream)
9898
}
9999

100100
return deflateWithoutZLib
101-
? new DeflaterOutputStream(entityStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true))
101+
? new ClosingDeflaterOutputStream(entityStream, Deflater.DEFAULT_COMPRESSION, true)
102102
: new DeflaterOutputStream(entityStream);
103103
}
104104
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.glassfish.jersey.message.internal;
2+
3+
import java.io.IOException;
4+
import java.io.OutputStream;
5+
import java.util.zip.Deflater;
6+
import java.util.zip.DeflaterOutputStream;
7+
8+
/**
9+
* After Java 9, in the class Deflater the method finalize doesn't call end anymore
10+
* The method end must be called explicitly. But when using the constructor
11+
* DeflaterOutputStream(OutputStream out, Deflater def), the Deflater.end() method will
12+
* not be called when closing the stream.
13+
* This lead to memory leaks in the off-heap.
14+
*
15+
* @see java.util.zip.Deflater
16+
* @see java.util.zip.DeflaterOutputStream
17+
*/
18+
public final class ClosingDeflaterOutputStream extends OutputStream {
19+
20+
private final Deflater deflater;
21+
22+
private final DeflaterOutputStream delegate;
23+
24+
private boolean closed = false;
25+
26+
public ClosingDeflaterOutputStream(OutputStream out, int level, boolean nowrap) {
27+
deflater = new Deflater(level, nowrap);
28+
delegate = new DeflaterOutputStream(out, deflater);
29+
}
30+
31+
@Override
32+
public void write(int b) throws IOException {
33+
delegate.write(b);
34+
}
35+
36+
@Override
37+
public void write(byte[] b, int off, int len) throws IOException {
38+
delegate.write(b, off, len);
39+
}
40+
41+
public void finish() throws IOException {
42+
delegate.finish();
43+
}
44+
45+
@Override
46+
public void close() throws IOException {
47+
if (!closed) {
48+
try {
49+
delegate.close();
50+
} finally {
51+
deflater.end();
52+
}
53+
closed = true;
54+
}
55+
}
56+
57+
@Override
58+
public void flush() throws IOException {
59+
delegate.flush();
60+
}
61+
62+
@Override
63+
public void write(byte[] b) throws IOException {
64+
delegate.write(b);
65+
}
66+
67+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.glassfish.jersey.message.internal;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.util.zip.Inflater;
6+
import java.util.zip.InflaterInputStream;
7+
8+
/**
9+
* After Java 9, in the class Inflater the method finalize doesn't call end anymore
10+
* The method end must be called explicitly. But when using the constructor
11+
* InflaterInputStream(InputStream out, Inflater def), the Inflater.end() method will
12+
* not be called when closing the stream.
13+
* This lead to memory leaks in the off-heap.
14+
*
15+
* @see java.util.zip.Inflater
16+
* @see java.util.zip.InflaterInputStream
17+
*/
18+
public class ClosingInflaterInputStream extends InputStream {
19+
20+
private final Inflater inflater;
21+
22+
private final InflaterInputStream delegate;
23+
24+
private boolean closed = false;
25+
26+
public ClosingInflaterInputStream(InputStream inputStream, boolean nowrap) {
27+
inflater = new Inflater(nowrap);
28+
delegate = new InflaterInputStream(inputStream, inflater);
29+
}
30+
31+
@Override
32+
public int read() throws IOException {
33+
return delegate.read();
34+
}
35+
36+
@Override
37+
public int read(byte[] b, int off, int len) throws IOException {
38+
return delegate.read(b, off, len);
39+
}
40+
41+
@Override
42+
public int available() throws IOException {
43+
return delegate.available();
44+
}
45+
46+
@Override
47+
public long skip(long n) throws IOException {
48+
return delegate.skip(n);
49+
}
50+
51+
@Override
52+
public void close() throws IOException {
53+
if (!closed) {
54+
inflater.end();
55+
delegate.close();
56+
closed = true;
57+
}
58+
}
59+
60+
@Override
61+
public boolean markSupported() {
62+
return delegate.markSupported();
63+
}
64+
65+
@Override
66+
public void mark(int readlimit) {
67+
delegate.mark(readlimit);
68+
}
69+
70+
@Override
71+
public void reset() throws IOException {
72+
delegate.reset();
73+
}
74+
75+
@Override
76+
public int read(byte[] b) throws IOException {
77+
return delegate.read(b);
78+
}
79+
80+
}

0 commit comments

Comments
 (0)