Issue #1628 clean up Inflaters used by HttpClient.
Former-commit-id:ef2dd2a031
[formerlyef2dd2a031
[formerly 765b3b00362cbe9a5d6c3e392e005b42d968d268]] Former-commit-id:e2efad85fe
Former-commit-id:2debab14f5
This commit is contained in:
parent
fa5872a35d
commit
cc0dcc8eae
2 changed files with 134 additions and 2 deletions
|
@ -37,7 +37,6 @@ import org.apache.http.HttpRequest;
|
||||||
import org.apache.http.HttpRequestInterceptor;
|
import org.apache.http.HttpRequestInterceptor;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.HttpResponseInterceptor;
|
import org.apache.http.HttpResponseInterceptor;
|
||||||
import org.apache.http.client.entity.GzipDecompressingEntity;
|
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
import org.apache.http.conn.ConnectionPoolTimeoutException;
|
import org.apache.http.conn.ConnectionPoolTimeoutException;
|
||||||
|
@ -77,6 +76,8 @@ import com.raytheon.uf.common.util.ByteArrayOutputStreamPool.ByteArrayOutputStre
|
||||||
* 08/09/12 15307 snaples Added putEntitiy in postStreamingEntity.
|
* 08/09/12 15307 snaples Added putEntitiy in postStreamingEntity.
|
||||||
* 01/07/13 DR 15294 D. Friedman Added streaming requests.
|
* 01/07/13 DR 15294 D. Friedman Added streaming requests.
|
||||||
* Jan 24, 2013 1526 njensen Added postDynamicSerialize()
|
* Jan 24, 2013 1526 njensen Added postDynamicSerialize()
|
||||||
|
* Feb 20, 2013 1628 bsteffen clean up Inflaters used by
|
||||||
|
* HttpClient.
|
||||||
*
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
@ -386,6 +387,11 @@ public class HttpClient {
|
||||||
statusHandler.handle(Priority.EVENTB,
|
statusHandler.handle(Priority.EVENTB,
|
||||||
"Error reading InputStream, assuming closed", e);
|
"Error reading InputStream, assuming closed", e);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
SafeGzipDecompressingEntity.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -747,7 +753,7 @@ public class HttpClient {
|
||||||
HeaderElement[] codecs = ceheader.getElements();
|
HeaderElement[] codecs = ceheader.getElements();
|
||||||
for (HeaderElement codec : codecs) {
|
for (HeaderElement codec : codecs) {
|
||||||
if (codec.getName().equalsIgnoreCase("gzip")) {
|
if (codec.getName().equalsIgnoreCase("gzip")) {
|
||||||
response.setEntity(new GzipDecompressingEntity(response
|
response.setEntity(new SafeGzipDecompressingEntity(response
|
||||||
.getEntity()));
|
.getEntity()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
/**
|
||||||
|
* This software was developed and / or modified by Raytheon Company,
|
||||||
|
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||||
|
*
|
||||||
|
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||||
|
* This software product contains export-restricted data whose
|
||||||
|
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||||
|
* to non-U.S. persons whether in the United States or abroad requires
|
||||||
|
* an export license or other authorization.
|
||||||
|
*
|
||||||
|
* Contractor Name: Raytheon Company
|
||||||
|
* Contractor Address: 6825 Pine Street, Suite 340
|
||||||
|
* Mail Stop B8
|
||||||
|
* Omaha, NE 68106
|
||||||
|
* 402.291.0100
|
||||||
|
*
|
||||||
|
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||||
|
* further licensing information.
|
||||||
|
**/
|
||||||
|
package com.raytheon.uf.common.comm;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.entity.GzipDecompressingEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
*
|
||||||
|
* This class obsessively closes GZIPInputStreams to end Inflaters
|
||||||
|
* and keep native memory free.
|
||||||
|
*
|
||||||
|
* Assumptions:
|
||||||
|
* This class can only be used if the content in this entity is going to be
|
||||||
|
* created and consumed on the same thread. If a new entity is created on the
|
||||||
|
* same thread, the content of the previous entity will be closed so don't use
|
||||||
|
* if a single thread is managing multiple responses at the same time.
|
||||||
|
*
|
||||||
|
* The Back Story:
|
||||||
|
* java.util.zip.Inflater is known to hold native memory until it is garbage
|
||||||
|
* collected or until end() is
|
||||||
|
* called(http://bugs.sun.com/view_bug.do?bug_id=4797189). The default
|
||||||
|
* GzipDecompressingEntity uses GZIPInputStream which uses Inflater for
|
||||||
|
* decompression. Ideally when the stream is done, it would get closed, which
|
||||||
|
* would trigger the Inflater to end and free the native resources. What really
|
||||||
|
* happens is that this entity gets wrapped in a BasicManagedEntity which wraps
|
||||||
|
* the GZIPInputStream in a EofSensorInputStream and at the end of the day the
|
||||||
|
* Inflater is left unended. The Inflater will still get cleaned when garbage
|
||||||
|
* collection runs and the finalizer gets invoked, but this adds some ambiguity
|
||||||
|
* to the availability of native memory. This calss exists to remove that
|
||||||
|
* ambiguity. There are a few things which can compound the problem and make
|
||||||
|
* things worse:
|
||||||
|
* 1. When the java heap is excessively large then the garbage collector may
|
||||||
|
* run less frequently since it has lots of space but it also means there
|
||||||
|
* is less native memory available.
|
||||||
|
* 2. When the process is running lots of native code outside the JVM then the
|
||||||
|
* garbage collector will not run as often but the demand for native memory
|
||||||
|
* will be greater.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* There are three ways that this class keeps the number of open
|
||||||
|
* GZIPInputStreams down.
|
||||||
|
* 1. Provides a static close method that can be used by HttpClient to force
|
||||||
|
* close GZIPInputStream.
|
||||||
|
* 2. Uses a ThreadLocal to limit the number of GZIPInputStreams to one per
|
||||||
|
* thread. This is also the mechanism used to staticly track which
|
||||||
|
* GZIPInputStream need to be closed.
|
||||||
|
* 3. Only holds weak references to the GZIPInputStreams so that if the
|
||||||
|
* garbage collector beats us to it then the Inflaters can get freed early.
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
*
|
||||||
|
* SOFTWARE HISTORY
|
||||||
|
*
|
||||||
|
* Date Ticket# Engineer Description
|
||||||
|
* ------------ ---------- ----------- --------------------------
|
||||||
|
* Feb 20, 2013 bsteffen Initial creation
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author bsteffen
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
class SafeGzipDecompressingEntity extends GzipDecompressingEntity{
|
||||||
|
|
||||||
|
private static ThreadLocal<WeakReference<GZIPInputStream>> stream = new ThreadLocal<WeakReference<GZIPInputStream>>();
|
||||||
|
|
||||||
|
public SafeGzipDecompressingEntity(HttpEntity entity) {
|
||||||
|
super(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getContent() throws IOException {
|
||||||
|
close();
|
||||||
|
InputStream istream = super.getContent();
|
||||||
|
if (istream instanceof GZIPInputStream) {
|
||||||
|
// save off a reference to the stream so it can be clsoed later.
|
||||||
|
stream.set(new WeakReference<GZIPInputStream>(
|
||||||
|
(GZIPInputStream) istream));
|
||||||
|
}
|
||||||
|
return istream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes whatever GZIPInputStream's are open on this Thread, freeing any
|
||||||
|
* native resources used by the Inflater.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void close() throws IOException {
|
||||||
|
WeakReference<GZIPInputStream> ref = stream.get();
|
||||||
|
if(ref != null){
|
||||||
|
stream.remove();
|
||||||
|
GZIPInputStream stream = ref.get();
|
||||||
|
if(stream != null){
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue