KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > quadcap > http > server22 > WebServer


1 package com.quadcap.http.server22;
2
3 /* Copyright 1997 - 2003 Quadcap Software. All rights reserved.
4  *
5  * This software is distributed under the Quadcap Free Software License.
6  * This software may be used or modified for any purpose, personal or
7  * commercial. Open Source redistributions are permitted. Commercial
8  * redistribution of larger works derived from, or works which bundle
9  * this software requires a "Commercial Redistribution License"; see
10  * http://www.quadcap.com/purchase.
11  *
12  * Redistributions qualify as "Open Source" under one of the following terms:
13  *
14  * Redistributions are made at no charge beyond the reasonable cost of
15  * materials and delivery.
16  *
17  * Redistributions are accompanied by a copy of the Source Code or by an
18  * irrevocable offer to provide a copy of the Source Code for up to three
19  * years at the cost of materials and delivery. Such redistributions
20  * must allow further use, modification, and redistribution of the Source
21  * Code under substantially the same terms as this license.
22  *
23  * Redistributions of source code must retain the copyright notices as they
24  * appear in each source code file, these license terms, and the
25  * disclaimer/limitation of liability set forth as paragraph 6 below.
26  *
27  * Redistributions in binary form must reproduce this Copyright Notice,
28  * these license terms, and the disclaimer/limitation of liability set
29  * forth as paragraph 6 below, in the documentation and/or other materials
30  * provided with the distribution.
31  *
32  * The Software is provided on an "AS IS" basis. No warranty is
33  * provided that the Software is free of defects, or fit for a
34  * particular purpose.
35  *
36  * Limitation of Liability. Quadcap Software shall not be liable
37  * for any damages suffered by the Licensee or any third party resulting
38  * from use of the Software.
39  */

40
41 import java.util.Date JavaDoc;
42 import java.util.Enumeration JavaDoc;
43 import java.util.Hashtable JavaDoc;
44 import java.util.Properties JavaDoc;
45 import java.util.Random JavaDoc;
46 import java.util.Vector JavaDoc;
47
48 import java.io.File JavaDoc;
49 import java.io.FileInputStream JavaDoc;
50 import java.io.FileOutputStream JavaDoc;
51 import java.io.InputStream JavaDoc;
52 import java.io.IOException JavaDoc;
53 import java.io.PrintWriter JavaDoc;
54
55 import java.text.SimpleDateFormat JavaDoc;
56
57 import java.net.InetAddress JavaDoc;
58 import java.net.MalformedURLException JavaDoc;
59 import java.net.Socket JavaDoc;
60 import java.net.URL JavaDoc;
61 import java.net.URLConnection JavaDoc;
62
63 import javax.servlet.RequestDispatcher JavaDoc;
64 import javax.servlet.Servlet JavaDoc;
65 import javax.servlet.ServletContext JavaDoc;
66 import javax.servlet.ServletException JavaDoc;
67 import javax.servlet.ServletRequest JavaDoc;
68 import javax.servlet.ServletResponse JavaDoc;
69
70 import javax.servlet.http.HttpServlet JavaDoc;
71 import javax.servlet.http.HttpServletRequest JavaDoc;
72 import javax.servlet.http.HttpSession JavaDoc;
73 import javax.servlet.http.HttpSessionContext JavaDoc;
74
75 import com.quadcap.net.server.Server;
76 import com.quadcap.net.server.Worker;
77
78 import com.quadcap.util.threads.Command;
79 import com.quadcap.util.threads.PeriodicScheduler;
80
81 import com.quadcap.util.text.OctetMap;
82 import com.quadcap.util.text.Scanner;
83
84 import com.quadcap.io.dir.Directory;
85
86 import com.quadcap.util.Config;
87 import com.quadcap.util.Debug;
88 import com.quadcap.util.Util;
89
90 /**
91  * A basic HTTP server.
92  *
93  * @author Stan Bailes
94  */

