Main Page | Packages | Class Hierarchy | Class List | Directories | File List | Class Members | Related Pages

Serve.java

00001 // Serve - minimal Java HTTP server class
00002 //
00003 // Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
00004 //
00005 // Redistribution and use in source and binary forms, with or without
00006 // modification, are permitted provided that the following conditions
00007 // are met:
00008 // 1. Redistributions of source code must retain the above copyright
00009 //    notice, this list of conditions and the following disclaimer.
00010 // 2. Redistributions in binary form must reproduce the above copyright
00011 //    notice, this list of conditions and the following disclaimer in the
00012 //    documentation and/or other materials provided with the distribution.
00013 //
00014 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00015 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00016 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00017 // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00018 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00019 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00020 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00021 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00022 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00023 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00024 // SUCH DAMAGE.
00025 //
00026 // Visit the ACME Labs Java page for up-to-date versions of this and other
00027 // fine Java utilities: http://www.acme.com/java/
00028 //
00029 
00030 // All enhancments Copyright (C)1998-2002 by Dmitriy Rogatkin
00031 // this version is compatible with JSDK 2.3
00032 // http://tjws.sourceforge.net
00033 // $Id: Serve.java,v 1.2 2005/07/21 13:57:08 dev Exp $
00034 
00035 package Acme.Serve;
00036 
00037 import java.io.*;
00038 import java.util.*;
00039 import java.net.*;
00040 import java.text.*;
00041 import javax.servlet.*;
00042 import javax.servlet.http.*;
00043 
00045 // <P>
00046 // This class implements a very small embeddable HTTP server.
00047 // It runs Servlets compatible with the API used by JavaSoft's
00048 // <A HREF="http://java.sun.com/products/java-server/">JavaServer</A> server.
00049 // It comes with default Servlets which provide the usual
00050 // httpd services, returning files and directory listings.
00051 // <P>
00052 // This is not in any sense a competitor for JavaServer.
00053 // JavaServer is a full-fledged HTTP server and more.
00054 // Acme.Serve is tiny, about 1500 lines, and provides only the
00055 // functionality necessary to deliver an Applet's .class files
00056 // and then start up a Servlet talking to the Applet.
00057 // They are both written in Java, they are both web servers, and
00058 // they both implement the Servlet API; other than that they couldn't
00059 // be more different.
00060 // <P>
00061 // This is actually the second HTTP server I've written.
00062 // The other one is called
00063 // <A HREF="http://www.acme.com/software/thttpd/">thttpd</A>,
00064 // it's written in C, and is also pretty small although much more
00065 // featureful than this.
00066 // <P>
00067 // Other Java HTTP servers:
00068 // <UL>
00069 // <LI> The above-mentioned <A HREF="http://java.sun.com/products/java-server/">JavaServer</A>.
00070 // <LI> W3C's <A HREF="http://www.w3.org/pub/WWW/Jigsaw/">Jigsaw</A>.
00071 // <LI> David Wilkinson's <A HREF="http://www.netlink.co.uk/users/cascade/http/">Cascade</A>.
00072 // <LI> Yahoo's <A HREF="http://www.yahoo.com/Computers_and_Internet/Software/Internet/World_Wide_Web/Servers/Java/">list of Java web servers</A>.
00073 // </UL>
00074 // <P>
00075 // A <A HREF="http://www.byte.com/art/9706/sec8/art1.htm">June 1997 BYTE magazine article</A> mentioning this server.<BR>
00076 // A <A HREF="http://www.byte.com/art/9712/sec6/art7.htm">December 1997 BYTE magazine article</A> giving it an Editor's Choice Award of Distinction.<BR>
00077 // <A HREF="/resources/classes/Acme/Serve/Serve.java">Fetch the software.</A><BR>
00078 // <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
00079 // <P>
00080 // @see Acme.Serve.servlet.http.HttpServlet
00081 // @see FileServlet
00082 // @see CgiServlet
00083 
00084 // make it final?
00085 public class Serve implements ServletContext, RequestDispatcher {
00086 
00087   private static final String progName = "Serve";
00088   public static final String ARG_PORT = "port";
00089   public static final String ARG_THROTTLES = "throttles";
00090   public static final String ARG_SERVLETS = "servlets";
00091   public static final String ARG_REALMS = "realms";
00092   public static final String ARG_ALIASES = "aliases";
00093   public static final String ARG_CGI_PATH = "cgi-path";
00094   public static final String ARG_SESSION_TIMEOUT = "session-timeout";
00095   public static final String ARG_LOG_OPTIONS = "log-options";
00096   public static final String ARG_SOCKET_FACTORY = "socketFactory";
00097 
00098   protected static final int DEF_SESSION_TIMEOUT = 30; // in minutes
00099   protected static final int DEF_PORT = 9090;
00100 
00102   public static void main(String[] args) {
00103     Hashtable arguments = new Hashtable(20);
00104 
00105     int argc = args.length;
00106     int argn;
00107     // Parse args.
00108     if (argc == 0) // a try to read from file for java -jar server.jar
00109       try {
00110         BufferedReader br = new BufferedReader(new FileReader("cmdparams"));
00111         StringTokenizer st = new StringTokenizer(br.readLine(), " ");
00112         args = new String[st.countTokens()];
00113         argc = args.length; // tail can be nulled
00114         for (int i = 0; i < argc && st.hasMoreTokens(); i++)
00115           args[i] = st.nextToken();
00116       } catch (Exception e) { // many can happen
00117       }
00118     for (argn = 0; argn < argc && args[argn].charAt(0) == '-';) {
00119       if (args[argn].equals("-p") && argn + 1 < argc) {
00120         ++argn;
00121         arguments.put(ARG_PORT, new Integer(args[argn]));
00122       } else if (args[argn].equals("-t") && argn + 1 < argc) {
00123         ++argn;
00124         arguments.put(ARG_THROTTLES, args[argn]);
00125       } else if (args[argn].equals("-s") && argn + 1 < argc) {
00126         ++argn;
00127         arguments.put(ARG_SERVLETS, args[argn]);
00128       } else if (args[argn].equals("-r") && argn + 1 < argc) {
00129         ++argn;
00130         arguments.put(ARG_REALMS, args[argn]);
00131       } else if (args[argn].equals("-a") && argn + 1 < argc) {
00132         ++argn;
00133         arguments.put(ARG_ALIASES, args[argn]);
00134       } else if (args[argn].equals("-c") && argn + 1 < argc) {
00135         ++argn;
00136         arguments.put(ARG_CGI_PATH, args[argn]);
00137       } else if (args[argn].equals("-e") && argn + 1 < argc) {
00138         ++argn;
00139         try {
00140           arguments.put(ARG_SESSION_TIMEOUT, new Integer(args[argn]));
00141         } catch (NumberFormatException nfe) {
00142         }
00143       } else if (args[argn].startsWith("-l")) {
00144         if (args[argn].length() > 2)
00145           arguments.put(ARG_LOG_OPTIONS, args[argn].substring(2).toUpperCase());
00146         else
00147           arguments.put(ARG_LOG_OPTIONS, "");
00148       } else if (args[argn].startsWith("-")) {
00149         if (args[argn].length() > 1)
00150           arguments.put(args[argn].substring(1),
00151           //.toUpperCase(),
00152           argn < argc - 1 ? args[++argn] : "");
00153       } else
00154         usage();
00155 
00156       ++argn;
00157     }
00158     if (argn != argc)
00159       usage();
00163     PrintStream printstream = System.err;
00164     try {
00165       printstream = new PrintStream(new FileOutputStream("AWS-" + System.currentTimeMillis() + ".log"), true);
00166       System.setErr(printstream);
00167     } catch (IOException e) {
00168       System.err.println("IO problem at setting a log stream " + e);
00169     }
00170     PathTreeDictionary mappingtable = new PathTreeDictionary();
00171     if (arguments.get(ARG_ALIASES) != null) {
00172       File file = new File((String) arguments.get(ARG_ALIASES));
00173       if (file.exists() && file.canRead()) {
00174         try {
00175           DataInputStream in = new DataInputStream(new FileInputStream(file));
00176           do {
00177             String mappingstr = in.readLine();
00178             if (mappingstr == null)
00179               break;
00180             StringTokenizer maptokenzr = new StringTokenizer(mappingstr, "=;");
00181             if (maptokenzr.hasMoreTokens()) {
00182               if (maptokenzr.nextToken("=").equalsIgnoreCase("from")) {
00183                 if (maptokenzr.hasMoreTokens()) {
00184                   String srcpath = maptokenzr.nextToken("=;");
00185                   if (maptokenzr.hasMoreTokens() && maptokenzr.nextToken(";=").equalsIgnoreCase("dir"))
00186                     try {
00187                       if (maptokenzr.hasMoreTokens())
00188                         mappingtable.put(srcpath, maptokenzr.nextToken());
00189                     } catch (NullPointerException e) {
00190                     }
00191                 }
00192               }
00193             }
00194           } while (true);
00195         } catch (IOException e) {
00196           System.err.println("Problem reading aliases file: " + arguments.get(ARG_ALIASES) + "/" + e);
00197         }
00198       } else
00199         System.err.println("FIle " + arguments.get(ARG_ALIASES) + " doesn't exist or not readable.");
00200     }
00203     PathTreeDictionary realms = new PathTreeDictionary();
00204     if (arguments.get(ARG_REALMS) != null) {
00205       try {
00206         DataInputStream in = new DataInputStream(new FileInputStream((String) arguments.get(ARG_REALMS)));
00207 
00208         do {
00209           String realmstr = in.readLine();
00210           if (realmstr == null)
00211             break;
00212           StringTokenizer rt = new StringTokenizer(realmstr, "=,:");
00213           String realmname = null;
00214           BasicAuthRealm realm = null;
00215           if (rt.hasMoreTokens())
00216             realmname = rt.nextToken();
00217           else
00218             continue;
00219           if (rt.hasMoreTokens()) {
00220             realm = new BasicAuthRealm(realmname);
00221             realms.put(rt.nextToken(), realm);
00222           } else
00223             continue;
00224           if (rt.hasMoreTokens()) {
00225             String user = rt.nextToken();
00226             if (rt.hasMoreTokens())
00227               realm.put(user, rt.nextToken());
00228           }
00229         } while (true);
00230       } catch (IOException ioe) {
00231         System.err.println("Problem in reading realms file " + arguments.get(ARG_REALMS) + "/ " + ioe);
00232       }
00233     }
00234 
00235     // Create the server.
00236     final Serve serve = new Serve(arguments, printstream);
00237     final String cf = (String) arguments.get(ARG_SERVLETS);
00238     serve.setMappingTable(mappingtable);
00239     serve.setRealms(realms);
00240 
00241     new Thread(new Runnable() {
00242       public void run() {
00243         serve.readServlets(cf);
00244       }
00245     }).start();
00246     // And add the standard Servlets.
00247     String throttles = (String) arguments.get(ARG_THROTTLES);
00248     if (throttles == null)
00249       serve.addDefaultServlets((String) arguments.get(ARG_CGI_PATH));
00250     else
00251       try {
00252         serve.addDefaultServlets((String) arguments.get(ARG_CGI_PATH), throttles);
00253       } catch (IOException e) {
00254         System.err.println("Problem reading throttles file: " + e);
00255         System.exit(1);
00256       }
00257 
00258     // And run.
00259     serve.serve();
00260 
00261     System.exit(0);
00262   }
00263 
00264   
00265   
00266   
00267   
00268   
00269   
00270   
00271   private static void usage() {
00272     System.err.println(
00273       "usage:  "
00274         + progName
00275         + " [-p port] [-s servletpropertiesfile] [-a aliasmappingfile] [-l[a][r]] [-c cgi-bin-dir] [-e [-]duration_in_minutes] [-socketFactory class name and other parameters}");
00276     System.exit(1);
00277   }
00278 
00279   private void readServlets(String cfgfile) {
00284     Hashtable servletstbl, parameterstbl;
00285     servletstbl = new Hashtable();
00286     parameterstbl = new Hashtable();
00287     if (cfgfile != null) {
00288       File file = new File(cfgfile);
00289       if (file.exists() && file.canRead()) {
00290         try {
00291           DataInputStream in = new DataInputStream(new FileInputStream(file));
00295           do {
00296             String servletdsc = in.readLine();
00297             if (servletdsc == null)
00298               break;
00299             StringTokenizer dsctokenzr = new StringTokenizer(servletdsc, ".=,", false);
00300             if (dsctokenzr.hasMoreTokens()) {
00301               if (!dsctokenzr.nextToken().equalsIgnoreCase("servlet")) {
00302                 System.err.println("No leading 'servlet' keyword, the sentence is skipped");
00303                 break;
00304               }
00305               if (dsctokenzr.hasMoreTokens()) {
00306                 String servletname = dsctokenzr.nextToken();
00307 
00308                 if (dsctokenzr.hasMoreTokens()) {
00309                   String lt = dsctokenzr.nextToken();
00310                   if (lt.equalsIgnoreCase("code")) {
00311                     if (dsctokenzr.hasMoreTokens())
00312                       servletstbl.put(servletname, dsctokenzr.nextToken("="));
00313                   } else if (lt.equalsIgnoreCase("initArgs")) {
00314                     Hashtable initparams = new Hashtable();
00315                     while (dsctokenzr.hasMoreTokens()) {
00316                       String key = dsctokenzr.nextToken("=,");
00317                       if (dsctokenzr.hasMoreTokens())
00318                         initparams.put(key, dsctokenzr.nextToken(",="));
00319                     }
00320                     parameterstbl.put(servletname, initparams);
00321                   } else
00322                     System.err.println("Unrecognized token " + lt + " in " + servletdsc + ", the line's skipped");
00323                 }
00324               }
00325             }
00326           }
00327           while (true);
00328         } catch (IOException e) {
00329           System.err.println("Problem reading cfg file: " + e);
00330         }
00331         Enumeration se = servletstbl.keys();
00332         String servletname;
00333         while (se.hasMoreElements()) {
00334           servletname = (String) se.nextElement();
00335           addServlet(servletname, (String) servletstbl.get(servletname), (Hashtable) parameterstbl.get(servletname));
00336         }
00337       }
00338     }
00339   }
00340 
00341   int port;
00342   String hostName;
00343   private PrintStream logStream;
00344   private boolean useAccLog;
00345   private boolean showUserAgent;
00346   private boolean showReferer;
00347   protected PathTreeDictionary registry;
00348   protected PathTreeDictionary realms;
00349   private PathTreeDictionary mappingtable;
00350   private Hashtable attributes;
00351   sun.misc.BASE64Decoder base64Dec = new sun.misc.BASE64Decoder();
00352   // for sessions
00353   int uniqer;
00354   HttpSessionContextImpl sessions;
00355   static int expiredIn;
00356   protected Hashtable arguments;
00357 
00359   public Serve(Hashtable arguments, PrintStream logStream) {
00360     this.arguments = arguments;
00361     this.logStream = logStream;
00362     registry = new PathTreeDictionary();
00363     realms = new PathTreeDictionary();
00364     attributes = new Hashtable();
00365     sessions = new HttpSessionContextImpl();
00366     setAccessLogged();
00367     expiredIn = arguments.get(ARG_SESSION_TIMEOUT) != null ? ((Integer) arguments.get(ARG_SESSION_TIMEOUT)).intValue() : DEF_SESSION_TIMEOUT;
00368     port = arguments.get(ARG_PORT) != null ? ((Integer) arguments.get(ARG_PORT)).intValue() : DEF_PORT;
00369   }
00370 
00371   public Serve() {
00372     this(new Hashtable(), System.err);
00373   }
00374 
00375   void setAccessLogged() {
00376     String logflags = (String) arguments.get(ARG_LOG_OPTIONS);
00377     if (logflags != null) {
00378       useAccLog = true;
00379       showUserAgent = logflags.indexOf('A') >= 0;
00380       showReferer = logflags.indexOf('R') >= 0;
00381     }
00382   }
00383 
00384   boolean isAccessLogged() {
00385     return useAccLog;
00386   }
00387   boolean isShowReferer() {
00388     return showReferer;
00389   }
00390   boolean isShowUserAgent() {
00391     return showUserAgent;
00392   }
00393 
00395   // pattern, which can contain wildcards, and the class name of the Servlet
00396   // to launch when a matching URL comes in.  Patterns are checked for
00397   // matches in the order they were added, and only the first match is run.
00398   public void addServlet(String urlPat, String className) {
00399     addServlet(urlPat, className, (Hashtable) null);
00400   }
00401 
00402   public void addServlet(String urlPat, String className, Hashtable initParams) {
00403     // Check if we're allowed to make one of these.
00404     SecurityManager security = System.getSecurityManager();
00405     if (security != null) {
00406       int i = className.lastIndexOf('.');
00407       if (i != -1) {
00408         security.checkPackageAccess(className.substring(0, i));
00409         security.checkPackageDefinition(className.substring(0, i));
00410       }
00411     }
00412 
00413     // Make a new one.
00414     try {
00415       addServlet(urlPat, (Servlet) Class.forName(className).newInstance(), initParams);
00416       return;
00417     } catch (ClassNotFoundException e) {
00418       log("Class not found: " + className);
00419     } catch (ClassCastException e) {
00420       log("Class cast problem: " + e.getMessage());
00421     } catch (InstantiationException e) {
00422       log("Instantiation problem: " + e.getMessage());
00423     } catch (IllegalAccessException e) {
00424       log("Illegal class access: " + e.getMessage());
00425     } catch (Exception e) {
00426       log("Unexpected problem creating servlet: " + e, e);
00427     }
00428   }
00429 
00431   // which can contain wildcards, and the Servlet to
00432   // launch when a matching URL comes in.  Patterns are checked for
00433   // matches in the order they were added, and only the first match is run.
00434   public void addServlet(String urlPat, Servlet servlet) {
00435     addServlet(urlPat, servlet, (Hashtable) null);
00436   }
00437 
00438   public void addServlet(String urlPat, Servlet servlet, Hashtable initParams) {
00439     try {
00440       servlet.init(new ServeConfig((ServletContext) this, initParams, urlPat));
00441       registry.put(urlPat, servlet);
00442     } catch (ServletException e) {
00443       log("Problem initializing servlet: " + e);
00444     }
00445   }
00446 
00448   // files or directory listings, and run CGI programs, much like a
00449   // standard HTTP server.
00450   // <P>
00451   // Because of the pattern checking order, this should be called
00452   // <B>after</B> you've added any custom Servlets.
00453   // <P>
00454   // The current set of default servlet mappings:
00455   // <UL>
00456   // <LI> If enabled, *.cgi goes to CgiServlet, and gets run as a CGI program.
00457   // <LI> * goes to FileServlet, and gets served up as a file or directory.
00458   // </UL>
00459   // @param cgi whether to run CGI programs
00460   // TODO: provide user specified CGI directory
00461   public void addDefaultServlets(String cgi) {
00462     if (cgi != null)
00463       addServlet("/" + cgi, new Acme.Serve.CgiServlet());
00464     addServlet("/", new Acme.Serve.FileServlet());
00465   }
00466 
00468   // @param cgi whether to run CGI programs
00469   // @param throttles filename to read FileServlet throttle settings from
00470   public void addDefaultServlets(String cgi, String throttles) throws IOException {
00471     if (cgi != null)
00472       addServlet("/" + cgi, new Acme.Serve.CgiServlet());
00473     addServlet("/", new Acme.Serve.FileServlet(throttles));
00474   }
00475 
00476   // Run the server.  Returns only on errors.
00477   boolean running = true;
00478   public void serve() {
00479     final ServerSocket serverSocket;
00480     try {
00481       serverSocket = createServerSocket();
00482     } catch (IOException e) {
00483       log("Server socket: " + e);
00484       return;
00485     }
00486     hostName = serverSocket.getInetAddress().getHostName();
00487  /*   new Thread(new Runnable() {
00488       public void run() {
00489         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
00490         String line;
00491         while (true) {
00492           try {
00493             System.out.print("Press \"q\" <ENTER>, for gracefully stopping the server ");
00494             line = in.readLine();
00495             if (line != null && line.length() > 0 && line.charAt(0) == 'q') {
00496               running = false;
00497               serverSocket.close();
00498               break;
00499             }
00500           } catch (IOException e) {
00501           }
00502         }
00503       }
00504     }, "Stop Monitor").start(); */
00505     if (expiredIn > 0) {
00506       Thread t = new Thread(new Runnable() {
00507         public void run() {
00508           while (running) {
00509             try {
00510               Thread.sleep(expiredIn * 60 * 1000);
00511             } catch (InterruptedException ie) {
00512             }
00513             Enumeration e = sessions.keys();
00514             while (e.hasMoreElements()) {
00515               Object sid = e.nextElement();
00516               if (sid != null) {
00517                 AcmeSession as = (AcmeSession) sessions.get(sid);
00518                 if (as != null
00519                   && (as.getMaxInactiveInterval() * 1000 < System.currentTimeMillis() - as.getLastAccessedTime()
00520                     || !as.isValid())) { //log("sesion is timeouted, last accessed " + new Date(as.getLastAccessedTime()));
00521                   // hashtable is synchronized impl
00522                   as = (AcmeSession) sessions.remove(sid);
00523                   if (as != null && as.isValid())
00524                     try {
00525                       as.invalidate();
00526                     } catch (IllegalStateException ise) {
00527 
00528                     }
00529                 }
00530               }
00531             }
00532           }
00533         }
00534       }, "Session cleaner");
00535       t.setPriority(Thread.MIN_PRIORITY);
00536       t.start();
00537     } else
00538       expiredIn = -expiredIn;
00539     System.out.println("WebServer :" + port + " is ready.");
00540     try {
00541       while (running) {
00542         Socket socket = serverSocket.accept();
00543         new ServeConnection(socket, this);
00544       }
00545     } catch (IOException e) {
00546       log("Accept: " + e);
00547     } finally {
00548       try {
00549         serverSocket.close();
00550       } catch (IOException e) {
00551       }
00552       destroyAllServlets();
00553     }
00554   }
00555 
00556   public static interface SocketFactory {
00557     public ServerSocket createSocket(Hashtable arguments) throws IOException, IllegalArgumentException;
00558   }
00559 
00560   protected ServerSocket createServerSocket() throws IOException {
00561     String socketFactoryClass = (String) arguments.get(ARG_SOCKET_FACTORY);
00562     if (socketFactoryClass != null)
00563       try {
00564         return ((SocketFactory) Class.forName(socketFactoryClass).newInstance()).createSocket(arguments);
00565       } catch (Exception e) {
00566         System.err.println("Couldn't create custom socket factory " + socketFactoryClass + " or call creation method. Standard socket will be created. " + e);
00567       }
00568     return new ServerSocket(port, 1000);
00569   }
00570 
00571   // Methods from ServletContext.
00572 
00574   // @param name the servlet name
00575   // @return null if the servlet does not exist
00576   public Servlet getServlet(String name) {
00577     try {
00578       return (Servlet) ((Object[]) registry.get(name))[0];
00579     } catch (NullPointerException npe) {
00580       return null;
00581     }
00582   }
00583 
00585   // are accesible will be returned.  This enumeration always includes the
00586   // servlet itself.
00587   public Enumeration getServlets() {
00588     return registry.elements();
00589   }
00590 
00592   // servlets that are accesible will be returned.  This enumeration always
00593   // includes the servlet itself.
00594   public Enumeration getServletNames() {
00595     return registry.keys();
00596   }
00597 
00599   public void destroyAllServlets() {
00600     Enumeration en = registry.elements();
00601     while (en.hasMoreElements()) {
00602       Servlet servlet = (Servlet) en.nextElement();
00603       servlet.destroy();
00604     }
00605     registry = new PathTreeDictionary();
00606     // invalidate all sessions?
00607   }
00608 
00609   public void setMappingTable(PathTreeDictionary mappingtable) {
00610     this.mappingtable = mappingtable;
00611   }
00612 
00613   public void setRealms(PathTreeDictionary realms) {
00614     this.realms = realms;
00615   }
00616 
00617   Object getSession(String id) {
00618     return sessions.get(id);
00619   }
00620 
00621   HttpSession createSession() {
00622     HttpSession result = new AcmeSession(generateSessionId(), expiredIn * 60, this, sessions);
00623     sessions.put(result.getId(), result);
00624     return result;
00625   }
00626 
00627   void removeSession(String id) {
00628     sessions.remove(id);
00629   }
00630 
00632   // @param message the message to log
00633   public void log(String message) {
00634     Date date = new Date(System.currentTimeMillis());
00635     logStream.println("[" + date.toString() + "] " + message);
00636   }
00637 
00638   public void log(String message, Throwable throwable) {
00639     StringWriter sw = new StringWriter();
00640     throwable.printStackTrace(new PrintWriter(sw));
00641     log(message + '\n' + sw);
00642   }
00643 
00645   // @param exception where to get the stack trace
00646   // @param message the message to log
00647   public void log(Exception exception, String message) {
00648     StringWriter sw = new StringWriter();
00649     exception.printStackTrace(new PrintWriter(sw));
00650     log("" + sw + '\n' + message);
00651   }
00652 
00654   // corresponding real path.  It returns null if the translation
00655   // cannot be performed.
00656   // @param path the path to be translated
00657   public String getRealPath(String path) {
00658     //System.err.print("["+path+"]->[");
00659     try { // this code only for debug purpose
00660       // check if it absolute URL
00661       URL url = new URL(path);
00662       path = url.getFile();
00663       new Exception("URL " + path + " specified in getRealPath").printStackTrace();
00664       path = null;
00665     } catch (MalformedURLException mfue) {
00666     }
00667     if (mappingtable != null) {
00668       // try find first sub-path
00669       Object[] os = mappingtable.get(path);
00670       if (os[0] == null)
00671         return null;
00672       int slpos = ((Integer) os[1]).intValue();
00673       if (path.length() > slpos && slpos > 0) {
00674         path = path.substring(slpos + 1);
00675       } else if (path.length() > 0) {
00676         char s = path.charAt(0);
00677         if (s == '/' || s == '\\')
00678           path = path.substring(1);
00679       }
00680       //System.err.println("Base:"+((String)os[0])+"\npath="+path+"\n pos="+slpos+']');
00681 
00682       return ((String) os[0]) + File.separatorChar + path;
00683     }
00684     return path;
00685   }
00686 
00688   // @param file file name whose MIME type is required
00689   public String getMimeType(String file) {
00690     file = file.toUpperCase();
00691 
00692     if (file.endsWith(".HTML") || file.endsWith(".HTM"))
00693       return "text/html";
00694     if (file.endsWith(".TXT"))
00695       return "text/plain";
00696     if (file.endsWith(".XML"))
00697       return "text/xml";
00698     if (file.endsWith(".CSS"))
00699       return "text/css";
00700     if (file.endsWith(".SGML") || file.endsWith(".SGM"))
00701       return "text/x-sgml";
00702     // Image
00703     if (file.endsWith(".GIF"))
00704       return "image/gif";
00705     if (file.endsWith(".JPG") || file.endsWith(".JPEG") || file.endsWith(".JPE"))
00706       return "image/jpeg";
00707     if (file.endsWith(".PNG"))
00708       return "image/png";
00709     if (file.endsWith(".TIF") || file.endsWith(".TIFF"))
00710       return "image/tiff";
00711     if (file.endsWith(".RGB"))
00712       return "image/x-rgb";
00713     if (file.endsWith(".XPM"))
00714       return "image/x-xpixmap";
00715     if (file.endsWith(".XBM"))
00716       return "image/x-xbitmap";
00717     if (file.endsWith(".SVG"))
00718       return "image/svg-xml ";
00719     if (file.endsWith(".SVGZ"))
00720       return "image/svg-xml ";
00721     // Audio
00722     if (file.endsWith(".AU") || file.endsWith(".SND"))
00723       return "audio/basic";
00724     if (file.endsWith(".MID") || file.endsWith(".MIDI") || file.endsWith(".RMI") || file.endsWith(".KAR"))
00725       return "audio/mid";
00726     if (file.endsWith(".MPGA") || file.endsWith(".MP2") || file.endsWith(".MP3"))
00727       return "audio/mpeg";
00728     if (file.endsWith(".WAV"))
00729       return "audio/wav";
00730     if (file.endsWith(".AIFF") || file.endsWith(".AIFC"))
00731       return "audio/aiff";
00732     if (file.endsWith(".AIF"))
00733       return "audio/x-aiff";
00734     if (file.endsWith(".RA"))
00735       return "audio/x-realaudio";
00736     if (file.endsWith(".RPM"))
00737       return "audio/x-pn-realaudio-plugin";
00738     if (file.endsWith(".RAM"))
00739       return "audio/x-pn-realaudio";
00740     if (file.endsWith(".SD2"))
00741       return "audio/x-sd2";
00742     // Application
00743     if (file.endsWith(".BIN") || file.endsWith(".DMS") || file.endsWith(".LHA") || file.endsWith(".LZH") || file.endsWith(".EXE") || file.endsWith(".CLASS"))
00744       return "application/octet-stream";
00745     if (file.endsWith(".HQX"))
00746       return "application/mac-binhex40";
00747     if (file.endsWith(".PS") || file.endsWith(".AI") || file.endsWith(".EPS"))
00748       return "application/postscript";
00749     if (file.endsWith(".PDF"))
00750       return "application/pdf";
00751     if (file.endsWith(".RTF"))
00752       return "application/rtf";
00753     if (file.endsWith(".DOC"))
00754       return "application/msword";
00755     if (file.endsWith(".PPT"))
00756       return "application/powerpoint";
00757     if (file.endsWith(".FIF"))
00758       return "application/fractals";
00759     if (file.endsWith(".P7C"))
00760       return "application/pkcs7-mime";
00761     // Application/x
00762     if (file.endsWith(".JS"))
00763       return "application/x-javascript";
00764     if (file.endsWith(".Z"))
00765       return "application/x-compress";
00766     if (file.endsWith(".GZ"))
00767       return "application/x-gzip";
00768     if (file.endsWith(".TAR"))
00769       return "application/x-tar";
00770     if (file.endsWith(".TGZ"))
00771       return "application/x-compressed";
00772     if (file.endsWith(".ZIP"))
00773       return "application/x-zip-compressed";
00774     if (file.endsWith(".DIR") || file.endsWith(".DCR") || file.endsWith(".DXR"))
00775       return "application/x-director";
00776     if (file.endsWith(".DVI"))
00777       return "application/x-dvi";
00778     if (file.endsWith(".TEX"))
00779       return "application/x-tex";
00780     if (file.endsWith(".LATEX"))
00781       return "application/x-latex";
00782     if (file.endsWith(".TCL"))
00783       return "application/x-tcl";
00784     if (file.endsWith(".CER") || file.endsWith(".CRT") || file.endsWith(".DER"))
00785       return "application/x-x509-ca-cert";
00786     // Video
00787     if (file.endsWith(".MPG") || file.endsWith(".MPE") || file.endsWith(".MPEG"))
00788       return "video/mpeg";
00789     if (file.endsWith(".QT") || file.endsWith(".MOV"))
00790       return "video/quicktime";
00791     if (file.endsWith(".AVI"))
00792       return "video/x-msvideo";
00793     if (file.endsWith(".MOVIE"))
00794       return "video/x-sgi-movie";
00795     // Chemical
00796     if (file.endsWith(".PDB") || file.endsWith(".XYZ"))
00797       return "chemical/x-pdb";
00798     // X-
00799     if (file.endsWith(".ICE"))
00800       return "x-conference/x-cooltalk";
00801     if (file.endsWith(".WRL") || file.endsWith(".VRML"))
00802       return "x-world/x-vrml";
00803     if (file.endsWith(".WML"))
00804       return "text/vnd.wap.wml";
00805     if (file.endsWith(".WMLC"))
00806       return "application/vnd.wap.wmlc";
00807     if (file.endsWith(".WMLS"))
00808       return "text/vnd.wap.wmlscript";
00809     if (file.endsWith(".WMLSC"))
00810       return "application/vnd.wap.wmlscriptc";
00811     if (file.endsWith(".WBMP"))
00812       return "image/vnd.wap.wbmp";
00813 
00814     return null;
00815   }
00816 
00818   // is running.
00819   // Same as the CGI variable SERVER_SOFTWARE.
00820   public String getServerInfo() {
00821     return Serve.Identification.serverName + " " + Serve.Identification.serverVersion + " (" + Serve.Identification.serverUrl + ")";
00822   }
00823 
00825   // null if the attribute does not exist.  This method allows access to
00826   // additional information about the service, not already provided by
00827   // the other methods in this interface.
00828   public Object getAttribute(String name) {
00829     // This server does not support attributes.
00830     return attributes.get(name);
00831   }
00832 
00834   public void removeAttribute(String name) {
00835     attributes.remove(name);
00836   }
00837 
00838   public void setAttribute(String name, Object object) {
00839     attributes.put(name, object);
00840   }
00841 
00842   public Enumeration getAttributeNames() {
00843     return attributes.keys();
00844   }
00845 
00846   public ServletContext getContext(String uripath) {
00847     return this; // only root context supported
00848   }
00849 
00850   public int getMajorVersion() {
00851     return 2; // support 2.x
00852   }
00853 
00854   public int getMinorVersion() {
00855     return 3; // support 2.3
00856   }
00857 
00858   // 2.3
00859 
00882   public java.util.Set getResourcePaths(java.lang.String path) {
00883     // TODO: implement
00884     return null;
00885   }
00886 
00893   public java.lang.String getServletContextName() {
00894     return null; //"ROOT";
00895   }
00896 
00897   // only root relative in this implementation
00898   public URL getResource(String path) throws MalformedURLException {
00899     return new URL("http", hostName, port, path);
00900   }
00901 
00902   public InputStream getResourceAsStream(String path) {
00903     return null; // we don't provide resources in this way
00904   }
00905 
00906   public RequestDispatcher getRequestDispatcher(String urlpath) {
00907     return this; // we don't provide resource dispatching in this way
00908   }
00909 
00910   // no way to specify parameters for context
00911   public String getInitParameter(String param) {
00912     return null;
00913   }
00914 
00915   public Enumeration getInitParameterNames() {
00916     return null;
00917   }
00918 
00919   public RequestDispatcher getNamedDispatcher(String name) {
00920     return this;
00921   }
00922 
00923   synchronized String generateSessionId() {
00924     return "-" + System.currentTimeMillis() + '-' + (uniqer++) + '-' + Math.round(Math.random() * 1000);
00925   }
00926 
00927   public void forward(ServletRequest _request, ServletResponse _response) throws ServletException, java.io.IOException {
00928   }
00929 
00930   public void include(ServletRequest _request, ServletResponse _response) throws ServletException, java.io.IOException {
00931   }
00932 
00933   final static class Identification {
00934     public static final String serverName = "Rogatkin's JWS based on Acme.Serve";
00935     public static final String serverVersion = "$Revision: 1.2 $";
00936     public static final String serverUrl = "http://tjws.sourceforge.net";
00937 
00939     public static void writeAddress(OutputStream o) throws IOException {
00940       PrintStream p = new PrintStream(o);
00941       p.println("<ADDRESS><A HREF=\"" + serverUrl + "\">" + serverName + " " + serverVersion + "</A></ADDRESS>");
00942     }
00943 
00944     public static void writeAddress(StringBuffer sb) throws IOException {
00945       sb.append("<ADDRESS><A HREF=\"" + serverUrl + "\">" + serverName + " " + serverVersion + "</A></ADDRESS>");
00946     }
00947   }
00948 }
00949 
00950 class ServeConfig implements ServletConfig {
00951 
00952   private ServletContext context;
00953   private Hashtable init_params;
00954   private String servletName;
00955 
00956   public ServeConfig(ServletContext context) {
00957     this(context, null, "undefined");
00958   }
00959 
00960   public ServeConfig(ServletContext context, Hashtable initParams, String servletName) {
00961     this.context = context;
00962     this.init_params = initParams;
00963     this.servletName = servletName;
00964   }
00965 
00966   // Methods from ServletConfig.
00967 
00969   public ServletContext getServletContext() {
00970     return context;
00971   }
00972 
00974   // @param name the parameter name
00975   public String getInitParameter(String name) {
00976     // This server supports servlet init params. :)
00977     if (init_params != null)
00978       return (String) init_params.get(name);
00979     return null;
00980   }
00981 
00983   // @param name the parameter name
00984   public Enumeration getInitParameterNames() {
00985     // This server does:) support servlet init params.
00986     if (init_params != null)
00987       return init_params.keys();
00988     return new Vector().elements();
00989   }
00990   // 2.2
00991   public String getServletName() {
00992     return servletName;
00993   }
00994 }
00995 
00997 
01000 class ServeConnection implements Runnable, HttpServletRequest, HttpServletResponse {
01001 
01002   private Socket socket;
01003   private Serve serve;
01004 
01005   private ServletInputStream in;
01006   private ServletOutputStream out;
01007 
01008   private Hashtable formParameters;
01009   private Hashtable attributes = new Hashtable();
01010 
01011   public final static String WWWFORMURLENCODE = "application/x-www-form-urlencoded";
01012   public final static String TRANSFERENCODING = "Transfer-Encoding";
01013   public final static String CHUNKED = "chunked";
01014   public final static String CONTENTLENGTH = "Content-Length";
01015   public final static String CONTENTTYPE = "Content-Type";
01016   public final static String SETCOOKIE = "Set-Cookie";
01017   public final static String COOKIE = "Cookie";
01018 
01019   public final static String SESSION_COOKIE_NAME = "JSESSIONID";
01020   // URL rewriting http://www.myserver.com/catalog/index.html;jsessionid=mysession1928
01021   // like: http://www.sun.com/2001-0227/sunblade/;$sessionid$AD5RQ0IAADJAZAMTA1LU5YQ
01022 
01023   private String reqMethod; // == null by default
01024   private String reqUriPath;
01025   private String reqProtocol;
01026   private String reqCharEncoding;
01027   private String remoteUser;
01028   private String authType;
01029   private boolean oneOne; // HTTP/1.1 or better
01030   private boolean reqMime;
01031   String reqQuery = null;
01032   private Vector reqHeaderNames = new Vector();
01033   private Vector reqHeaderValues = new Vector();
01034   private Locale locale; // = java.util.Locale.getDefault();
01035   private int uriLen;
01036   private static final Hashtable EMPTYHASHTABLE = new Hashtable();
01037 
01038   private Vector outCookies;
01039   private Vector inCookies;
01040 
01041   private String sessionCookieValue;
01042 
01044   public ServeConnection(Socket socket, Serve serve) {
01045     // Save arguments.
01046     this.socket = socket;
01047     this.serve = serve;
01048 
01049     // Start a separate thread to read and handle the request.
01050     Thread thread = new Thread(this, "Request handler");
01051     thread.start();
01052   }
01053 
01064   public String getLocalAddr() {
01065     return null; // TODO:
01066   }
01067 
01077   public String getLocalName() {
01078     return null; // TODO: get bind address
01079   }
01080 
01089   public int getLocalPort() {
01090     return serve.port;
01091   }
01092 
01101   public int getRemotePort() {
01102     return serve.port;
01103   }
01104 
01105   // Methods from Runnable.
01106   public void run() {
01107     try {
01108       // Get the streams.
01109       in = new ServeInputStream(socket.getInputStream());
01110       out = new ServeOutputStream(socket.getOutputStream(), this);
01111     } catch (IOException e) {
01112       problem("Getting streams: " + e.getMessage(), SC_BAD_REQUEST);
01113     }
01114 
01115     parseRequest();
01116 
01117     if (serve.isAccessLogged()) {
01118       serve.log(
01119         socket.getInetAddress().toString()
01120           + ' '
01121           + reqMethod
01122           + ' '
01123           + reqUriPath
01124           + ' '
01125           + resCode
01126           + (serve.isShowReferer() ? "| " + getHeader("Referer") : "")
01127           + (serve.isShowUserAgent() ? "| " + getHeader("User-Agent") : ""));
01128     }
01129 
01130     try {
01131       socket.close();
01132     } catch (IOException e) { /* ignore */
01133     }
01134   }
01135 
01136   private void parseRequest() {
01137     byte[] lineBytes = new byte[4096];
01138     int len;
01139     String line;
01140 
01141     try {
01142       // Read the first line of the request.
01143       len = in.readLine(lineBytes, 0, lineBytes.length);
01144       if (len == -1 || len == 0) {
01145         problem("Empty request", SC_BAD_REQUEST);
01146         return;
01147       }
01148       line = new String(lineBytes, 0, len);
01149       StringTokenizer ust = new StringTokenizer(line);
01150       reqProtocol = null;
01151       if (ust.hasMoreTokens()) {
01152         reqMethod = ust.nextToken();
01153         if (ust.hasMoreTokens()) {
01154           reqUriPath = Acme.Utils.urlDecoder(ust.nextToken());
01155           if (ust.hasMoreTokens()) {
01156             reqProtocol = ust.nextToken();
01157             oneOne = !reqProtocol.toUpperCase().equals("HTTP/1.0");
01158             reqMime = true;
01159             // Read the rest of the lines.
01160             String s;
01161             while ((s = ((ServeInputStream) in).readLine()) != null) {
01162               if (s.length() == 0)
01163                 break;
01164 
01165               int c = s.indexOf(':', 0);
01166               if (c > 0) {
01167                 String key = s.substring(0, c).trim();
01168                 String value = s.substring(c + 1, s.length()).trim();
01169                 reqHeaderNames.addElement(key.toLowerCase());
01170                 reqHeaderValues.addElement(value);
01171               } else
01172                 serve.log("header field without ':'");
01173             }
01174           } else {
01175             reqProtocol = "HTTP/0.9";
01176             oneOne = false;
01177             reqMime = false;
01178           }
01179         }
01180       }
01181       if (reqProtocol == null) {
01182         problem("Malformed request line", SC_BAD_REQUEST);
01183         return;
01184       }
01185       // Check Host: header in HTTP/1.1 requests.
01186       if (oneOne) {
01187         String host = getHeader("host");
01188         if (host == null) {
01189           problem("Host header missing on HTTP/1.1 request", SC_BAD_REQUEST);
01190           return;
01191         }
01192       }
01193 
01194       // Decode %-sequences.
01195       //reqUriPath = decode( reqUriPath );
01196       // Split off query string, if any.
01197       int qmark = reqUriPath.indexOf('?');
01198       if (qmark > -1) {
01199         reqQuery = reqUriPath.substring(qmark + 1);
01200         reqUriPath = reqUriPath.substring(0, qmark);
01201       }
01202       if (CHUNKED.equals(getHeader(TRANSFERENCODING))) {
01203         setHeader(CONTENTLENGTH, null);
01204         ((ServeInputStream) in).chunking(true);
01205       }
01206 
01207       Object[] os = serve.registry.get(reqUriPath);
01208       if (os != null) {
01209         uriLen = ((Integer) os[1]).intValue();
01210         runServlet((HttpServlet) os[0]);
01211       }
01212     } catch (IOException e) {
01213       problem("Reading request: " + e.getMessage(), SC_BAD_REQUEST);
01214     }
01215   }
01216 
01217   private void runServlet(HttpServlet servlete) {
01218     // Set default response fields.
01219     setStatus(SC_OK);
01220     setDateHeader("Date", System.currentTimeMillis());
01221     setHeader("Server", Serve.Identification.serverName + "/" + Serve.Identification.serverVersion);
01222     setHeader("MIME-Version", "1.0");
01223     try {
01224       parseCookies();
01225       authenificate();
01226       if (servlete instanceof SingleThreadModel)
01227         synchronized (servlete) {
01228           servlete.service((ServletRequest) this, (ServletResponse) this);
01229         } else
01230         servlete.service((ServletRequest) this, (ServletResponse) this);
01231     } catch (IOException e) {
01232       e.printStackTrace();
01233       problem("IO problem running servlet: " + e.toString(), SC_BAD_REQUEST);
01234     } catch (ServletException e) {
01235       problem("problem running servlet: " + e.toString(), SC_BAD_REQUEST);
01236     } catch (Exception e) {
01237       problem("unexpected problem running servlet: " + e.toString(), SC_INTERNAL_SERVER_ERROR);
01238       e.printStackTrace();
01239     }
01240   }
01241 
01242   private boolean authenificate() throws IOException {
01243     Object[] o = serve.realms.get(getPathInfo());
01244     BasicAuthRealm realm = null;
01245     if (o == null)
01246       realm = (BasicAuthRealm) o[0];
01247     String credentials = getHeader("Authorization");
01248 
01249     if (credentials != null) {
01250       credentials = credentials.substring(credentials.indexOf(' ') + 1);
01251       try {
01252         ByteArrayOutputStream baos = new ByteArrayOutputStream();
01253         serve.base64Dec.decodeBuffer(new StringBufferInputStream(credentials), baos);
01254         credentials = baos.toString();
01255       } catch (IOException e) {
01256         e.printStackTrace();
01257       }
01258       int i = credentials.indexOf(':');
01259       String user = credentials.substring(0, i);
01260       String password = credentials.substring(i + 1);
01261       remoteUser = user;
01262       authType = "Basic"; // support only basic authenification
01263       if (realm == null)
01264         return true;
01265       String realPassword = (String) realm.get(user);
01266       System.err.println("User " + user + " Password " + password + " real " + realPassword);
01267       if (realPassword != null && realPassword.equals(password))
01268         return true;
01269     }
01270     if (realm == null)
01271       return true;
01272 
01273     setStatus(SC_UNAUTHORIZED);
01274     setHeader("WWW-Authenticate", "basic realm=\"" + realm.name() + '"');
01275     writeHeaders();
01276     return false;
01277   }
01278 
01279   private void problem(String logMessage, int resCode) {
01280     serve.log(logMessage);
01281     try {
01282       sendError(resCode);
01283     } catch (IllegalStateException e) { /* ignore */
01284     } catch (IOException e) { /* ignore */
01285     }
01286   }
01287 
01288   private String decode(String str) {
01289     StringBuffer result = new StringBuffer();
01290     int l = str.length();
01291     for (int i = 0; i < l; ++i) {
01292       char c = str.charAt(i);
01293       if (c == '%' && i + 2 < l) {
01294         char c1 = str.charAt(i + 1);
01295         char c2 = str.charAt(i + 2);
01296         if (isHexit(c1) && isHexit(c2)) {
01297           result.append((char) (hexit(c1) * 16 + hexit(c2)));
01298           i += 2;
01299         } else
01300           result.append(c);
01301       } else if (c == '+')
01302         result.append(' ');
01303       else
01304         result.append(c);
01305     }
01306     return result.toString();
01307   }
01308 
01309   private boolean isHexit(char c) {
01310     String legalChars = "0123456789abcdefABCDEF";
01311     return (legalChars.indexOf(c) != -1);
01312   }
01313 
01314   private int hexit(char c) {
01315     if (c >= '0' && c <= '9')
01316       return c - '0';
01317     if (c >= 'a' && c <= 'f')
01318       return c - 'a' + 10;
01319     if (c >= 'A' && c <= 'F')
01320       return c - 'A' + 10;
01321     return 0; // shouldn't happen, we're guarded by isHexit()
01322   }
01323 
01324   void parseCookies() {
01325     if (inCookies == null)
01326       inCookies = new Vector();
01327     try {
01328       String cookie_name;
01329       String cookie_value;
01330       String cookie_path;
01331       String cookies = getHeader(COOKIE);
01332       if (cookies == null)
01333         return;
01334       //Enumeration e = getHeaders(COOKIE);
01335       //while(e.hasMoreElements())
01336       //        cookies += (String)e.nextElement();
01337       StringTokenizer st = new StringTokenizer(cookies, ";", true);
01338       // TODO: write a parser to avoid tokenizers
01339       while (st.hasMoreTokens()) {
01340         StringTokenizer st2 = new StringTokenizer(st.nextToken(), "=");
01341         if (st2.hasMoreTokens()) {
01342           cookie_name = st2.nextToken().trim();
01343           if (st2.hasMoreTokens()) {
01344             cookie_value = st2.nextToken(",").trim();
01345             if (cookie_value.length() > 0 && cookie_value.charAt(0) == '=')
01346               cookie_value = cookie_value.substring(1);
01347             cookie_path = "/";
01348             while (st2.hasMoreTokens()) {
01349               String cookie_atr = st2.nextToken();
01350               if ("$Version".equalsIgnoreCase(cookie_atr) || "$Path".equalsIgnoreCase(cookie_atr) || "$Domain".equalsIgnoreCase(cookie_atr))
01351                 continue;
01352               cookie_path = st2.nextToken();
01353             }
01354             Cookie cookie = new Cookie(cookie_name, cookie_value);
01355             //System.err.println("Cookie set:"+cookie_name+':'+cookie_value);
01356             cookie.setPath(cookie_path);
01357             inCookies.addElement(cookie);
01358             if (SESSION_COOKIE_NAME.equals(cookie_name) && sessionCookieValue == null) {
01359               sessionCookieValue = cookie_value;
01360               try {
01361                 ((AcmeSession) serve.getSession(sessionCookieValue)).userTouch();
01362               } catch (Throwable t) {
01363               }
01364             }
01365           }
01366         }
01367       }
01368     } catch (Throwable e) {
01369       System.err.println("prepareCookies(): " + e);
01370       e.printStackTrace();
01371     }
01372   }
01373 
01374   // Methods from ServletRequest.
01375 
01377   // Same as the CGI variable CONTENT_LENGTH.
01378   public int getContentLength() {
01379     return getIntHeader(CONTENTLENGTH, -1);
01380   }
01381 
01383   // not known.
01384   // Same as the CGI variable CONTENT_TYPE.
01385   public String getContentType() {
01386     return getHeader(CONTENTTYPE);
01387   }
01388 
01390   // the form <protocol>/<major version>.<minor version>.
01391   // Same as the CGI variable SERVER_PROTOCOL.
01392   public String getProtocol() {
01393     return reqProtocol;
01394   }
01395 
01397   // "http", "https", or "ftp".  Different schemes have different rules
01398   // for constructing URLs, as noted in RFC 1738.  The URL used to create
01399   // a request may be reconstructed using this scheme, the server name
01400   // and port, and additional information such as URIs.
01401   public String getScheme() {
01402     if (socket.getClass().getName().toUpperCase().indexOf("SSL") < 0)
01403       return "http";
01404     else
01405       return "https";
01406   }
01407 
01409   // the request URI.
01410   // Same as the CGI variable SERVER_NAME.
01411   public String getServerName() {
01412     String serverName;
01413     int serverPort = 80;
01414     serverName = getHeader("Host");
01415     if (serverName != null && serverName.length() > 0) {
01416       int colon = serverName.indexOf(':');
01417       if (colon >= 0) {
01418         if (colon < serverName.length())
01419           serverPort = Integer.parseInt(serverName.substring(colon + 1));
01420         serverName = serverName.substring(0, colon);
01421       }
01422     }
01423 
01424     if (serverName == null) {
01425       try {
01426         serverName = InetAddress.getLocalHost().getHostName();
01427       } catch (java.net.UnknownHostException ignore) {
01428         serverName = "127.0.0.0";
01429       }
01430     }
01431 
01432     int slash = serverName.indexOf("/");
01433     if (slash >= 0)
01434       serverName = serverName.substring(slash + 1);
01435     return serverName;
01436   }
01437 
01439   // the <port> part of the request URI.
01440   // Same as the CGI variable SERVER_PORT.
01441   public int getServerPort() {
01442     return socket.getLocalPort();
01443   }
01444 
01446   // Same as the CGI variable REMOTE_ADDR.
01447   public String getRemoteAddr() {
01448     return socket.getInetAddress().toString();
01449   }
01450 
01452   // request.
01453   // Same as the CGI variable REMOTE_HOST.
01454   public String getRemoteHost() {
01455     String result = socket.getInetAddress().getHostName();
01456     return result != null ? result : getRemoteAddr();
01457   }
01458 
01460   // corresponding real path, or null if the translation can not be
01461   // performed for any reason.  For example, an HTTP servlet would
01462   // resolve the path using the virtual docroot, if virtual hosting is
01463   // enabled, and with the default docroot otherwise.  Calling this
01464   // method with the string "/" as an argument returns the document root.
01465   public String getRealPath(String path) {
01466     return serve.getRealPath(path);
01467   }
01468 
01470   // @exception IllegalStateException if getReader has already been called
01471   // @exception IOException on other I/O-related errors
01472   public ServletInputStream getInputStream() throws IOException {
01473     synchronized (in) {
01474       if (((ServeInputStream) in).isReturnedAsReader())
01475         throw new IllegalStateException("Already returned as a reader.");
01476       ((ServeInputStream) in).setReturnedAsReader(true);
01477     }
01478     return in;
01479   }
01480 
01482   // @exception UnsupportedEncodingException if the character set encoding isn't supported
01483   // @exception IllegalStateException if getInputStream has already been called
01484   // @exception IOException on other I/O-related errors
01485   public BufferedReader getReader() {
01486     synchronized (in) {
01487       if (((ServeInputStream) in).isReturnedAsStream())
01488         throw new IllegalStateException("Already returned as a stream.");
01489       ((ServeInputStream) in).setReturnedAsStream(true);
01490     }
01491     if (reqCharEncoding != null)
01492       try {
01493         return new BufferedReader(new InputStreamReader(in, reqCharEncoding));
01494       } catch (UnsupportedEncodingException uee) {
01495       }
01496     return new BufferedReader(new InputStreamReader(in));
01497   }
01498 
01499   private Hashtable getParametersFromRequest() {
01500     Hashtable result = null;
01501     //System.out.println("Req:"+reqMethod+" con:"+getContentType()+" eq "+WWWFORMURLENCODE.equals(getContentType()));
01502     if ("GET".equals(reqMethod)) {
01503       if (reqQuery != null)
01504         try {
01505           result = HttpUtils.parseQueryString(reqQuery);
01506         } catch (IllegalArgumentException ex) {
01507         }
01508     } else if ("POST".equals(reqMethod))
01509       if (WWWFORMURLENCODE.equals(getContentType()))
01510         try {
01511           result = HttpUtils.parsePostData(getContentLength(), getInputStream());
01512           if (reqQuery != null && reqQuery.length() > 0) {
01513             Acme.Utils.putAll(result, HttpUtils.parseQueryString(reqQuery));
01514           }
01515         } catch (Exception ex) {
01516           serve.log("Exception " + ex + " at parsing post data of length " + getContentLength());
01517         } else
01518         try {
01519           if (reqQuery != null)
01520             result = HttpUtils.parseQueryString(reqQuery);
01521         } catch (Exception ex) {
01522         }
01523     return result != null ? result : EMPTYHASHTABLE;
01524   }
01525 
01527   public Enumeration getParameterNames() {
01528     if (formParameters == null)
01529       formParameters = getParametersFromRequest();
01530     return formParameters.keys();
01531   }
01532 
01534   // if not found.
01535   // @param name the parameter name
01536   public String getParameter(String name) {
01537     String[] params = getParameterValues(name);
01538     if (params == null || params.length == 0)
01539       return null;
01540 
01541     return params[0];
01542   }
01543 
01545   // array of strings, or null if the named parameter does not exist.
01546   public String[] getParameterValues(String name) {
01547     if (formParameters == null)
01548       getParameterNames();
01549 
01550     return (String[]) formParameters.get(name);
01551   }
01552 
01554   // the attribute does not exist.  This method allows access to request
01555   // information not already provided by the other methods in this interface.
01556   public Object getAttribute(String name) {
01557     return attributes.get(name);
01558   }
01559 
01560   // Methods from HttpServletRequest.
01561 
01563   public Cookie[] getCookies() {
01564     Cookie[] cookieArray = new Cookie[inCookies.size()];
01565     inCookies.copyInto(cookieArray);
01566     return cookieArray;
01567   }
01568 
01570   // "HEAD", "POST", or an extension method.
01571   // Same as the CGI variable REQUEST_METHOD.
01572   public String getMethod() {
01573     return reqMethod;
01574   }
01575 
01576   /***
01577     Returns the part of this request's URL from the protocol name up to
01578     the query string in the first line of the HTTP request.
01579     To reconstruct an URL with a scheme and host,
01580     use HttpUtils.getRequestURL(javax.servlet.http.HttpServletRequest).
01581   */
01583   public String getRequestURI() {
01584     return reqUriPath;
01585   }
01586 
01598   public java.lang.StringBuffer getRequestURL() {
01599     return new StringBuffer().append(getScheme()).append("://").append(serve.hostName).append(serve.port == 80 ? "" : String.valueOf(serve.port)).append(
01600       getRequestURI());
01601   }
01602 
01604   // invoked.
01605   // Analogous to the CGI variable SCRIPT_NAME.
01606   public String getServletPath() {
01607     // In this server, the entire path is regexp-matched against the
01608     // servlet pattern, so there's no good way to distinguish which
01609     // part refers to the servlet.
01610     return uriLen > 0 ? reqUriPath.substring(0, uriLen) : "";
01611   }
01612 
01614   // immediately preceding the query string.  Returns null if not specified.
01615   // Same as the CGI variable PATH_INFO.
01616   public String getPathInfo() {
01617     // In this server, the entire path is regexp-matched against the
01618     // servlet pattern, so there's no good way to distinguish which
01619     // part refers to the servlet.
01620     return uriLen >= reqUriPath.length() ? null : reqUriPath.substring(uriLen);
01621   }
01622 
01624   // null if no extra path information was specified.
01625   // Same as the CGI variable PATH_TRANSLATED.
01626   public String getPathTranslated() {
01627     // In this server, the entire path is regexp-matched against the
01628     // servlet pattern, so there's no good way to distinguish which
01629     // part refers to the servlet.
01630     return getRealPath(getPathInfo());
01631   }
01632 
01634   // Same as the CGI variable QUERY_STRING.
01635   public String getQueryString() {
01636     return reqQuery;
01637   }
01638 
01640   // Same as the CGI variable REMOTE_USER.
01641   public String getRemoteUser() {
01642     return remoteUser;
01643   }
01644 
01646   // Same as the CGI variable AUTH_TYPE.
01647   public String getAuthType() {
01648     return authType;
01649   }
01650 
01652   // Same as the information passed in the CGI variabled HTTP_*.
01653   // @param name the header field name
01654   public String getHeader(String name) {
01655     int i = reqHeaderNames.indexOf(name.toLowerCase());
01656     if (i == -1)
01657       return null;
01658     return (String) reqHeaderValues.elementAt(i);
01659   }
01660 
01661   public int getIntHeader(String name) {
01662     return getIntHeader(name, 0);
01663   }
01664 
01666   // @param name the header field name
01667   // @param def the integer value to return if header not found or invalid
01668   public int getIntHeader(String name, int def) {
01669     String val = getHeader(name);
01670     if (val == null)
01671       return def;
01672     try {
01673       return Integer.parseInt(val);
01674     } catch (Exception e) {
01675       return def;
01676     }
01677   }
01678 
01680   // @param name the header field name
01681   // @param def the long value to return if header not found or invalid
01682   public long getLongHeader(String name, long def) {
01683     String val = getHeader(name);
01684     if (val == null)
01685       return def;
01686     try {
01687       return Long.parseLong(val);
01688     } catch (Exception e) {
01689       return def;
01690     }
01691   }
01692 
01693   public long getDateHeader(String name) {
01694     String val = getHeader(name);
01695     if (val == null)
01696       return 0;
01697     try {
01698       return headerdateformat.parse(val).getTime();
01699     } catch (Exception e) {
01700       throw new IllegalArgumentException("Value " + val + " can't be converted to Date using " + headerdateformat.toPattern());
01701     }
01702 
01703   }
01705   // @param name the header field name
01706   // @param def the date value to return if header not found or invalid
01707   public long getDateHeader(String name, long def) {
01708     String val = getHeader(name);
01709     if (val == null)
01710       return def;
01711     try {
01712       return DateFormat.getDateInstance().parse(val).getTime();
01713     } catch (Exception e) {
01714       return def;
01715     }
01716   }
01717 
01719   public Enumeration getHeaderNames() {
01720     return reqHeaderNames.elements();
01721   }
01722 
01724   // create is false or, if necessary, creates a new session for the
01725   // request, if create is true.
01726   // <P>
01727   // Note: to ensure the session is properly maintained, the servlet
01728   // developer must call this method (at least once) before any output
01729   // is written to the response.
01730   // <P>
01731   // Additionally, application-writers need to be aware that newly
01732   // created sessions (that is, sessions for which HttpSession.isNew
01733   // returns true) do not have any application-specific state.
01734   public HttpSession getSession(boolean create) {
01735     // look for session
01736     // get session cookie
01737     HttpSession result = getSession();
01738     if (result == null && create) {
01739       result = serve.createSession();
01740     }
01741     if (result != null)
01742       sessionCookieValue = result.getId();
01743     return result;
01744   }
01745 
01746   // JSDK 2.1
01747   public HttpSession getSession() {
01748     AcmeSession result = null;
01749     if (sessionCookieValue != null) {
01750       result = (AcmeSession) serve.getSession(sessionCookieValue);
01751       if (result != null && !result.isValid()) {
01752         serve.removeSession(sessionCookieValue);
01753         result = null;
01754       }
01755     }
01756     return result;
01757   }
01758 
01759   public boolean isRequestedSessionIdFromURL() {
01760     return false;
01761   }
01762 
01763   // from ServletRequest
01764   public Enumeration getAttributeNames() {
01765     return attributes.keys();
01766   }
01767 
01768   public void setAttribute(String key, Object o) {
01769     attributes.put(key, o);
01770   }
01771 
01773   // from the actual session id.  For example, if the request specified
01774   // an id for an invalid session, then this will get a new session with
01775   // a new id.
01776   public String getRequestedSessionId() {
01777     return sessionCookieValue;
01778   }
01779 
01781   // valid in the current session context.  If it is not valid, the
01782   // requested session will never be returned from the getSession
01783   // method.
01784   public boolean isRequestedSessionIdValid() {
01785     if (sessionCookieValue != null) {
01786       AcmeSession session = (AcmeSession) serve.getSession(sessionCookieValue);
01787       if (session != null && session.isValid()) {
01788         return true;
01789       }
01790     }
01791     return false;
01792   }
01793 
01797   public boolean isRequestedSessionIdFromCookie() {
01798     return true;
01799   }
01800 
01802   // part of the URL.  (The requested session may not be the one returned
01803   // by the getSession method.)
01804   public boolean isRequestedSessionIdFromUrl() {
01805     return false;
01806   }
01807 
01808   // Methods from ServletResponse.
01809 
01811   // @param length the content length
01812   public void setContentLength(int length) {
01813     setIntHeader(CONTENTLENGTH, length);
01814   }
01815 
01817   // @param type the content type
01818   public void setContentType(String type) {
01819     setHeader(CONTENTTYPE, type != null ? type : "Unknown");
01820   }
01821 
01823   public ServletOutputStream getOutputStream() {
01824     synchronized (out) {
01825       if (((ServeOutputStream) out).isReturnedAsWriter())
01826         throw new IllegalStateException("Already returned as a writer");
01827       ((ServeOutputStream) out).setReturnedAsStream(true);
01828     }
01829     return out;
01830   }
01831 
01833   // the response will be modified, if necessary, to reflect the character
01834   // encoding used, through the charset=... property.  This means that the
01835   // content type must be set before calling this method.
01836   // @exception UnsupportedEncodingException if no such encoding can be provided
01837   // @exception IllegalStateException if getOutputStream has been called
01838   // @exception IOException on other I/O errors
01839   public PrintWriter getWriter() throws IOException {
01840     synchronized (out) {
01841       if (((ServeOutputStream) out).isReturnedAsStream())
01842         throw new IllegalStateException("Already was returned as servlet output stream");
01843       ((ServeOutputStream) out).setReturnedAsWriter(true);
01844     }
01845     String encoding = getCharacterEncoding();
01846     if (encoding != null)
01847       return new PrintWriter(new OutputStreamWriter(out, encoding));
01848     else
01849       return new PrintWriter(out);
01850   }
01851 
01853   // character encoding is either the one specified in the assigned
01854   // content type, or one which the client understands.  If no content
01855   // type has yet been assigned, it is implicitly set to text/plain.
01856   public String getCharacterEncoding() {
01857     String ct = (String) resHeaderNames.get(CONTENTTYPE);
01858     if (ct != null) {
01859       int scp = ct.indexOf(';');
01860       if (scp > 0) {
01861         scp = ct.toLowerCase().indexOf("charset=", scp);
01862         if (scp >= 0) {
01863           ct = ct.substring(scp + 8);
01864           scp = ct.indexOf(' ');
01865           if (scp > 0)
01866             ct = ct.substring(0, scp);
01867           scp = ct.indexOf(';');
01868           if (scp > 0)
01869             ct = ct.substring(0, scp);
01870           return ct;
01871         }
01872       }
01873     }
01874     return null;
01875   }
01876 
01877   // 2.2
01878   // do not use buffer
01879   public void flushBuffer() {
01880   }
01881 
01888   public void resetBuffer() {
01889     throw new IllegalStateException("The method not implemented");
01890   }
01891 
01892   public int getBufferSize() {
01893     return 0;
01894   }
01895 
01896   public void setBufferSize(int size) {
01897   }
01898 
01907   // a caller should think about syncronization 
01908   public boolean isCommitted() {
01909     return headersWritten;
01910   }
01911 
01918   public void reset() throws IllegalStateException {
01919     if (!isCommitted()) {
01920       outCookies.removeAllElements();
01921       resHeaderNames.clear();
01922     } else
01923       throw new IllegalStateException("Header have already been committed.");
01924   }
01925 
01934   public void setLocale(java.util.Locale locale) {
01935     this.locale = locale;
01936   }
01937 
01938   public java.util.Locale getLocale() {
01939     return locale;
01940   }
01941 
01942   // should decide about supported locales and charsets, no a good decision yet
01943   public Enumeration getLocales() {
01944     // TODO: get locales from Accept-Language (RFC 2616)
01945     //Locale.getAvailableLocales() 
01946     return null;
01947   }
01948 
01956   public void setCharacterEncoding(String _enc) {
01957     reqCharEncoding = _enc;
01958   }
01959 
01960   public void addDateHeader(String header, long date) {
01961     addHeader(header, expdatefmt.format(new Date(date)));
01962   }
01963 
01964   public void addHeader(String header, String value) {
01965     Object o = resHeaderNames.get(header);
01966     if (o == null)
01967       setHeader(header, value);
01968     else {
01969       if (o instanceof String[]) {
01970         String[] oldVal = (String[]) o;
01971         String[] newVal = new String[oldVal.length + 1];
01972         System.arraycopy(oldVal, 0, newVal, 0, oldVal.length);
01973         newVal[oldVal.length] = value;
01974         resHeaderNames.put(header, newVal);
01975       } else if (o instanceof String) {
01976         String[] newVal = new String[2];
01977         newVal[0] = (String) o;
01978         newVal[1] = value;
01979         resHeaderNames.put(header, newVal);
01980       } else
01981         throw new RuntimeException("Invalid content of header hash - " + o.getClass().getName());
01982     }
01983   }
01984 
01985   public void addIntHeader(String header, int value) {
01986     addHeader(header, Integer.toString(value));
01987   }
01988 
01989   public RequestDispatcher getRequestDispatcher(String urlpath) {
01990     // TODO: calculate dispacter relatively the current path
01991     return null; // we don't provide resource dispatching in this way
01992   }
01993 
01994   public boolean isSecure() {
01995     return false;
01996   }
01997 
01998   public void removeAttribute(String name) {
01999   }
02000 
02001   // only root context supported
02002   public String getContextPath() {
02003     return "";
02004   }
02005 
02006   public Enumeration getHeaders(String header) {
02007     Vector result = new Vector();
02008     int i = -1;
02009     while ((i = reqHeaderNames.indexOf(header.toLowerCase(), i + 1)) >= 0)
02010       result.addElement(reqHeaderValues.elementAt(i));
02011     return result.elements();
02012   }
02013 
02014   public java.security.Principal getUserPrincipal() {
02015     return null;
02016   }
02017 
02018   public boolean isUserInRole(String user) {
02019     return true;
02020   }
02021 
02031   public java.util.Map getParameterMap() {
02032     return (java.util.Map) formParameters;
02033   }
02034 
02035   // Methods from HttpServletResponse.
02036 
02038   // multiple times to set more than one cookie.
02039   public void addCookie(Cookie cookie) {
02040     if (outCookies == null)
02041       outCookies = new Vector();
02042 
02043     outCookies.addElement(cookie);
02044   }
02045 
02047   // specified name.
02048   public boolean containsHeader(String name) {
02049     return resHeaderNames.contains(name);
02050   }
02051 
02052   // JSDK 2.1 extension
02053   public String encodeURL(String url) {
02054     return url; // as is
02055   }
02056 
02057   public String encodeRedirectURL(String url) {
02058     return url; // as is
02059   }
02060 
02061   private int resCode = -1;
02062   private String resMessage = null;
02063   private Hashtable resHeaderNames = new Hashtable();
02064 
02065   protected static final SimpleDateFormat expdatefmt = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss 'GMT'");
02066   protected static final SimpleDateFormat headerdateformat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
02067   static {
02068     TimeZone tz = TimeZone.getTimeZone("GMT");
02069     tz.setID("GMT");
02070     expdatefmt.setTimeZone(tz);
02071   }
02072 
02074   // @param resCode the status code
02075   // @param resMessage the status message
02076   public void setStatus(int resCode, String resMessage) {
02077     //  if (this.resCode > 0 && this.resCode != SC_OK)
02078     //          throw new IllegalStateException("Result code "+this.resCode+" was already set.");
02079     this.resCode = resCode;
02080     this.resMessage = resMessage;
02081   }
02082 
02084   // @param resCode the status code
02085   public void setStatus(int resCode) {
02086     switch (resCode) {
02087       case SC_CONTINUE :
02088         setStatus(resCode, "Continue");
02089         break;
02090       case SC_SWITCHING_PROTOCOLS :
02091         setStatus(resCode, "Switching protocols");
02092         break;
02093       case SC_OK :
02094         setStatus(resCode, "Ok");
02095         break;
02096       case SC_CREATED :
02097         setStatus(resCode, "Created");
02098         break;
02099       case SC_ACCEPTED :
02100         setStatus(resCode, "Accepted");
02101         break;
02102       case SC_NON_AUTHORITATIVE_INFORMATION :
02103         setStatus(resCode, "Non-authoritative");
02104         break;
02105       case SC_NO_CONTENT :
02106         setStatus(resCode, "No content");
02107         break;
02108       case SC_RESET_CONTENT :
02109         setStatus(resCode, "Reset content");
02110         break;
02111       case SC_PARTIAL_CONTENT :
02112         setStatus(resCode, "Partial content");
02113         break;
02114       case SC_MULTIPLE_CHOICES :
02115         setStatus(resCode, "Multiple choices");
02116         break;
02117       case SC_MOVED_PERMANENTLY :
02118         setStatus(resCode, "Moved permanentently");
02119         break;
02120       case SC_MOVED_TEMPORARILY :
02121         setStatus(resCode, "Moved temporarily");
02122         break;
02123       case SC_SEE_OTHER :
02124         setStatus(resCode, "See other");
02125         break;
02126       case SC_NOT_MODIFIED :
02127         setStatus(resCode, "Not modified");
02128         break;
02129       case SC_USE_PROXY :
02130         setStatus(resCode, "Use proxy");
02131         break;
02132       case SC_BAD_REQUEST :
02133         setStatus(resCode, "Bad request");
02134         break;
02135       case SC_UNAUTHORIZED :
02136         setStatus(resCode, "Unauthorized");
02137         break;
02138       case SC_PAYMENT_REQUIRED :
02139         setStatus(resCode, "Payment required");
02140         break;
02141       case SC_FORBIDDEN :
02142         setStatus(resCode, "Forbidden");
02143         break;
02144       case SC_NOT_FOUND :
02145         setStatus(resCode, "Not found");
02146         break;
02147       case SC_METHOD_NOT_ALLOWED :
02148         setStatus(resCode, "Method not allowed");
02149         break;
02150       case SC_NOT_ACCEPTABLE :
02151         setStatus(resCode, "Not acceptable");
02152         break;
02153       case SC_PROXY_AUTHENTICATION_REQUIRED :
02154         setStatus(resCode, "Proxy auth required");
02155         break;
02156       case SC_REQUEST_TIMEOUT :
02157         setStatus(resCode, "Request timeout");
02158         break;
02159       case SC_CONFLICT :
02160         setStatus(resCode, "Conflict");
02161         break;
02162       case SC_GONE :
02163         setStatus(resCode, "Gone");
02164         break;
02165       case SC_LENGTH_REQUIRED :
02166         setStatus(resCode, "Length required");
02167         break;
02168       case SC_PRECONDITION_FAILED :
02169         setStatus(resCode, "Precondition failed");
02170         break;
02171       case SC_REQUEST_ENTITY_TOO_LARGE :
02172         setStatus(resCode, "Request entity too large");
02173         break;
02174       case SC_REQUEST_URI_TOO_LONG :
02175         setStatus(resCode, "Request URI too large");
02176         break;
02177       case SC_UNSUPPORTED_MEDIA_TYPE :
02178         setStatus(resCode, "Unsupported media type");
02179         break;
02180       case SC_INTERNAL_SERVER_ERROR :
02181         setStatus(resCode, "Internal server error");
02182         break;
02183       case SC_NOT_IMPLEMENTED :
02184         setStatus(resCode, "Not implemented");
02185         break;
02186       case SC_BAD_GATEWAY :
02187         setStatus(resCode, "Bad gateway");
02188         break;
02189       case SC_SERVICE_UNAVAILABLE :
02190         setStatus(resCode, "Service unavailable");
02191         break;
02192       case SC_GATEWAY_TIMEOUT :
02193         setStatus(resCode, "Gateway timeout");
02194         break;
02195       case SC_HTTP_VERSION_NOT_SUPPORTED :
02196         setStatus(resCode, "HTTP version not supported");
02197         break;
02198       default :
02199         setStatus(resCode, "");
02200         break;
02201     }
02202   }
02203 
02205   // @param name the header field name
02206   // @param value the header field value
02207   public void setHeader(String name, String value) {
02208     resHeaderNames.put(name, value);
02209   }
02210 
02212   // @param name the header field name
02213   // @param value the header field integer value
02214   public void setIntHeader(String name, int value) {
02215     setHeader(name, Integer.toString(value));
02216   }
02217 
02219   // @param name the header field name
02220   // @param value the header field long value
02221   public void setLongHeader(String name, long value) {
02222     setHeader(name, Long.toString(value));
02223   }
02224 
02226   // @param name the header field name
02227   // @param value the header field date value
02228   public void setDateHeader(String name, long value) {
02229     setHeader(name, expdatefmt.format(new Date(value)));
02230   }
02231 
02232   private static final String[] weekdays = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
02233   /*
02235   private static String to1123String( Date date )
02236   {
02237   // We have to go through some machinations here to get the
02238   // correct day of the week in GMT.  getDay() gives the day in
02239   // local time.  getDate() gives the day of the month in local
02240   // time.  toGMTString() gives a formatted string in GMT.  So, we
02241   // extract the day of the month from the GMT string, and if it
02242   // doesn't match the local one we change the local day of the
02243   // week accordingly.
02244   //
02245   // The Date class sucks.
02246   int localDay = date.getDay();
02247   int localDate = date.getDate();
02248   String gmtStr = date.toGMTString();
02249   int blank = gmtStr.indexOf( ' ' );
02250   int gmtDate = Integer.parseInt( gmtStr.substring( 0, blank ) );
02251   int gmtDay;
02252   if ( gmtDate > localDate || ( gmtDate < localDate && gmtDate == 1 ) )
02253   gmtDay = ( localDay + 1 ) % 7;
02254   else if ( localDate > gmtDate || ( localDate < gmtDate && localDate == 1 ) )
02255   gmtDay = ( localDay + 6 ) % 7;
02256   else
02257   gmtDay = localDay;
02258   return weekdays[gmtDay] + ( gmtDate < 10 ? ", 0" : ", " ) + gmtStr;
02259   }
02260   */
02261   private boolean headersWritten = false;
02262 
02264   // output stream.
02265   // @exception IOException if an I/O error has occurred
02266   void writeHeaders() throws IOException {
02267     synchronized (this) {
02268       if (headersWritten)
02269         return;
02270 
02271       headersWritten = true;
02272     }
02273     if (reqMime) {
02274       //boolean chunked_out = false;
02275       out.println(reqProtocol + " " + resCode + " " + resMessage);
02276 
02277       Enumeration he = resHeaderNames.keys();
02278       while (he.hasMoreElements()) {
02279         String name = (String) he.nextElement();
02280         Object o = resHeaderNames.get(name);
02281         if (o instanceof String) {
02282           String value = (String) o;
02283           if (value != null) // just in case
02284             out.println(name + ": " + value);
02285           /* moved to servlet
02286           if (TRANSFERENCODING.equals(name) && CHUNKED.equals(value))
02287           chunked_out = true;*/
02288         } else if (o instanceof String[]) {
02289           String[] values = (String[]) o;
02290           out.println(name + ": " + values[0]);
02291           for (int i = 0; i < values.length; i++)
02292             out.print("," + values[i]);
02293           out.println();
02294         }
02295       }
02296       StringBuffer sb = null;
02297       Cookie cc = null;
02298       // add session cookie
02299       // if cookie based session
02300       if (sessionCookieValue != null) {
02301         if (outCookies == null)
02302           outCookies = new Vector();
02303         cc = new Cookie(SESSION_COOKIE_NAME, sessionCookieValue);
02304         cc.setMaxAge(Math.abs(Serve.expiredIn) * 60);
02305         outCookies.addElement(cc);
02306       }
02307 
02308       // how to remove a cookie
02309       //        cc = new Cookie(cookieName, "");
02310       //        cc.setMaxAge(0);
02311       //
02312       for (int i = 0; outCookies != null && i < outCookies.size(); i++) {
02313         if (sb == null)
02314           sb = new StringBuffer(SETCOOKIE + ": ");
02315         else
02316           //sb.append(',');
02317           sb.append("\r\n" + SETCOOKIE + ": "); // for IE not understanding the standard
02318         cc = (Cookie) outCookies.elementAt(i);
02319         sb.append(cc.getName());
02320         sb.append('=');
02321         sb.append(cc.getValue());
02322         if (cc.getComment() != null) {
02323           sb.append("; Comment=" + cc.getComment());
02324         }
02325         if (cc.getDomain() != null) {
02326           sb.append("; Domain=" + cc.getDomain());
02327         }
02328 
02329         if (cc.getMaxAge() >= 0) {
02330           sb.append("; expires=");
02331           sb.append(expdatefmt.format(new Date(System.currentTimeMillis() + 1000 * cc.getMaxAge())));
02332         }
02333         if (cc.getPath() != null) {
02334           sb.append("; Path=" + cc.getPath());
02335         }
02336         if (cc.getSecure()) {
02337           sb.append("; Secure");
02338         }
02339         if (cc.getVersion() > 0) {
02340           sb.append("; Version=" + cc.getVersion());
02341         }
02342       }
02343       if (sb != null) {
02344         out.println(sb.toString());
02345         //System.err.println("We sent cookies: "+sb);
02346       }
02347       out.println("");
02348       out.flush();
02349       //if (chunked_out) ((ServeOutputStream)out).setChunked(true);
02350     }
02351   }
02352 
02354   // @param resCode the status code
02355   // @param resMessage the status message
02356   // @exception IOException if an I/O error has occurred
02357   public void sendError(int resCode, String resMessage) throws IOException {
02358     setStatus(resCode, resMessage);
02359     realSendError();
02360   }
02361 
02363   // message.
02364   // @param resCode the status code
02365   // @exception IOException if an I/O error has occurred
02366   public void sendError(int resCode) throws IOException {
02367     setStatus(resCode);
02368     realSendError();
02369   }
02370 
02371   private void realSendError() throws IOException {
02372     if (isCommitted())
02373       throw new IllegalStateException("Can not send error, headers have been already written");
02374     synchronized (out) {
02375       // no more out after error
02376        ((ServeOutputStream) out).setReturnedAsStream(true);
02377       ((ServeOutputStream) out).setReturnedAsWriter(true);
02378     }
02379     setContentType("text/html");
02380     StringBuffer sb = new StringBuffer(100);
02381     sb
02382       .append("<HTML><HEAD>")
02383       .append("<TITLE>" + resCode + " " + resMessage + "</TITLE>")
02384       .append("</HEAD><BODY BGCOLOR=\"#F1D0F2\">")
02385       .append("<H2>" + resCode + " " + resMessage + "</H2>")
02386       .append("<HR>");
02387     Serve.Identification.writeAddress(sb);
02388     sb.append("</BODY></HTML>");
02389     setContentLength(sb.length());
02390     out.print(sb.toString());
02391     out.flush();
02392   }
02393 
02395   // location URL.
02396   // @param location the redirect location URL
02397   // @exception IOException if an I/O error has occurred
02398   public void sendRedirect(String location) throws IOException {
02399     if (isCommitted())
02400       throw new IllegalStateException("Can not redirect, headers have been already written");
02401     synchronized (out) {
02402       // no more out after redirect
02403        ((ServeOutputStream) out).setReturnedAsStream(true);
02404       ((ServeOutputStream) out).setReturnedAsWriter(true);
02405     }
02406     setHeader("Location", location);
02407     setStatus(SC_MOVED_TEMPORARILY);
02408     setContentType("text/html");
02409     StringBuffer sb = new StringBuffer(200);
02410     sb.append(
02411       "<HTML><HEAD>"
02412         + "<TITLE>"
02413         + SC_MOVED_TEMPORARILY
02414         + " Moved</TITLE>"
02415         + "</HEAD><BODY BGCOLOR=\"#F1D0F2\">"
02416         + "<H2>"
02417         + SC_MOVED_TEMPORARILY
02418         + " Moved</H2>"
02419         + "This document has moved <a href="
02420         + location
02421         + ">here.<HR>");
02422     Serve.Identification.writeAddress(sb);
02423     sb.append("</BODY></HTML>");
02424     setContentLength(sb.length());
02425     // to avoid further out
02426     out.print(sb.toString());
02427     out.flush();
02428   }
02429 
02430   // URL session-encoding stuff.  Not implemented, but the API is here
02431   // for compatibility.
02432 
02434   // encoding is not needed, returns the URL unchanged. The
02435   // implementation of this method should include the logic to determine
02436   // whether the session ID needs to be encoded in the URL. For example,
02437   // if the browser supports cookies, or session tracking is turned off,
02438   // URL encoding is unnecessary.
02439   // <P>
02440   // All URLs emitted by a Servlet should be run through this method.
02441   // Otherwise, URL rewriting cannot be used with browsers which do not
02442   // support cookies.
02443   public String encodeUrl(String url) {
02444     return url;
02445   }
02446 
02448   // encoding is not needed, returns the URL unchanged. The
02449   // implementation of this method should include the logic to determine
02450   // whether the session ID needs to be encoded in the URL.  Because the
02451   // rules for making this determination differ from those used to
02452   // decide whether to encode a normal link, this method is seperate
02453   // from the encodeUrl method.
02454   // <P>
02455   // All URLs sent to the HttpServletResponse.sendRedirect method should be
02456   // run through this method.  Otherwise, URL rewriting cannot be used with
02457   // browsers which do not support cookies.
02458   public String encodeRedirectUrl(String url) {
02459     return url;
02460   }
02461 
02462 }
02463 
02464 class BasicAuthRealm extends Hashtable {
02465   String name;
02466   BasicAuthRealm(String name) {
02467     this.name = name;
02468   }
02469 
02470   String name() {
02471     return name;
02472   }
02473 }
02474 
02475 class ServeInputStream extends ServletInputStream {
02476   /* ------------------------------------------------------------ */
02479   private BufferedInputStream in;
02480   private int chunksize = 0;
02481   private boolean chunking = false;
02482   private boolean returnedAsReader, returnedAsStream;
02483 
02484   /* ------------------------------------------------------------ */
02487   public ServeInputStream(InputStream in) {
02488     this.in = new BufferedInputStream(in);
02489   }
02490 
02491   /* ------------------------------------------------------------ */
02495   public void chunking(boolean chunking) {
02496     this.chunking = chunking;
02497   }
02498 
02499   /* ------------------------------------------------------------ */
02505   // TODO: won't work with encoding
02506   public String readLine() throws IOException {
02507     StringBuffer buf = new StringBuffer(1024);
02508 
02509     int c;
02510     boolean cr = false;
02511     boolean lf = false;
02512 
02513     LineLoop : while ((c = chunking ? read() : in.read()) != -1) {
02514       switch (c) {
02515         case 10 :
02516           lf = true;
02517           break LineLoop;
02518 
02519         case 13 :
02520           cr = true;
02521           if (!chunking)
02522             in.mark(2);
02523           break;
02524 
02525         default :
02526           if (cr) {
02527             //if (chunking)
02528             //log("Cannot handle CR in chunking mode");
02529             in.reset();
02530             break LineLoop;
02531           } else
02532             buf.append((char) c);
02533           break;
02534       }
02535     }
02536 
02537     if (c == -1 && buf.length() == 0)
02538       return null;
02539     //System.err.println(buf.toString()); //###
02540 
02541     return buf.toString();
02542   }
02543 
02544   /* ------------------------------------------------------------ */
02545   public int read() throws IOException {
02546     if (chunking) {
02547       int b = -1;
02548       if (chunksize <= 0 && getChunkSize() <= 0)
02549         return -1;
02550       b = in.read();
02551       chunksize = (b < 0) ? -1 : (chunksize - 1);
02552       return b;
02553     }
02554 
02555     return in.read();
02556   }
02557 
02558   /* ------------------------------------------------------------ */
02559   public int read(byte b[]) throws IOException {
02560     return read(b, 0, b.length);
02561   }
02562 
02563   /* ------------------------------------------------------------ */
02564   public int read(byte b[], int off, int len) throws IOException {
02565     if (chunking) {
02566       if (chunksize <= 0 && getChunkSize() <= 0)
02567         return -1;
02568       if (len > chunksize)
02569         len = chunksize;
02570       len = in.read(b, off, len);
02571       chunksize = (len < 0) ? -1 : (chunksize - len);
02572     } else
02573       len = in.read(b, off, len);
02574     //System.err.println(new String(b,off,len)); //###        
02575 
02576     return len;
02577   }
02578 
02579   /* ------------------------------------------------------------ */
02580   public long skip(long len) throws IOException {
02581     if (chunking) {
02582       if (chunksize <= 0 && getChunkSize() <= 0)
02583         return -1;
02584       if (len > chunksize)
02585         len = chunksize;
02586       len = in.skip(len);
02587       chunksize = (len < 0) ? -1 : (chunksize - (int) len);
02588     } else
02589       len = in.skip(len);
02590     return len;
02591   }
02592 
02593   /* ------------------------------------------------------------ */
02597   public int available() throws IOException {
02598     if (chunking) {
02599       int len = in.available();
02600       if (len <= chunksize)
02601         return len;
02602       return chunksize;
02603     }
02604 
02605     return in.available();
02606   }
02607 
02608   /* ------------------------------------------------------------ */
02609   public void close() throws IOException {
02610     in.close();
02611     chunksize = -1;
02612   }
02613 
02614   /* ------------------------------------------------------------ */
02618   public boolean markSupported() {
02619     return false;
02620   }
02621 
02622   /* ------------------------------------------------------------ */
02625   public void reset() {
02626   }
02627 
02628   /* ------------------------------------------------------------ */
02632   public void mark(int readlimit) {
02633   }
02634 
02635   /* ------------------------------------------------------------ */
02636   private int getChunkSize() throws IOException {
02637     if (chunksize < 0)
02638       return -1;
02639 
02640     chunksize = -1;
02641 
02642     // Get next non blank line
02643     chunking = false;
02644     String line = readLine();
02645     while (line != null && line.length() == 0)
02646       line = readLine();
02647     chunking = true;
02648 
02649     // Handle early EOF or error in format
02650     if (line == null)
02651       return -1;
02652 
02653     // Get chunksize
02654     int i = line.indexOf(';');
02655     if (i > 0)
02656       line = line.substring(0, i).trim();
02657     chunksize = Integer.parseInt(line, 16);
02658 
02659     // check for EOF
02660     if (chunksize == 0) {
02661       chunksize = -1;
02662       // Look for footers
02663       chunking = false;
02664     }
02665     return chunksize;
02666   }
02667 
02668   boolean isReturnedAsStream() {
02669     return returnedAsStream;
02670   }
02671 
02672   void setReturnedAsStream(boolean _on) {
02673     returnedAsStream = _on;
02674   }
02675 
02676   boolean isReturnedAsReader() {
02677     return returnedAsReader;
02678   }
02679 
02680   void setReturnedAsReader(boolean _on) {
02681     returnedAsReader = _on;
02682   }
02683 
02684 }
02685 
02686 
02687 
02688 
02689 
02690 
02691 
02692 
02693 
02694 
02695 
02696 
02697 
02698 
02699 
02700 
02701 
02702 
02703 
02704 
02705 
02706 
02707 
02708 
02709 
02710 
02711 
02712 
02713 
02714 
02715 
02716 
02717 
02718 
02719 
02720 
02721 
02722 
02723 
02724 
02725 
02726 
02727 
02728 
02729 
02730 
02731 
02732 
02733 
02734 
02735 
02736 
02737 
02738 
02739 
02740 
02741 
02742 
02743 
02744 
02745 
02746 
02747 
02748 
02749 
02750 
02751 
02752 
02753 
02754 
02755 
02756 
02757 
02758 
02759 
02760 
02761 
02762 
02763 
02764 
02765 
02766 
02767 
02768 
02769 
02770 
02771 
02772 
02773 class ServeOutputStream extends ServletOutputStream {
02774 
02775   // underneath stream
02776   private OutputStream out;
02777   //private BufferedWriter writer; // for top speed
02778   private ServeConnection conn;
02779   private boolean returnedAsStream, returnedAsWriter;
02780 
02781   public ServeOutputStream(OutputStream out, ServeConnection conn) {
02782 
02783     this.out = out;
02784     this.conn = conn;
02785   }
02786 
02787   public void print(String s) throws IOException {
02788     write(s.getBytes());
02789   }
02790 
02791   public void write(int b) throws IOException {
02792     conn.writeHeaders();
02793     out.write(b);
02794   }
02795 
02796   public void write(byte[] b) throws IOException {
02797     write(b, 0, b.length);
02798   }
02799 
02800   public void write(byte[] b, int off, int len) throws IOException {
02801     conn.writeHeaders();
02802     out.write(b, off, len);
02803   }
02804 
02805   public void flush() throws IOException {
02806     conn.writeHeaders();
02807     out.flush();
02808   }
02809 
02810   public void close() throws IOException {
02811     conn.writeHeaders();
02812     out.close();
02813   }
02814 
02815   boolean isReturnedAsStream() {
02816     return returnedAsStream;
02817   }
02818 
02819   void setReturnedAsStream(boolean _set) {
02820     returnedAsStream = _set;
02821   }
02822 
02823   boolean isReturnedAsWriter() {
02824     return returnedAsWriter;
02825   }
02826 
02827   void setReturnedAsWriter(boolean _set) {
02828     returnedAsWriter = _set;
02829   }
02830 }
02831 
02832 
02833 
02834 
02835 
02836 
02837 
02838 
02839 
02840 
02841 
02842 
02843 
02844 
02845 
02846 
02847 
02848 
02849 
02850 
02851 
02852 
02853 
02854 
02855 
02856 
02857 
02858 
02859 
02860 
02861 
02862 
02863 
02864 
02865 
02871 class PathTreeDictionary {
02872   Node root_node;
02873 
02874   PathTreeDictionary() {
02875     root_node = new Node();
02876   }
02877 
02878   void put(String path, Object value) {
02879     StringTokenizer st = new StringTokenizer(path, "\\/");
02880     Node cur_node = root_node;
02881     while (st.hasMoreTokens()) {
02882       String nodename = st.nextToken();
02883       Node node = (Node) cur_node.get(nodename);
02884       if (node == null) {
02885         node = new Node();
02886         cur_node.put(nodename, node);
02887       }
02888       cur_node = node;
02889     }
02890     cur_node.object = value;
02891   }
02892 
02898   Object[] get(String path) {
02899     Object[] result = new Object[2];
02900     if (path == null)
02901       return result;
02902     char[] ps = path.toCharArray();
02903     Node cur_node = root_node;
02904     int p0 = 0, lm = 0; // last match
02905     result[0] = cur_node.object;
02906     boolean div_state = true;
02907     for (int i = 0; i < ps.length; i++) {
02908       if (ps[i] == '/' || ps[i] == '\\') {
02909         if (div_state)
02910           continue;
02911         Node node = (Node) cur_node.get(new String(ps, p0, i - p0));
02912         if (node == null) {
02913           result[1] = new Integer(lm);
02914           return result;
02915         }
02916         if (node.object != null) {
02917           result[0] = node.object;
02918           lm = i;
02919         }
02920         cur_node = node;
02921         div_state = true;
02922       } else {
02923         if (div_state) {
02924           p0 = i;
02925           div_state = false;
02926         }
02927       }
02928     }
02929     cur_node = (Node) cur_node.get(new String(ps, p0, ps.length - p0));
02930     if (cur_node != null && cur_node.object != null) {
02931       result[0] = cur_node.object;
02932       lm = ps.length;
02933     }
02934     result[1] = new Integer(lm);
02935     return result;
02936   }
02937 
02938   Enumeration keys() {
02939     Vector result = new Vector();
02940     addSiblingNames(root_node, result, "");
02941     return result.elements();
02942   }
02943 
02944   void addSiblingNames(Node node, Vector result, String path) {
02945     Enumeration e = node.keys();
02946     while (e.hasMoreElements()) {
02947       String pc = (String) e.nextElement();
02948       Node childNode = (Node) node.get(pc);
02949       pc = path + '/' + pc;
02950       if (childNode.object != null)
02951         result.addElement(pc);
02952       addSiblingNames(childNode, result, pc);
02953     }
02954   }
02955 
02956   Enumeration elements() {
02957     Vector result = new Vector();
02958     addSiblingObjects(root_node, result);
02959     return result.elements();
02960   }
02961 
02962   void addSiblingObjects(Node node, Vector result) {
02963     Enumeration e = node.keys();
02964     while (e.hasMoreElements()) {
02965       Node childNode = (Node) node.get(e.nextElement());
02966       if (childNode.object != null)
02967         result.addElement(childNode.object);
02968       addSiblingObjects(childNode, result);
02969     }
02970   }
02971 
02972   class Node extends Hashtable {
02973     Object object;
02974   }
02975 }
02976 
02980 class AcmeSession extends Hashtable implements HttpSession {
02981   private long createTime;
02982   private long lastAccessTime;
02983   private String id;
02984   private int inactiveInterval; // in seconds
02985   private boolean expired;
02986   private ServletContext servletContext;
02987   private HttpSessionContext sessionContext;
02988 
02989   // TODO: check in documentation what is default inactive interval and what means 0  
02990   // and what is mesurement unit
02991   AcmeSession(String id, ServletContext servletContext, HttpSessionContext sessionContext) {
02992     this(id, 0, servletContext, sessionContext);
02993   }
02994 
02995   AcmeSession(String id, int inactiveInterval, ServletContext servletContext, HttpSessionContext sessionContext) {
02996     createTime = System.currentTimeMillis();
02997     this.id = id;
02998     this.inactiveInterval = inactiveInterval;
02999     this.servletContext = servletContext;
03000     this.sessionContext = sessionContext;
03001   }
03002   public long getCreationTime() {
03003     return createTime;
03004   }
03005 
03006   public String getId() {
03007     return id;
03008   }
03009 
03010   public long getLastAccessedTime() {
03011     return lastAccessTime;
03012   }
03013 
03014   public void setMaxInactiveInterval(int interval) {
03015     inactiveInterval = interval;
03016   }
03017 
03018   public int getMaxInactiveInterval() {
03019     return inactiveInterval;
03020   }
03021 
03025   public HttpSessionContext getSessionContext() {
03026 
03027     return sessionContext;
03028   }
03029 
03035   public ServletContext getServletContext() {
03036     return servletContext;
03037   }
03038 
03039   public java.lang.Object getAttribute(java.lang.String name) throws IllegalStateException {
03040     if (expired)
03041       throw new IllegalStateException();
03042     return get((Object) name);
03043   }
03044 
03045   public java.lang.Object getValue(java.lang.String name) throws IllegalStateException {
03046     return getAttribute(name);
03047   }
03048 
03049   public java.util.Enumeration getAttributeNames() throws IllegalStateException {
03050     if (expired)
03051       throw new IllegalStateException();
03052     return keys();
03053   }
03054 
03055   public java.lang.String[] getValueNames() throws IllegalStateException {
03056     Enumeration e = getAttributeNames();
03057     Vector names = new Vector();
03058     while (e.hasMoreElements())
03059       names.addElement(e.nextElement());
03060     String[] result = new String[names.size()];
03061     names.copyInto(result);
03062     return result;
03063   }
03064 
03065   public void setAttribute(String name, Object value) throws IllegalStateException {
03066     if (expired)
03067       throw new IllegalStateException();
03068     Object oldValue = put((Object) name, value);
03069     if (oldValue != null && oldValue instanceof HttpSessionBindingListener)
03070        ((HttpSessionBindingListener) oldValue).valueUnbound(new HttpSessionBindingEvent(this, name));
03071     if (value instanceof HttpSessionBindingListener)
03072        ((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name));
03073   }
03074 
03075   public void putValue(String name, Object value) throws IllegalStateException {
03076     setAttribute(name, value);
03077   }
03078 
03079   public void removeAttribute(java.lang.String name) throws IllegalStateException {
03080     if (expired)
03081       throw new IllegalStateException();
03082     Object value = remove((Object) name);
03083     if (value != null && value instanceof HttpSessionBindingListener)
03084        ((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name));
03085   }
03086 
03087   public void removeValue(java.lang.String name) throws IllegalStateException {
03088     removeAttribute(name);
03089   }
03090 
03091   public synchronized void invalidate() throws IllegalStateException {
03092     if (expired)
03093       throw new IllegalStateException();
03094     Enumeration e = getAttributeNames();
03095     while (e.hasMoreElements()) {
03096       removeAttribute((String) e.nextElement());
03097     }
03098     setExpired(true);
03099     // would be nice remove it from hash table also
03100   }
03101 
03102   public boolean isNew() throws IllegalStateException {
03103     if (expired)
03104       throw new IllegalStateException();
03105     return lastAccessTime == 0;
03106   }
03107 
03108   private void setExpired(boolean expired) {
03109     this.expired = expired;
03110   }
03111 
03112   boolean isValid() {
03113     return !expired;
03114   }
03115 
03116   void userTouch() {
03117     lastAccessTime = System.currentTimeMillis();
03118   }
03119 }
03120 
03121 // TODO: reconsider implementation by providing
03122 // inner class implementing HttpSessionContext
03123 // and returning it on request
03124 // to avoid casting this class to Hashtable
03125 class HttpSessionContextImpl extends Hashtable implements HttpSessionContext {
03126 
03127   public java.util.Enumeration getIds() {
03128     return keys();
03129   }
03130 
03131   public HttpSession getSession(java.lang.String sessionId) {
03132     return (HttpSession) get(sessionId);
03133   }
03134 }

Generated on Wed Dec 14 21:05:35 2005 for OpenMobileIS by  doxygen 1.4.4