blob: 779afb5ad7ba988d2163077e02e08e878ec67453 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.net.www.http;
27
28import java.net.URL;
29import java.net.HttpURLConnection;
30import java.io.*;
31import java.util.StringTokenizer;
32import sun.net.ProgressSource;
33import sun.net.www.MeteredStream;
34
35/**
36 * A stream that has the property of being able to be kept alive for
37 * multiple downloads from the same server.
38 *
39 * @author Stephen R. Pietrowicz (NCSA)
40 * @author Dave Brown
41 */
42public
43class KeepAliveStream extends MeteredStream implements Hurryable {
44
45 // instance variables
46 HttpClient hc;
47
48 boolean hurried;
49
50 // has this KeepAliveStream been put on the queue for asynchronous cleanup.
51 protected boolean queuedForCleanup = false;
52
53 private static KeepAliveStreamCleaner queue = new KeepAliveStreamCleaner();
54 private static Thread cleanerThread = null;
55 private static boolean startCleanupThread;
56
57 /**
58 * Constructor
59 */
60 public KeepAliveStream(InputStream is, ProgressSource pi, long expected, HttpClient hc) {
61 super(is, pi, expected);
62 this.hc = hc;
63 }
64
65 /**
66 * Attempt to cache this connection
67 */
68 public void close() throws IOException {
69 // If the inputstream is closed already, just return.
70 if (closed) {
71 return;
72 }
73
74 // If this stream has already been queued for cleanup.
75 if (queuedForCleanup) {
76 return;
77 }
78
79 // Skip past the data that's left in the Inputstream because
80 // some sort of error may have occurred.
81 // Do this ONLY if the skip won't block. The stream may have
82 // been closed at the beginning of a big file and we don't want
83 // to hang around for nothing. So if we can't skip without blocking
84 // we just close the socket and, therefore, terminate the keepAlive
85 // NOTE: Don't close super class
86 try {
87 if (expected > count) {
88 long nskip = (long) (expected - count);
89 if (nskip <= available()) {
90 long n = 0;
91 while (n < nskip) {
92 nskip = nskip - n;
93 n = skip(nskip);
94 }
95 } else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) {
96 //put this KeepAliveStream on the queue so that the data remaining
97 //on the socket can be cleanup asyncronously.
98 queueForCleanup(new KeepAliveCleanerEntry(this, hc));
99 } else {
100 hc.closeServer();
101 }
102 }
103 if (!closed && !hurried && !queuedForCleanup) {
104 hc.finished();
105 }
106 } finally {
107 if (pi != null)
108 pi.finishTracking();
109
110 if (!queuedForCleanup) {
111 // nulling out the underlying inputstream as well as
112 // httpClient to let gc collect the memories faster
113 in = null;
114 hc = null;
115 closed = true;
116 }
117 }
118 }
119
120 /* we explicitly do not support mark/reset */
121
122 public boolean markSupported() {
123 return false;
124 }
125
126 public void mark(int limit) {}
127
128 public void reset() throws IOException {
129 throw new IOException("mark/reset not supported");
130 }
131
132 public synchronized boolean hurry() {
133 try {
134 /* CASE 0: we're actually already done */
135 if (closed || count >= expected) {
136 return false;
137 } else if (in.available() < (expected - count)) {
138 /* CASE I: can't meet the demand */
139 return false;
140 } else {
141 /* CASE II: fill our internal buffer
142 * Remind: possibly check memory here
143 */
144 int size = (int) (expected - count);
145 byte[] buf = new byte[size];
146 DataInputStream dis = new DataInputStream(in);
147 dis.readFully(buf);
148 in = new ByteArrayInputStream(buf);
149 hurried = true;
150 return true;
151 }
152 } catch (IOException e) {
153 // e.printStackTrace();
154 return false;
155 }
156 }
157
158 private static synchronized void queueForCleanup(KeepAliveCleanerEntry kace) {
159 if(queue != null && !kace.getQueuedForCleanup()) {
160 if (!queue.offer(kace)) {
161 kace.getHttpClient().closeServer();
162 return;
163 }
164
165 kace.setQueuedForCleanup();
166 }
167
168 startCleanupThread = (cleanerThread == null);
169 if (!startCleanupThread) {
170 if (!cleanerThread.isAlive()) {
171 startCleanupThread = true;
172 }
173 }
174
175 if (startCleanupThread) {
176 java.security.AccessController.doPrivileged(
177 new java.security.PrivilegedAction() {
178 public Object run() {
179 // We want to create the Keep-Alive-SocketCleaner in the
180 // system threadgroup
181 ThreadGroup grp = Thread.currentThread().getThreadGroup();
182 ThreadGroup parent = null;
183 while ((parent = grp.getParent()) != null) {
184 grp = parent;
185 }
186
187 cleanerThread = new Thread(grp, queue, "Keep-Alive-SocketCleaner");
188 cleanerThread.setDaemon(true);
189 cleanerThread.setPriority(Thread.MAX_PRIORITY - 2);
190 cleanerThread.start();
191 return null;
192 }
193 });
194 }
195 }
196
197 protected long remainingToRead() {
198 return expected - count;
199 }
200
201 protected void setClosed() {
202 in = null;
203 hc = null;
204 closed = true;
205 }
206}
207
208
209class KeepAliveCleanerEntry
210{
211 KeepAliveStream kas;
212 HttpClient hc;
213
214 public KeepAliveCleanerEntry(KeepAliveStream kas, HttpClient hc) {
215 this.kas = kas;
216 this.hc = hc;
217 }
218
219 protected KeepAliveStream getKeepAliveStream() {
220 return kas;
221 }
222
223 protected HttpClient getHttpClient() {
224 return hc;
225 }
226
227 protected void setQueuedForCleanup() {
228 kas.queuedForCleanup = true;
229 }
230
231 protected boolean getQueuedForCleanup() {
232 return kas.queuedForCleanup;
233 }
234
235}