001: // Copyright (C) 1998-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
002: // All rights reserved. Use of this class is limited.
003: // Please see the LICENSE for more information.
004:
005: package com.oreilly.servlet;
006:
007: import java.io.*;
008: import java.net.*;
009: import java.util.*;
010: import javax.servlet.*;
011: import javax.servlet.http.*;
012:
013: /**
014: * A superclass for HTTP servlets that wish to accept raw socket
015: * connections. DaemonHttpServlet
016: * starts listening for client requests in its <tt>init()</tt> method
017: * and stops listening in its <tt>destroy()</tt> method. In between,
018: * for every connection it receives, it calls the abstract
019: * <tt>handleClient(Socket client)</tt> method. This method should
020: * be implemented by the servlet subclassing DaemonHttpServlet.
021: * The port on which the servlet is to listen is determined by the
022: * <tt>getSocketPort()</tt> method.
023: *
024: * @see com.oreilly.servlet.RemoteDaemonHttpServlet
025: *
026: * @author <b>Jason Hunter</b>, Copyright © 1998
027: * @version 1.0, 98/09/18
028: */
029: public abstract class DaemonHttpServlet extends HttpServlet {
030:
031: /**
032: * The default listening port (1313)
033: */
034: protected int DEFAULT_PORT = 1313;
035: private Thread daemonThread;
036:
037: /**
038: * Begins a thread listening for socket connections. Subclasses
039: * that override this method must be sure to first call
040: * <tt>super.init(config)</tt>.
041: *
042: * @param config the servlet config
043: * @exception ServletException if a servlet exception occurs
044: */
045: public void init(ServletConfig config) throws ServletException {
046: super .init(config);
047:
048: try {
049: daemonThread = new Daemon(this );
050: daemonThread.start();
051: } catch (Exception e) {
052: log("Problem starting socket server daemon thread"
053: + e.getClass().getName() + ": " + e.getMessage());
054: }
055: }
056:
057: /**
058: * Returns the socket port on which the servlet will listen.
059: * A servlet can change the port in three ways: by using the
060: * <tt>socketPort</tt> init parameter, by setting the <tt>DEFAULT_PORT</tt>
061: * variable before calling <tt>super.init()</tt>, or by overriding this
062: * method's implementation.
063: *
064: * @return the port number on which to listen
065: */
066: protected int getSocketPort() {
067: try {
068: return Integer.parseInt(getInitParameter("socketPort"));
069: } catch (NumberFormatException e) {
070: return DEFAULT_PORT;
071: }
072: }
073:
074: /**
075: * Handles a new socket connection. Subclasses must define this method.
076: *
077: * @param client the client socket
078: */
079: abstract public void handleClient(Socket client);
080:
081: /**
082: * Halts the thread listening for socket connections. Subclasses
083: * that override this method must be sure to first call
084: * <tt>super.destroy()</tt>.
085: */
086: public void destroy() {
087: try {
088: daemonThread.stop();
089: daemonThread = null;
090: } catch (Exception e) {
091: log("Problem stopping server socket daemon thread: "
092: + e.getClass().getName() + ": " + e.getMessage());
093: }
094: }
095: }
096:
097: // This work is broken into a helper class so that subclasses of
098: // DaemonHttpServlet can define their own run() method without problems.
099:
100: class Daemon extends Thread {
101:
102: private ServerSocket serverSocket;
103: private DaemonHttpServlet servlet;
104:
105: public Daemon(DaemonHttpServlet servlet) {
106: this .servlet = servlet;
107: }
108:
109: public void run() {
110: try {
111: // Create a server socket to accept connections
112: serverSocket = new ServerSocket(servlet.getSocketPort());
113: } catch (Exception e) {
114: servlet.log("Problem establishing server socket: "
115: + e.getClass().getName() + ": " + e.getMessage());
116: return;
117: }
118:
119: try {
120: while (true) {
121: // As each connection comes in, call the servlet's handleClient().
122: // Note this method is blocking. It's the servlet's responsibility
123: // to spawn a handler thread for long-running connections.
124: try {
125: servlet.handleClient(serverSocket.accept());
126: } catch (IOException ioe) {
127: servlet
128: .log("Problem accepting client's socket connection: "
129: + ioe.getClass().getName()
130: + ": "
131: + ioe.getMessage());
132: }
133: }
134: } catch (ThreadDeath e) {
135: // When the thread is killed, close the server socket
136: try {
137: serverSocket.close();
138: } catch (IOException ioe) {
139: servlet.log("Problem closing server socket: "
140: + ioe.getClass().getName() + ": "
141: + ioe.getMessage());
142: }
143: }
144: }
145: }
|