1 /* 2 * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.net.www.http; 27 28 import java.io.*; 29 import sun.net.ProgressSource; 30 import sun.net.www.MeteredStream; 31 32 /** 33 * A stream that has the property of being able to be kept alive for 34 * multiple downloads from the same server. 35 * 36 * @author Stephen R. Pietrowicz (NCSA) 37 * @author Dave Brown 38 */ 39 public 40 class KeepAliveStream extends MeteredStream implements Hurryable { 41 42 // instance variables 43 HttpClient hc; 44 45 boolean hurried; 46 47 // has this KeepAliveStream been put on the queue for asynchronous cleanup. 48 protected boolean queuedForCleanup = false; 49 50 private static final KeepAliveStreamCleaner queue = new KeepAliveStreamCleaner(); 51 private static Thread cleanerThread; // null 52 53 /** 54 * Constructor 55 */ 56 public KeepAliveStream(InputStream is, ProgressSource pi, long expected, HttpClient hc) { 57 super(is, pi, expected); 58 this.hc = hc; 59 } 60 61 /** 62 * Attempt to cache this connection 63 */ 64 public void close() throws IOException { 65 // If the inputstream is closed already, just return. 66 if (closed) { 67 return; 68 } 69 70 // If this stream has already been queued for cleanup. 71 if (queuedForCleanup) { 72 return; 73 } 74 75 // Skip past the data that's left in the Inputstream because 76 // some sort of error may have occurred. 77 // Do this ONLY if the skip won't block. The stream may have 78 // been closed at the beginning of a big file and we don't want 79 // to hang around for nothing. So if we can't skip without blocking 80 // we just close the socket and, therefore, terminate the keepAlive 81 // NOTE: Don't close super class 82 try { 83 if (expected > count) { 84 long nskip = (long) (expected - count); 85 if (nskip <= available()) { 86 long n = 0; 87 while (n < nskip) { 88 nskip = nskip - n; 89 n = skip(nskip); 90 } 91 } else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) { 92 //put this KeepAliveStream on the queue so that the data remaining 93 //on the socket can be cleanup asyncronously. 94 queueForCleanup(new KeepAliveCleanerEntry(this, hc)); 95 } else { 96 hc.closeServer(); 97 } 98 } 99 if (!closed && !hurried && !queuedForCleanup) { 100 hc.finished(); 101 } 102 } finally { 103 if (pi != null) 104 pi.finishTracking(); 105 106 if (!queuedForCleanup) { 107 // nulling out the underlying inputstream as well as 108 // httpClient to let gc collect the memories faster 109 in = null; 110 hc = null; 111 closed = true; 112 } 113 } 114 } 115 116 /* we explicitly do not support mark/reset */ 117 118 public boolean markSupported() { 119 return false; 120 } 121 122 public void mark(int limit) {} 123 124 public void reset() throws IOException { 125 throw new IOException("mark/reset not supported"); 126 } 127 128 public synchronized boolean hurry() { 129 try { 130 /* CASE 0: we're actually already done */ 131 if (closed || count >= expected) { 132 return false; 133 } else if (in.available() < (expected - count)) { 134 /* CASE I: can't meet the demand */ 135 return false; 136 } else { 137 /* CASE II: fill our internal buffer 138 * Remind: possibly check memory here 139 */ 140 int size = (int) (expected - count); 141 byte[] buf = new byte[size]; 142 DataInputStream dis = new DataInputStream(in); 143 dis.readFully(buf); 144 in = new ByteArrayInputStream(buf); 145 hurried = true; 146 return true; 147 } 148 } catch (IOException e) { 149 // e.printStackTrace(); 150 return false; 151 } 152 } 153 154 private static void queueForCleanup(KeepAliveCleanerEntry kace) { 155 synchronized(queue) { 156 if(!kace.getQueuedForCleanup()) { 157 if (!queue.offer(kace)) { 158 kace.getHttpClient().closeServer(); 159 return; 160 } 161 162 kace.setQueuedForCleanup(); 163 queue.notifyAll(); 164 } 165 166 boolean startCleanupThread = (cleanerThread == null); 167 if (!startCleanupThread) { 168 if (!cleanerThread.isAlive()) { 169 startCleanupThread = true; 170 } 171 } 172 173 if (startCleanupThread) { 174 java.security.AccessController.doPrivileged( 175 new java.security.PrivilegedAction<Void>() { 176 public Void run() { 177 // We want to create the Keep-Alive-SocketCleaner in the 178 // system threadgroup 179 ThreadGroup grp = Thread.currentThread().getThreadGroup(); 180 ThreadGroup parent = null; 181 while ((parent = grp.getParent()) != null) { 182 grp = parent; 183 } 184 185 cleanerThread = new Thread(grp, queue, "Keep-Alive-SocketCleaner"); 186 cleanerThread.setDaemon(true); 187 cleanerThread.setPriority(Thread.MAX_PRIORITY - 2); 188 // Set the context class loader to null in order to avoid 189 // keeping a strong reference to an application classloader. 190 cleanerThread.setContextClassLoader(null); 191 cleanerThread.start(); 192 return null; 193 } 194 }); 195 } 196 } // queue 197 } 198 199 protected long remainingToRead() { 200 return expected - count; 201 } 202 203 protected void setClosed() { 204 in = null; 205 hc = null; 206 closed = true; 207 } 208 } 209 210 211 class KeepAliveCleanerEntry 212 { 213 KeepAliveStream kas; 214 HttpClient hc; 215 216 public KeepAliveCleanerEntry(KeepAliveStream kas, HttpClient hc) { 217 this.kas = kas; 218 this.hc = hc; 219 } 220 221 protected KeepAliveStream getKeepAliveStream() { 222 return kas; 223 } 224 225 protected HttpClient getHttpClient() { 226 return hc; 227 } 228 229 protected void setQueuedForCleanup() { 230 kas.queuedForCleanup = true; 231 } 232 233 protected boolean getQueuedForCleanup() { 234 return kas.queuedForCleanup; 235 } 236 237 }