001: // HttpBasicConnection.java
002: // $Id: HttpBasicConnection.java,v 1.29 2007/02/11 18:40:08 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.www.protocol.http;
007:
008: import java.lang.reflect.Method;
009: import java.lang.reflect.InvocationTargetException;
010:
011: import java.io.BufferedInputStream;
012: import java.io.BufferedOutputStream;
013: import java.io.IOException;
014: import java.io.InputStream;
015: import java.io.OutputStream;
016: import java.io.PrintStream;
017:
018: import java.net.InetAddress;
019: import java.net.Socket;
020:
021: import org.w3c.www.mime.MimeParser;
022: import org.w3c.www.mime.MimeParserFactory;
023:
024: import org.w3c.www.http.HttpStreamObserver;
025:
026: class HttpBasicConnection extends HttpConnection implements
027: HttpStreamObserver {
028: private static final boolean debug = false;
029:
030: static Method sock_m = null;
031: static {
032: try {
033: Class c = java.net.Socket.class;
034: sock_m = c.getMethod("isClosed", (Class[]) null);
035: } catch (NoSuchMethodException ex) {
036: // not using a recent jdk...
037: sock_m = null;
038: }
039: }
040:
041: // A small class that start a thread to create a socket
042: // while the original thread waits only for some amount of time
043: // and indicate the other thread (if not finished) that the newly
044: // created socket can be destroyed if it took too long.
045: private class TimedSocket implements Runnable {
046: Socket _sock = null;
047: InetAddress _inetaddr = null;
048: int _port = 0;
049: boolean _ok = true;
050: IOException _ioex = null;
051:
052: public synchronized Socket getSocket(InetAddress inetaddr,
053: int port) throws IOException {
054: _inetaddr = inetaddr;
055: _port = port;
056: Thread t = new Thread(this );
057: t.start();
058:
059: try {
060: t.join(connect_timeout);
061: } catch (InterruptedException iex) {
062: _ok = false;
063: if (_sock != null) {
064: try {
065: _sock.close();
066: } catch (IOException ioex) {
067: }
068: ;
069: _sock = null;
070: }
071: }
072: if (_sock != null) {
073: return _sock;
074: }
075: _ok = false;
076: // should we interrupt the other thread here?
077: if (_ioex != null) {
078: throw _ioex;
079: } else {
080: throw new IOException("Connect timed out");
081: }
082: }
083:
084: public void run() {
085: Socket s = null;
086: try {
087: s = new Socket(inetaddr, port);
088: } catch (IOException ex) {
089: _ioex = ex;
090: }
091: if (_ok) {
092: _sock = s;
093: } else {
094: try {
095: if (s != null) {
096: s.close();
097: }
098: } catch (IOException ioex) {
099: }
100: ;
101: }
102: }
103: }
104:
105: /**
106: * The physical socket underlying the connection.
107: */
108: private Socket socket = null;
109: /**
110: * The MIME parser to read input from the connection.
111: */
112: MimeParser parser = null;
113: /**
114: * THe socket output stream, when available.
115: */
116: OutputStream output = null;
117: /**
118: * The socket input stream when available.
119: */
120: InputStream input = null;
121: /**
122: * The MimeParser factory to use to create Reply instances.
123: */
124: MimeParserFactory reply_factory = null;
125: /**
126: * The thread that owns the connection, for checking assertions.
127: */
128: Thread th = null;
129: /**
130: * The target INET address of this connection.
131: */
132: InetAddress inetaddr = null;
133: /**
134: * The target port number for this connection.
135: */
136: int port = -1;
137: /**
138: * The Timout on the underlying socket
139: */
140: int timeout = 300000;
141: /**
142: * The Connection timeout for the underlying socket
143: */
144: int connect_timeout = 3000;
145: /**
146: * All connections are associated with a uniq identifier, for debugging.
147: */
148: protected int id = -1;
149: /**
150: * if a close is needed at the end of the connection (ie: on a
151: * Connection: close client or server side
152: */
153: protected boolean closeOnEOF = false;
154: /**
155: * Old thread (same thread will try to reuse the same connection)
156: */
157: protected Thread old_th = null;
158:
159: protected synchronized void setCloseOnEOF(boolean doit) {
160: closeOnEOF = doit;
161: }
162:
163: /**
164: * Print this connection into a String.
165: * @return A String containing the external representation for the
166: * connection.
167: */
168:
169: public String toString() {
170: return inetaddr + ":" + port + "[" + id + "]";
171: }
172:
173: /**
174: * The entity stream we observe has reached its end.
175: * Notify the server that it can now reuse the connection safely for some
176: * other pending requests.
177: * @param in The stream that has reached its end of file.
178: */
179:
180: public synchronized void notifyEOF(InputStream in) {
181: markIdle(closeOnEOF);
182: }
183:
184: /**
185: * The entity stream we were to observe refuse to be observed.
186: * The connection will not be reusable, so we should detach it from the
187: * managing server, without closing it, since the entity reader will
188: * close it itself.
189: * @param in The stream that has been closed.
190: */
191:
192: public synchronized void notifyFailure(InputStream in) {
193: markIdle(true);
194: }
195:
196: /**
197: * The entity stream we observe has been closed.
198: * After making sure the entire entity has been read, we can safely hand
199: * out the connection to the server, for later reuse.
200: * @param in The stream that has been closed.
201: */
202:
203: public synchronized void notifyClose(InputStream in) {
204: boolean _close = false;
205: try {
206: if (in.available() > 0) {
207: _close = true;
208: }
209: // try {
210: // byte buffer[] = new byte[1024];
211: // while (in.read(buffer) >= 0) {
212: // }
213: } catch (IOException ex) {
214: _close = true;
215: } finally {
216: markIdle(_close);
217: }
218:
219: }
220:
221: /**
222: * Close this connection to terminate it.
223: * This method will only close the streams, and free all the data
224: * structures that it keeps.
225: */
226:
227: public synchronized void close() {
228: close(false);
229: }
230:
231: /**
232: * Close this connection to terminate it.
233: * This method will only close the streams, and free all the data
234: * structures that it keeps.
235: */
236:
237: private synchronized void close(boolean force) {
238: boolean doit = (socket != null || force);
239: // Close the socket:
240: try {
241: if (socket != null) {
242: socket.close();
243: }
244: } catch (IOException ex) {
245: }
246: socket = null;
247: // Mark all data as invalid:
248: output = null;
249: input = null;
250: parser = null;
251: cached = false;
252: th = null;
253: old_th = null;
254: if (doit) {
255: // Mark that connection as dead:
256: ((HttpBasicServer) server).deleteConnection(this );
257: }
258: }
259:
260: /**
261: * Used only when we can't evaluate the end of the connection.
262: * In that case, we are just unregistering it, and wait for the GC
263: * to clean the mess afterwards.
264: * This method will not close the stream, but will free all the data
265: * structures that it keeps to help the GC.
266: */
267:
268: protected synchronized void detach() {
269: boolean doit = (socket != null);
270: // wait for the socket to be GCed
271: socket = null;
272: // Mark all data as invalid:
273: output = null;
274: input = null;
275: parser = null;
276: cached = false;
277: th = null;
278: old_th = null;
279: if (doit) {
280: // Mark that connection as dead:
281: ((HttpBasicServer) server).deleteConnection(this );
282: }
283: }
284:
285: /**
286: * Mark this connection as being used.
287: * The server, which keeps track of idle connections, has decided to use
288: * this connection to run some request. Mark this connection as used
289: * and unregister it from the server's list of idle connections.
290: * <p>Some assumptions are checked before handing out the connection
291: * for use, which can throw an RuntimeException.
292: * @return A boolean, <strong>true</strong> if the connection can be used
293: * or reused, <strong>false</strong> otherwise (the connection was detected
294: * idle, and destroy itself).
295: * @exception RuntimeException If the connection is in an invalid state.
296: */
297:
298: public boolean markUsed() {
299: cached = false;
300: if (debug)
301: System.out.println(this + " used !");
302: if (th != null)
303: throw new RuntimeException(this + " already used by " + th);
304: th = Thread.currentThread();
305: if (socket != null) {
306: cached = true;
307: if (sock_m != null) {
308: try {
309: // check if socket is good using jdk1.4 feature
310: // would be faster to do it natively...
311: Boolean b = (Boolean) sock_m.invoke(socket,
312: (Object[]) null);
313: if (debug) {
314: System.out.println("Socket is closed? " + b);
315: }
316: if (b.booleanValue()) {
317: try {
318: socket.close();
319: } catch (IOException ieox) {
320: }
321: socket = null;
322: }
323: } catch (InvocationTargetException ex) {
324: if (debug)
325: ex.printStackTrace();
326: // weird, let's close it
327: try {
328: socket.close();
329: } catch (IOException ieox) {
330: }
331: socket = null;
332: } catch (IllegalAccessException ex) {
333: // weird also here :)
334: if (debug)
335: ex.printStackTrace();
336: try {
337: socket.close();
338: } catch (IOException ieox) {
339: }
340: socket = null;
341: } catch (Exception fex) {
342: try {
343: socket.close();
344: } catch (IOException ieox) {
345: }
346: socket = null;
347: }
348: }
349: }
350: // damn... jdk1.4 is not behaving as it should :(
351: if (socket == null) {
352: try {
353: TimedSocket ts = new TimedSocket();
354: socket = ts.getSocket(inetaddr, port);
355: socket.setSoTimeout(timeout);
356: output = new BufferedOutputStream(socket
357: .getOutputStream());
358: input = new BufferedInputStream(socket.getInputStream());
359: parser = new MimeParser(input, reply_factory);
360: cached = false;
361: } catch (Throwable ex) {
362: if (debug) {
363: ex.printStackTrace();
364: }
365: // Close that connection (cleanup):
366: close(true);
367: return false;
368: }
369: }
370: return true;
371: }
372:
373: /**
374: * The connection is now idle again.
375: * Mark the connection as idle, and register it to the server's list of
376: * idle connection (if this connection can be reused). If the connection
377: * cannot be reused, detach it from the server and forget about it (the
378: * caller will close it by closing the entity stream).
379: * @param close Should this connection be physically closed (it is not
380: * reusable), or should we try to keep track of it for later reuse.
381: * @exception RuntimeException If the connection is in an invalid state.
382: */
383:
384: public void markIdle(boolean close) {
385: // Has this connection already been marked idle ?
386: synchronized (this ) {
387: if (th == null)
388: return;
389: if (debug)
390: System.out.println(this + " idle !" + close);
391: // Check consistency:
392: // if ( Thread.currentThread() != th )
393: // throw new RuntimeException(this +
394: // " th mismatch " +
395: // th +
396: // "/" +
397: // Thread.currentThread());
398: // Ok, mark idle for good:
399: old_th = th;
400: th = null;
401: }
402: if (close) {
403: if (debug)
404: System.out.println(this + " closing !");
405: close();
406: } else {
407: // Notify the server that a new connection is available:
408: ((HttpBasicServer) server).registerConnection(this );
409: }
410: }
411:
412: /**
413: * Some data available on input, while writing to the server.
414: * This callback gets called when the client is emitting data to the
415: * server and the server has sent us something before we actually sent
416: * all our bytes.
417: * <p>Take any appropriate action.
418: */
419:
420: public void notifyInputAvailable(InputStream in) {
421: return;
422: }
423:
424: /**
425: * Get the MIME parser to read from this connection.
426: * All access to the connection's input stream should go through the MIME
427: * parser to ensure buffering coherency.
428: * @return A MimeParser instance suitable to parse the reply input stream.
429: * @exception RuntimeException If the connection was not connected.
430: */
431:
432: public MimeParser getParser() {
433: if (parser == null)
434: throw new RuntimeException("getParser while disconnected.");
435: return parser;
436: }
437:
438: /**
439: * Get the connection output stream.
440: * @return The output stream to send data on this connection.
441: * @exception RuntimeException If the connection was not previously opened.
442: */
443:
444: public OutputStream getOutputStream() {
445: if (output == null)
446: throw new RuntimeException(
447: "getOutputStream while disconnected.");
448: return output;
449: }
450:
451: /**
452: * Can this connection be reused as a first choice when requested?
453: * This is only a hint, as if all connections fail, the first one will
454: * be forced by default
455: * @return a boolean, true by default
456: */
457: protected boolean mayReuse() {
458: if (old_th != null) {
459: Thread cur_th = Thread.currentThread();
460: if (old_th == cur_th) {
461: return true;
462: }
463: return false;
464: }
465: return true;
466: }
467:
468: public void finalize() {
469: if (socket != null) {
470: if (debug) {
471: System.out
472: .println("HttpBasicConnection closed in finalize");
473: }
474: close();
475: }
476: }
477:
478: /**
479: * Create a new connection.
480: * To be used only by HttpServer instances.
481: */
482:
483: HttpBasicConnection(HttpServer server, int id, InetAddress addr,
484: int port, int timeout, MimeParserFactory reply_factory)
485: throws IOException {
486: this (server, id, addr, port, timeout, 3000, reply_factory);
487: }
488:
489: /**
490: * Create a new connection.
491: * To be used only by HttpServer instances.
492: */
493:
494: HttpBasicConnection(HttpServer server, int id, InetAddress addr,
495: int port, int timeout, int connect_timeout,
496: MimeParserFactory reply_factory) throws IOException {
497: this.server = server;
498: this.inetaddr = addr;
499: this.port = port;
500: this.id = id;
501: this.timeout = timeout;
502: this.connect_timeout = connect_timeout;
503: this.reply_factory = reply_factory;
504: }
505:
506: }
|