95 public class WebServer {
96     Random JavaDoc random = new Random JavaDoc();
97     
98     /**
99      * My (cached) host name
100      */

101     String JavaDoc hostName = null;
102
103     /**
104      * My servlet contexts
105      */

106     Hashtable JavaDoc contexts = new Hashtable JavaDoc();
107
108     /**
109      * Default mime types
110      */

111     Hashtable JavaDoc mimeTypes = new Hashtable JavaDoc();
112
113     WebApplication defaultContext = null;
114
115     File JavaDoc tmpDir;
116
117     long sessionCount = 0;
118     static final int defaultInactiveInterval = 60;
119     static final int expireCheckerInterval = 60;
120
121     PeriodicScheduler expireChecker;
122
123     PrintWriter JavaDoc reqStream;
124
125     Server server;
126
127     /**
128      * Server interface: initialize this server
129      *
130      * @param p the properties used to specify information about the
131      * server's startup.
132      * @exception IOException may be thrown
133      */

134     /*{com.quadcap.http.server22.WebServer.xml}
135      *
136      * <config>
137      * <config-var>
138      * <config-name>tempDir</config-name>
139      * <config-dflt>./repository</config-dflt>
140      * <config-desc>Specifies the name of a directory where temporary
141      * JSP-generated files are placed.</config-desc>
142      * </config-var>
143      *
144      * <config-var>
145      * <config-name>context.*.root</config-name>
146      * <config-dflt><i>none</i></config-dflt>
147      * <config-desc>Specify the context path for a web application.
148      * The application
149      * name is specified by the 'wildcard' portion of the property
150      * name.</config-desc>
151      * </config-var>
152      *
153      * <config-var>
154      * <config-name>context.*.docBase</config-name>
155      * <config-dflt><i>none</i></config-dflt>
156      * <config-desc>Specify the path to the web application's files,
157      * either as
158      * a directory name or the name of a Web Application Archive
159      * (.war) file.</config-desc>
160      * </config-var>
161      *
162      * <config-var>
163      * <config-name>acceptor.*.port</config-name>
164      * <config-dflt><i>none</i></config-dflt>
165      * <config-desc>Specify the port number for the server to listen on.
166      * The
167      * wildcard portion of the name gives the name of the socket
168      * acceptor, and doesn't have any real significance other than
169      * as a unique name for the acceptor.</config-desc>
170      * </config-var>
171      *
172      * <config-var>
173      * <config-name>acceptor.*.queueDepth</config-name>
174      * <config-dflt>32</config-dflt>
175      * <config-desc>Specify the TCP queue depth associated with the named
176      * acceptor.</config-desc>
177      * </config-var>
178      *
179      * <include name="docstubs/com.quadcap.net.server.Server.xml"/>
180      *
181      * </config>
182      */

183     public void init(Properties JavaDoc p)
184     throws Exception JavaDoc
185     {
186         p.put("workerClass", "com.quadcap.http.server22.WebWorker");
187     server = new Server(p, this);
188
189         String JavaDoc tmp = p.getProperty("tempDir", "repository");
190         tmpDir = new File JavaDoc(tmp);
191         if (!tmpDir.isDirectory() && !tmpDir.mkdirs()) {
192             throw new IOException JavaDoc("Can't create temp directory: " + tmp);
193         }
194
195         tmp = p.getProperty("requestLog", "web.log");
196         reqStream = new PrintWriter JavaDoc(new FileOutputStream JavaDoc(tmp));
197
198         InputStream JavaDoc dis = ClassLoader.getSystemResourceAsStream(
199             "com/quadcap/http/server22/mime.types");
200         if (dis != null) {
201             try {
202                 parseMimeTypes(dis);
203             } finally {
204                 dis.close();
205             }
206         }
207         
208         Enumeration JavaDoc e = Config.getMatchingProps(p, "context.*.root");
209         while (e.hasMoreElements()) {
210             String JavaDoc context = e.nextElement().toString();
211             String JavaDoc c = "context." + context;
212             String JavaDoc root = p.getProperty(c + ".root");
213             String JavaDoc docBase = p.getProperty(c + ".docBase");
214             if (docBase == null) {
215                 Debug.println(0, "No docBase specified for context " +
216                               context);
217             } else {
218                 try {
219                     WebApplication app = addWebApplication(root, docBase);
220                     if (context.equals("default")) {
221                         defaultContext = app;
222                     }
223                 } catch (Throwable JavaDoc t) {
224                     Debug.print(t);
225                 }
226             }
227         }
228
229         boolean found = false;
230         e = Config.getMatchingProps(p, "acceptor.*.port");
231         while (e.hasMoreElements()) {
232             found = true;
233             String JavaDoc acceptor = e.nextElement().toString();
234             String JavaDoc a = "acceptor." + acceptor;
235             Properties JavaDoc ap = new Properties JavaDoc();
236             ap.put("port", p.getProperty(a + ".port"));
237             ap.put("queueDepth", p.getProperty(a + ".queueDepth", "32"));
238             Debug.println("startAcceptor: " + ap.get("port"));
239             server.startAcceptor(ap);
240         }
241
242         expireChecker = new PeriodicScheduler(server.getThreadGroup(),
243                                               "expire checker",
244                                               this);
245         expireChecker.add("Check for expired sessions",
246                           new ExpireChecker(),
247                           expireCheckerInterval * 1000);
248         expireChecker.start();
249         if (!found) {
250             Debug.println(0, "No acceptor.<name>.port properties found!!");
251         }
252     }
253
254     public void stop() {
255         server.stop();
256     }
257
258     final void parseMimeTypes(InputStream JavaDoc is) throws IOException JavaDoc {
259         Scanner s = new Scanner(is);
260         s.skipWhile(OctetMap.crlfChars);
261         int c;
262         while ((c = s.peek()) >= 0) {
263             if (c == '#') {
264                 s.skipUntil(OctetMap.crlfChars);
265             } else {
266                 String JavaDoc mimeType = s.parseWhile(OctetMap.uriChars);
267                 s.skipWhile(OctetMap.wsChars);
268                 while ((c = s.peek()) >= 0 && OctetMap.tokenChars.has(c)) {
269                     String JavaDoc ext = s.parseWhile(OctetMap.tokenChars);
270                     mimeTypes.put(ext, mimeType);
271                     s.skipWhile(OctetMap.wsChars);
272                 }
273                 s.skipUntil(OctetMap.crlfChars);
274             }
275             s.skipWhile(OctetMap.crlfChars);
276         }
277         is.close();
278     }
279
280     public void removeWebApplication(String JavaDoc root) {
281         WebApplication app = getContextForRoot(root);
282         if (app != null) {
283             contexts.remove(root);
284             app.shutdown();
285         }
286     }
287
288     public WebApplication addWebApplication(String JavaDoc root, String JavaDoc docBase)
289         throws IOException JavaDoc, ServletException JavaDoc
290     {
291         // ---- remove trailing slash from context path
292
int len = root.length();
293         if (len > 0 && root.charAt(len-1) == '/') {
294             root = root.substring(0, len-1);
295         }
296         //#ifdef DEBUG
297
if (Trace.level() > 1) {
298             Debug.println("addWebApplication(" + root + ", " + docBase + ")");
299         }
300         //#endif
301

302         WebApplication oldApp = (WebApplication)contexts.get(root);
303         if (oldApp != null) {
304             try {
305                 Debug.println("[Shutting down old app for " + root + ": " +
306                               oldApp);
307                 oldApp.shutdown();
308             } catch (Throwable JavaDoc t) {
309             }
310         }
311
312         File JavaDoc dir = new File JavaDoc(docBase);
313         Directory d = Directory.getDirectory(dir);
314         WebApplication app = new WebApplication();
315         app.init(this, root, d);
316
317         contexts.put(root, app);
318
319         
320         return app;
321     }
322
323     /**
324      * Returns the name and version of the network service under which the
325      * servlet is running.
326      *
327      * @return the server information string
328      */

329     public String JavaDoc getServerInfo() {
330         //> return \"Quadcap Web Server [defined VERSION]\";
331
//#autogen begin
332
return "Quadcap Web Server 3.4";
333 //#autogen end
334
}
335
336     /**
337      * Returns the mime type of the specified file, or null if not known.
338      */

339     public String JavaDoc getMimeTypeForExt(String JavaDoc ext) {
340         String JavaDoc type = (String JavaDoc)mimeTypes.get(ext.toLowerCase());
341         return type;
342     }
343
344     /**
345      * Return the servlet context for the specified path.
346      */

347     public WebApplication getContext(String JavaDoc path) {
348         WebApplication ret = (WebApplication)contexts.get(path);
349         for (int i = path.length() - 1; ret == null && i >= 0; i--) {
350             if (path.charAt(i) == '/') {
351                 String JavaDoc subPath = path.substring(0, i);
352                 ret = (WebApplication)contexts.get(subPath);
353             }
354         }
355         if (ret == null) ret = defaultContext;
356         Debug.println("defaultContext = " + defaultContext);
357         return ret;
358     }
359
360     String JavaDoc makeSessionId() {
361         int r = random.nextInt() & 0xfffffff;
362         return "" + (sessionCount++) + "." + r + "." +
363             System.currentTimeMillis();
364     }
365     
366     public void expireSessions() {
367         Enumeration JavaDoc e = contexts.elements();
368         while (e.hasMoreElements()) {
369             WebApplication app = (WebApplication)e.nextElement();
370             app.expireSessions();
371         }
372     }
373     
374     final File JavaDoc getTempDir() {
375         return tmpDir;
376     }
377
378     public Enumeration JavaDoc getContextRoots() {
379         return contexts.keys();
380     }
381
382     public WebApplication getContextForRoot(String JavaDoc root) {
383         return (WebApplication)contexts.get(root);
384     }
385
386     public static void main(String JavaDoc args[]) {
387         Properties JavaDoc p = Config.getProperties();
388         WebServer w = new WebServer();
389         try {
390             w.init(p);
391         } catch (Throwable JavaDoc t) {
392             Debug.print(t);
393         }
394     }
395
396     static SimpleDateFormat JavaDoc df;
397     static {
398         df = new SimpleDateFormat JavaDoc("yyyy/MM/dd HH:mm:ss.SSS");
399     }
400     public void requestLog(String JavaDoc s) {
401         reqStream.println(df.format(new Date JavaDoc()) + " " + s);
402         reqStream.flush();
403     }
404 }
405
406
407 class ExpireChecker implements Command {
408     public void execute(Object JavaDoc context) {
409         ((WebServer)context).expireSessions();
410     }
411 }
412
413
Popular Tags