001: // SocketClient.java
002: // $Id: SocketClient.java,v 1.25 2004/08/30 16:04:44 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.jigsaw.http.socket;
007:
008: import java.io.BufferedInputStream;
009: import java.io.DataOutputStream;
010: import java.io.IOException;
011: import java.io.PrintStream;
012:
013: import java.net.InetAddress;
014: import java.net.Socket;
015: import java.net.SocketException;
016:
017: import org.w3c.jigsaw.http.Client;
018: import org.w3c.jigsaw.http.ClientException;
019: import org.w3c.jigsaw.http.httpd;
020:
021: import org.w3c.tools.resources.ServerInterface;
022:
023: /**
024: * This class implements the object that handles client connections.
025: * One such object exists per open connections at any given time.
026: * <p>The basic architecture is the following: the httpd instance accepts
027: * new connections on its port. When such a connection is accepted a Client
028: * object is requested to the client pool (which can implement what ever
029: * strategy is suitable). Each request is than managed by looking up an
030: * resource and invoking the resource methods corresponding to the request.
031: * @see org.w3c.jigsaw.http.httpd
032: * @see org.w3c.jigsaw.http.Request
033: * @see org.w3c.jigsaw.http.Reply
034: */
035:
036: public class SocketClient extends Client implements Runnable {
037: private static final boolean trace = false;
038:
039: /**
040: * The ClientFactory that created this client.
041: */
042: private SocketClientFactory pool = null;
043: /**
044: * The socket currently handled by the client.
045: */
046: protected Socket socket = null;
047: /**
048: * Is this client still alive ?
049: */
050: protected boolean alive = false;
051: /**
052: * The client state for this client, has managed by the SocketClientFactory
053: * @see SocketClientFactory
054: */
055: SocketClientState state = null;
056: /**
057: * Number of times this client was bound to a connection.
058: */
059: protected int bindcount = 0;
060: /**
061: * The thread that we have been attached to.
062: */
063: protected Thread thread = null;
064: /**
065: * Our reusable output buffer.
066: */
067: protected SocketOutputBuffer bufout = null;
068: /**
069: * Our we idle (waiting for next request ?)
070: */
071: protected boolean idle = false;
072:
073: /**
074: * are we done?
075: */
076: protected boolean done = false;
077:
078: /**
079: * Print that client into a String.
080: * @return A String instance.
081: */
082:
083: public String toString() {
084: if (thread != null)
085: return "client-" + state.id + "(" + thread.getName() + ")";
086: else
087: return "client-" + state.id;
088: }
089:
090: /**
091: * If this client is allocated a thread, join it.
092: */
093:
094: public void join() {
095: if (thread != null) {
096: while (true) {
097: try {
098: thread.join();
099: } catch (InterruptedException ex) {
100: }
101: }
102: }
103: }
104:
105: /**
106: * Run for our newly attached connection.
107: * @return A boolean, <strong>true</strong> if the client is to be killed
108: * as decided by its SocketClientFactory creator.
109: */
110:
111: public void run() {
112: thread = Thread.currentThread();
113: if (trace)
114: System.out.println(this + ": powered by " + thread);
115: try {
116: if (bufout == null) {
117: // Make sure that buffer is a little smaller then the client
118: // buffer. so writing to it will not copy it
119: int bufsize = getServer().getClientBufferSize() - 1;
120: bufout = new SocketOutputBuffer(socket
121: .getOutputStream(), bufsize);
122: } else {
123: bufout.reuse(socket.getOutputStream());
124: }
125: startConnection(new BufferedInputStream(socket
126: .getInputStream()), new DataOutputStream(bufout));
127: } catch (IOException ex) {
128: if (debug)
129: ex.printStackTrace();
130: } catch (ClientException ex) {
131: if (debug)
132: ex.printStackTrace();
133: // Emit some debugging traces:
134: if (debug) {
135: if (ex.ex != null)
136: ex.ex.printStackTrace();
137: else
138: ex.printStackTrace();
139: }
140: // If output is null, we have killed the connection...
141: if (alive && !idle) {
142: error("caught ClientException: ["
143: + ex.getClass().getName() + "] "
144: + ex.getMessage());
145: }
146: } catch (Exception ex) {
147: if (debug) {
148: System.out
149: .println("unknown exception caught in client run");
150: ex.printStackTrace();
151: }
152: } finally {
153: if (!pool.clientConnectionFinished(this )) {
154: pool.clientFinished(this );
155: }
156: thread = null;
157: }
158: }
159:
160: /**
161: * Client implementation - Get the IP address of this client.
162: * @return An InetAddress instance, or <strong>null</strong> if the
163: * client is not currently running.
164: */
165:
166: public InetAddress getInetAddress() {
167: return (socket != null) ? socket.getInetAddress() : null;
168: }
169:
170: /**
171: * Client implementation - This connection has been stopped.
172: * Make sure the whole socket is closed, and be ready to handle
173: * next connection.
174: */
175:
176: protected void stopConnection() {
177: if (trace)
178: System.out.println(this + ": stopConnection.");
179: if (socket != null) {
180: try {
181: socket.close();
182: } catch (Exception ex) {
183: }
184: socket = null;
185: // alive = false;
186: // if (!pool.idleClientRemove(this)) {
187: // pool.clientFinished(this);
188: // }
189: }
190: }
191:
192: /**
193: * Get the thread powering that client.
194: * @return A Thread instance, or <strong>null</strong>.
195: */
196:
197: protected Thread getThread() {
198: return thread;
199: }
200:
201: /**
202: * Client implementation - The current connection is idle.
203: * The client is about to wait for the next request from the net, mark
204: * it as idle to make it a candidate for persistent connection closing.
205: * @return A boolean, if <strong>true</strong> our client factory wants
206: * us to stop right now, in order to handle some other connection.
207: */
208:
209: protected boolean idleConnection() {
210: synchronized (state) {
211: if (trace)
212: System.out.println(this + ": idleConnection.");
213: idle = true;
214: return !pool.notifyIdle(this );
215: }
216: }
217:
218: /**
219: * Client implementation - The current connection is in use.
220: * A request is about to be processed, mark that connection as used, to
221: * remove it from the idle state.
222: */
223:
224: protected void usedConnection() {
225: synchronized (state) {
226: if (trace)
227: System.out.println(this + ": usedConnection.");
228: idle = false;
229: pool.notifyUse(this );
230: }
231: }
232:
233: /**
234: * SocketClientFactory interface - Bind the socket to this client.
235: * Binding a socket to a client triggers the processing of the underlying
236: * connection. It is assumed that the client was ready to handle a new
237: * connection before this method was called.
238: * @param socket The socket this client should now handle.
239: */
240:
241: protected synchronized void bind(Socket socket) {
242: done = false;
243: ServerInterface server = getServer();
244: if (trace)
245: System.out.println(this + ": bind.");
246: this .socket = socket;
247: try {
248: // socket.setSoTimeout(server.getRequestTimeOut());
249: socket.setSoTimeout(pool.timeout);
250: } catch (SocketException ex) {
251: if (trace)
252: ex.printStackTrace();
253: server.errlog("Unable to set socket timeout!");
254: }
255: this .idle = false;
256: bindcount++;
257: pool.run(this );
258: }
259:
260: /**
261: * SocketClientFactory interface - Unbind this client.
262: * This client is handling an idle connection, unbind it to make
263: * it free for handling some other more buzy connection.
264: */
265:
266: protected synchronized void unbind() {
267: if (trace)
268: System.out.println(this + ": unbind.");
269: interruptConnection(true);
270: }
271:
272: /**
273: * SocketClientFactory interface - Kill this client.
274: * The clean way for our client factory to shut us down unconditionally.
275: * This will free all resources acquired by this client, stop the current
276: * connection processing if needed, and terminate the underlying thread.
277: */
278:
279: protected synchronized void kill(boolean now) {
280: if (trace)
281: System.out.println(this + ": kill.");
282: alive = false;
283: interruptConnection(now);
284: }
285:
286: /**
287: * Get the total number of times this client was bound to a socket.
288: * @return An integer, indicatingthe number of bind calls on this client.
289: */
290:
291: public final int getBindCount() {
292: return bindcount;
293: }
294:
295: /**
296: * Create an empty client, that will be ready to work.
297: * The created client will run and wait for it to be <code>bind</code>
298: * to some socket before proceeding.
299: * @param server The server to which this client is attached.
300: * @param id The client identifier.
301: * @see org.w3c.jigsaw.http.Client
302: * @see org.w3c.jigsaw.http.ClientFactory
303: */
304:
305: protected SocketClient(httpd server, SocketClientFactory pool,
306: SocketClientState state) {
307: initialize(server, state.id);
308: this .socket = null;
309: this .pool = pool;
310: this .state = state;
311: this .alive = true;
312: }
313:
314: }
|