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 005package com.oreilly.servlet; 006 007import java.io.*; 008import java.net.*; 009import java.util.*; 010import javax.servlet.*; 011import 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 */ 029public 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 } 052 catch (Exception e) { 053 log("Problem starting socket server daemon thread" + 054 e.getClass().getName() + ": " + e.getMessage()); 055 } 056 } 057 058 /** 059 * Returns the socket port on which the servlet will listen. 060 * A servlet can change the port in three ways: by using the 061 * <tt>socketPort</tt> init parameter, by setting the <tt>DEFAULT_PORT</tt> 062 * variable before calling <tt>super.init()</tt>, or by overriding this 063 * method's implementation. 064 * 065 * @return the port number on which to listen 066 */ 067 protected int getSocketPort() { 068 try { return Integer.parseInt(getInitParameter("socketPort")); } 069 catch (NumberFormatException e) { return DEFAULT_PORT; } 070 } 071 072 /** 073 * Handles a new socket connection. Subclasses must define this method. 074 * 075 * @param client the client socket 076 */ 077 abstract public void handleClient(Socket client); 078 079 /** 080 * Halts the thread listening for socket connections. Subclasses 081 * that override this method must be sure to first call 082 * <tt>super.destroy()</tt>. 083 */ 084 public void destroy() { 085 try { 086 daemonThread.stop(); 087 daemonThread = null; 088 } 089 catch (Exception e) { 090 log("Problem stopping server socket daemon thread: " + 091 e.getClass().getName() + ": " + e.getMessage()); 092 } 093 } 094} 095 096// This work is broken into a helper class so that subclasses of 097// DaemonHttpServlet can define their own run() method without problems. 098 099class Daemon extends Thread { 100 101 private ServerSocket serverSocket; 102 private DaemonHttpServlet servlet; 103 104 public Daemon(DaemonHttpServlet servlet) { 105 this.servlet = servlet; 106 } 107 108 public void run() { 109 try { 110 // Create a server socket to accept connections 111 serverSocket = new ServerSocket(servlet.getSocketPort()); 112 } 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 } 127 catch (IOException ioe) { 128 servlet.log("Problem accepting client's socket connection: " + 129 ioe.getClass().getName() + ": " + ioe.getMessage()); 130 } 131 } 132 } 133 catch (ThreadDeath e) { 134 // When the thread is killed, close the server socket 135 try { 136 serverSocket.close(); 137 } 138 catch (IOException ioe) { 139 servlet.log("Problem closing server socket: " + 140 ioe.getClass().getName() + ": " + ioe.getMessage()); 141 } 142 } 143 } 144}