Mercurial > hg > blitz_condensed
comparison src/com/go/trove/net/LazySocket.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3dc0c5604566 |
---|---|
1 /* ==================================================================== | |
2 * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group | |
3 * ==================================================================== | |
4 * The Tea Software License, Version 1.1 | |
5 * | |
6 * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved. | |
7 * | |
8 * Redistribution and use in source and binary forms, with or without | |
9 * modification, are permitted provided that the following conditions | |
10 * are met: | |
11 * | |
12 * 1. Redistributions of source code must retain the above copyright | |
13 * notice, this list of conditions and the following disclaimer. | |
14 * | |
15 * 2. Redistributions in binary form must reproduce the above copyright | |
16 * notice, this list of conditions and the following disclaimer in | |
17 * the documentation and/or other materials provided with the | |
18 * distribution. | |
19 * | |
20 * 3. The end-user documentation included with the redistribution, | |
21 * if any, must include the following acknowledgment: | |
22 * "This product includes software developed by the | |
23 * Walt Disney Internet Group (http://opensource.go.com/)." | |
24 * Alternately, this acknowledgment may appear in the software itself, | |
25 * if and wherever such third-party acknowledgments normally appear. | |
26 * | |
27 * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must | |
28 * not be used to endorse or promote products derived from this | |
29 * software without prior written permission. For written | |
30 * permission, please contact opensource@dig.com. | |
31 * | |
32 * 5. Products derived from this software may not be called "Tea", | |
33 * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet", | |
34 * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior | |
35 * written permission of the Walt Disney Internet Group. | |
36 * | |
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
40 * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS | |
41 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
42 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
43 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
44 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
45 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
47 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
48 * ==================================================================== | |
49 * | |
50 * For more information about Tea, please see http://opensource.go.com/. | |
51 */ | |
52 | |
53 package com.go.trove.net; | |
54 | |
55 import java.net.*; | |
56 import java.io.*; | |
57 | |
58 /****************************************************************************** | |
59 * A socket implementation that lazily establishs a connection. It only | |
60 * connects when actually needed. Setting options and getting I/O streams will | |
61 * not force a connection to be established. As soon as a read or write | |
62 * operation is performed, a connection is established. | |
63 * <p> | |
64 * If the first write operation requires a connection to be established, then a | |
65 * recycled connection is requested. The connection is tested by writing the | |
66 * data to it. If this fails, a new connection is requested and the operation | |
67 * is tried again. | |
68 * | |
69 * @author Brian S O'Neill | |
70 * @version | |
71 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 00/12/05 <!-- $--> | |
72 */ | |
73 class LazySocket extends Socket { | |
74 private final Impl mImpl; | |
75 | |
76 public LazySocket(SocketFactory factory) throws SocketException { | |
77 this(factory, null, factory.getDefaultTimeout()); | |
78 } | |
79 | |
80 public LazySocket(SocketFactory factory, Object session) | |
81 throws SocketException | |
82 { | |
83 this(factory, session, factory.getDefaultTimeout()); | |
84 } | |
85 | |
86 public LazySocket(SocketFactory factory, long timeout) | |
87 throws SocketException | |
88 { | |
89 this(factory, null, timeout); | |
90 } | |
91 | |
92 public LazySocket(SocketFactory factory, Object session, long timeout) | |
93 throws SocketException | |
94 { | |
95 this(new Impl(factory, session, timeout)); | |
96 } | |
97 | |
98 private LazySocket(Impl impl) throws SocketException { | |
99 super(impl); | |
100 mImpl = impl; | |
101 } | |
102 | |
103 /** | |
104 * Returns the internal wrapped socket or null if not connected. After | |
105 * calling recycle, this LazySocket instance is closed. | |
106 */ | |
107 CheckedSocket recycle() { | |
108 CheckedSocket s; | |
109 if (mImpl.mClosed) { | |
110 s = null; | |
111 } | |
112 else { | |
113 s = mImpl.mSocket; | |
114 mImpl.mSocket = null; | |
115 try { | |
116 mImpl.close(); | |
117 } | |
118 catch (IOException e) { | |
119 } | |
120 } | |
121 return s; | |
122 } | |
123 | |
124 private static class Impl extends SocketImpl { | |
125 private final SocketFactory mFactory; | |
126 private final Object mSession; | |
127 private final long mTimeout; | |
128 | |
129 private boolean mClosed; | |
130 private CheckedSocket mSocket; | |
131 | |
132 private Object[] mOptions; | |
133 | |
134 private InputStream mIn; | |
135 private OutputStream mOut; | |
136 | |
137 public Impl(SocketFactory factory, Object session, long timeout) { | |
138 mFactory = factory; | |
139 mSession = session; | |
140 mTimeout = timeout; | |
141 } | |
142 | |
143 public void setOption(int optId, Object value) throws SocketException { | |
144 int optionIndex; | |
145 | |
146 switch (optId) { | |
147 case TCP_NODELAY: | |
148 optionIndex = 0; | |
149 break; | |
150 case SO_LINGER: | |
151 optionIndex = 1; | |
152 break; | |
153 case SO_TIMEOUT: | |
154 optionIndex = 2; | |
155 break; | |
156 case SO_SNDBUF: | |
157 optionIndex = 3; | |
158 break; | |
159 case SO_RCVBUF: | |
160 optionIndex = 4; | |
161 break; | |
162 case SO_BINDADDR: | |
163 case SO_REUSEADDR: | |
164 case IP_MULTICAST_IF: | |
165 default: | |
166 throw new SocketException("Invalid option: " + optId); | |
167 } | |
168 | |
169 if (mOptions == null) { | |
170 mOptions = new Object[5]; | |
171 } | |
172 | |
173 mOptions[optionIndex] = value; | |
174 | |
175 if (mSocket == null) { | |
176 return; | |
177 } | |
178 | |
179 switch (optId) { | |
180 case TCP_NODELAY: | |
181 mSocket.setTcpNoDelay(((Boolean)value).booleanValue()); | |
182 break; | |
183 case SO_LINGER: | |
184 if (value instanceof Boolean) { | |
185 mSocket.setSoLinger(((Boolean)value).booleanValue(), 0); | |
186 } | |
187 else { | |
188 mSocket.setSoLinger(true, ((Integer)value).intValue()); | |
189 } | |
190 break; | |
191 case SO_TIMEOUT: | |
192 mSocket.setSoTimeout(((Integer)value).intValue()); | |
193 break; | |
194 case SO_SNDBUF: | |
195 mSocket.setSendBufferSize(((Integer)value).intValue()); | |
196 break; | |
197 case SO_RCVBUF: | |
198 mSocket.setReceiveBufferSize(((Integer)value).intValue()); | |
199 break; | |
200 } | |
201 } | |
202 | |
203 public Object getOption(int optId) throws SocketException { | |
204 Socket socket = createSocket(); | |
205 | |
206 switch (optId) { | |
207 case TCP_NODELAY: | |
208 return socket.getTcpNoDelay() ? Boolean.TRUE : Boolean.FALSE; | |
209 case SO_BINDADDR: | |
210 return socket.getLocalAddress(); | |
211 case SO_LINGER: | |
212 return new Integer(socket.getSoLinger()); | |
213 case SO_TIMEOUT: | |
214 return new Integer(socket.getSoTimeout()); | |
215 case SO_SNDBUF: | |
216 return new Integer(socket.getSendBufferSize()); | |
217 case SO_RCVBUF: | |
218 return new Integer(socket.getReceiveBufferSize()); | |
219 case SO_REUSEADDR: | |
220 case IP_MULTICAST_IF: | |
221 default: | |
222 throw new SocketException("Invalid option: " + optId); | |
223 } | |
224 } | |
225 | |
226 protected InputStream getInputStream() throws IOException { | |
227 if (mIn == null) { | |
228 mIn = new In(); | |
229 } | |
230 return mIn; | |
231 } | |
232 | |
233 protected OutputStream getOutputStream() throws IOException { | |
234 if (mOut == null) { | |
235 mOut = new Out(); | |
236 } | |
237 return mOut; | |
238 } | |
239 | |
240 protected int available() throws IOException { | |
241 return getInputStream().available(); | |
242 } | |
243 | |
244 protected void close() throws IOException { | |
245 if (!mClosed) { | |
246 mClosed = true; | |
247 if (mSocket != null) { | |
248 mSocket.close(); | |
249 } | |
250 } | |
251 } | |
252 | |
253 protected InetAddress getInetAddress() { | |
254 if (mSocket != null) { | |
255 return mSocket.getInetAddress(); | |
256 } | |
257 else { | |
258 return mFactory.getInetAddressAndPort(mSession).getInetAddress(); | |
259 } | |
260 } | |
261 | |
262 protected int getPort() { | |
263 if (mSocket != null) { | |
264 return mSocket.getPort(); | |
265 } | |
266 else { | |
267 return mFactory.getInetAddressAndPort(mSession).getPort(); | |
268 } | |
269 } | |
270 | |
271 protected int getLocalPort() { | |
272 if (mSocket != null) { | |
273 return mSocket.getLocalPort(); | |
274 } | |
275 else { | |
276 return -1; | |
277 } | |
278 } | |
279 | |
280 Socket createSocket() throws SocketException { | |
281 if (mSocket != null) { | |
282 return mSocket; | |
283 } | |
284 | |
285 if (mClosed) { | |
286 throw new SocketException("Socket is closed"); | |
287 } | |
288 | |
289 mSocket = mFactory.createSocket(mSession, mTimeout); | |
290 applyOptions(); | |
291 | |
292 return mSocket; | |
293 } | |
294 | |
295 Socket getSocket(byte[] data, int off, int len) | |
296 throws SocketException | |
297 { | |
298 if (mSocket != null) { | |
299 return mSocket; | |
300 } | |
301 | |
302 if (mClosed) { | |
303 throw new SocketException("Socket is closed"); | |
304 } | |
305 | |
306 long timeout = mTimeout; | |
307 long start; | |
308 if (timeout > 0) { | |
309 start = System.currentTimeMillis(); | |
310 } | |
311 else { | |
312 start = 0; | |
313 } | |
314 | |
315 try { | |
316 mSocket = mFactory.getSocket(mSession, timeout); | |
317 applyOptions(); | |
318 OutputStream out = mSocket.getOutputStream(); | |
319 out.write(data, off, len); | |
320 out.flush(); | |
321 } | |
322 catch (Exception e) { | |
323 if (mSocket != null) { | |
324 try { | |
325 mSocket.close(); | |
326 } | |
327 catch (Exception e2) { | |
328 } | |
329 } | |
330 | |
331 if (timeout > 0) { | |
332 timeout = timeout - (System.currentTimeMillis() - start); | |
333 if (timeout < 0) { | |
334 timeout = 0; | |
335 } | |
336 } | |
337 | |
338 mSocket = mFactory.createSocket(mSession, timeout); | |
339 applyOptions(); | |
340 try { | |
341 OutputStream out = mSocket.getOutputStream(); | |
342 out.write(data, off, len); | |
343 out.flush(); | |
344 } | |
345 catch (IOException e2) { | |
346 throw new SocketException(e2.getMessage()); | |
347 } | |
348 } | |
349 | |
350 return mSocket; | |
351 } | |
352 | |
353 private void applyOptions() throws SocketException { | |
354 if (mOptions == null || mSocket == null) { | |
355 return; | |
356 } | |
357 | |
358 Object[] options = mOptions; | |
359 Object value; | |
360 | |
361 if ((value = options[0]) != null) { | |
362 mSocket.setTcpNoDelay(((Boolean)value).booleanValue()); | |
363 } | |
364 if ((value = options[1]) != null) { | |
365 if (value instanceof Boolean) { | |
366 mSocket.setSoLinger(((Boolean)value).booleanValue(), 0); | |
367 } | |
368 else { | |
369 mSocket.setSoLinger(true, ((Integer)value).intValue()); | |
370 } | |
371 } | |
372 if ((value = options[2]) != null) { | |
373 mSocket.setSoTimeout(((Integer)value).intValue()); | |
374 } | |
375 if ((value = options[3]) != null) { | |
376 mSocket.setSendBufferSize(((Integer)value).intValue()); | |
377 } | |
378 if ((value = options[4]) != null) { | |
379 mSocket.setReceiveBufferSize(((Integer)value).intValue()); | |
380 } | |
381 } | |
382 | |
383 protected void create(boolean stream) throws IOException { | |
384 error(); | |
385 } | |
386 | |
387 protected void connect(String host, int port) throws IOException { | |
388 error(); | |
389 } | |
390 | |
391 protected void connect(InetAddress host, int port) throws IOException { | |
392 error(); | |
393 } | |
394 | |
395 protected void connect(SocketAddress host, | |
396 int port) throws IOException { | |
397 error(); | |
398 } | |
399 | |
400 protected void bind(InetAddress host, int port) throws IOException { | |
401 error(); | |
402 } | |
403 | |
404 protected void listen(int backlog) throws IOException { | |
405 error(); | |
406 } | |
407 | |
408 protected void accept(SocketImpl s) throws IOException { | |
409 error(); | |
410 } | |
411 | |
412 protected void sendUrgentData(int aByte) throws IOException { | |
413 throw new UnsupportedOperationException(); | |
414 } | |
415 | |
416 private void error() throws IOException { | |
417 throw new IOException("Unsupported operation"); | |
418 } | |
419 | |
420 private class In extends InputStream { | |
421 private InputStream mStream; | |
422 | |
423 public int read() throws IOException { | |
424 return getStream().read(); | |
425 } | |
426 | |
427 public int read(byte[] b) throws IOException { | |
428 return getStream().read(b); | |
429 } | |
430 | |
431 public int read(byte[] b, int off, int len) throws IOException { | |
432 return getStream().read(b, off, len); | |
433 } | |
434 | |
435 public long skip(long n) throws IOException { | |
436 return getStream().skip(n); | |
437 } | |
438 | |
439 public int available() throws IOException { | |
440 return getStream().available(); | |
441 } | |
442 | |
443 public void close() throws IOException { | |
444 if (mStream != null) { | |
445 mStream.close(); | |
446 } | |
447 Impl.this.close(); | |
448 } | |
449 | |
450 public void mark(int readlimit) { | |
451 try { | |
452 getStream().mark(readlimit); | |
453 } | |
454 catch (IOException e) { | |
455 } | |
456 } | |
457 | |
458 public void reset() throws IOException { | |
459 if (mStream == null) { | |
460 throw new IOException("Stream not marked"); | |
461 } | |
462 else { | |
463 mStream.reset(); | |
464 } | |
465 } | |
466 | |
467 public boolean markSupported() { | |
468 try { | |
469 return getStream().markSupported(); | |
470 } | |
471 catch (IOException e) { | |
472 return false; | |
473 } | |
474 } | |
475 | |
476 private InputStream getStream() throws IOException { | |
477 if (mStream == null) { | |
478 mStream = createSocket().getInputStream(); | |
479 } | |
480 return mStream; | |
481 } | |
482 } | |
483 | |
484 private class Out extends OutputStream { | |
485 private OutputStream mStream; | |
486 | |
487 public void write(int b) throws IOException { | |
488 write(new byte[] {(byte)b}, 0, 1); | |
489 } | |
490 | |
491 public void write(byte[] b) throws IOException { | |
492 write(b, 0, b.length); | |
493 } | |
494 | |
495 public void write(byte[] b, int off, int len) throws IOException { | |
496 if (mStream == null) { | |
497 mStream = getSocket(b, off, len).getOutputStream(); | |
498 } | |
499 else { | |
500 mStream.write(b, off, len); | |
501 } | |
502 } | |
503 | |
504 public void flush() throws IOException { | |
505 if (mStream != null) { | |
506 mStream.flush(); | |
507 } | |
508 } | |
509 | |
510 public void close() throws IOException { | |
511 if (mStream != null) { | |
512 mStream.close(); | |
513 } | |
514 Impl.this.close(); | |
515 } | |
516 } | |
517 } | |
518 } |