001: package com.quadcap.http.server22;
002:
003: /* Copyright 1997 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.File;
042: import java.io.InputStream;
043: import java.io.InputStreamReader;
044: import java.io.IOException;
045: import java.io.Reader;
046:
047: import java.util.ArrayList;
048: import java.util.Collections;
049: import java.util.Comparator;
050: import java.util.Enumeration;
051: import java.util.HashMap;
052: import java.util.Hashtable;
053: import java.util.List;
054: import java.util.Properties;
055: import java.util.Vector;
056:
057: import java.net.URL;
058: import java.net.MalformedURLException;
059:
060: import javax.servlet.RequestDispatcher;
061: import javax.servlet.Servlet;
062: import javax.servlet.ServletContext;
063: import javax.servlet.ServletException;
064: import javax.servlet.ServletRequest;
065: import javax.servlet.ServletResponse;
066:
067: import javax.servlet.http.HttpServlet;
068: import javax.servlet.http.HttpServletRequest;
069: import javax.servlet.http.HttpSession;
070: import javax.servlet.http.HttpSessionContext;
071:
072: import com.quadcap.io.dir.Directory;
073: import com.quadcap.io.dir.Entry;
074:
075: import com.quadcap.util.Debug;
076: import com.quadcap.util.Util;
077:
078: /**
079: * This class provides a wrapper for a Web Application, implementing
080: * the application's ServletContext interface.
081: *
082: * @author Stan Bailes
083: */
084: public class WebApplication implements ServletContext {
085: WebServer server;
086: WebClassLoader classLoader;
087: String contextPath;
088:
089: /**
090: * Map name -> Servlet
091: */
092: HashMap servlets = new HashMap();
093:
094: /**
095: * Session table: sessionId -> HttpSession
096: */
097: Hashtable sessions = new Hashtable();
098:
099: /**
100: * Servlet context attributes
101: */
102: Hashtable attributes = new Hashtable();
103:
104: /**
105: * Mime types
106: */
107: Hashtable mimeTypes = new Hashtable();
108:
109: /**
110: * Path mappings
111: */
112: Hashtable pathMappings = new Hashtable();
113: Hashtable extMappings = new Hashtable();
114: Hashtable exactMappings = new Hashtable();
115:
116: WebServlet defaultServlet = null;
117:
118: /**
119: * Welcome file list
120: */
121: List welcomeFiles = new ArrayList();
122:
123: /**
124: * Error page
125: */
126: String errorPage = null;
127:
128: /**
129: * My document root
130: */
131: Directory docRoot;
132:
133: String displayName = "";
134:
135: long sessionCount = 0;
136: int sessionTimeout = 3600;
137:
138: Properties initParams = new Properties();
139:
140: public WebApplication() {
141: }
142:
143: public void init(WebServer server, String root, Directory dir)
144: throws ServletException, IOException {
145: //#ifdef DEBUG
146: if (Trace.level() > 0) {
147: Debug.println(toString() + ".init(" + root + ", "
148: + dir.getRootPath() + ")");
149: }
150: //#endif
151: this .server = server;
152: this .docRoot = dir;
153: this .contextPath = root;
154:
155: File tmp = server.getTempDir();
156: File myTmp = new File(tmp, root);
157: if (!myTmp.isDirectory() && !myTmp.mkdirs()) {
158: throw new ServletException("Can't create repository: "
159: + myTmp);
160: }
161: setAttribute("repository", myTmp);
162: classLoader = new WebClassLoader(dir, myTmp);
163: classLoader.init();
164: attributes.put("com.quadcap.classLoader", classLoader);
165: InputStream dis = ClassLoader
166: .getSystemResourceAsStream("com/quadcap/http/server22/defaultContext.xml");
167: if (dis == null) {
168: throw new ServletException("Can't load default context");
169: }
170: Reader dfr = new InputStreamReader(dis);
171: try {
172: parseDeploymentDescriptor(dfr);
173: } finally {
174: dfr.close();
175: }
176: setDisplayName("");
177:
178: Entry entry = dir.getEntry("WEB-INF/web.xml");
179: if (entry != null) {
180: Reader ddr = new InputStreamReader(entry.getInputStream());
181: try {
182: parseDeploymentDescriptor(ddr);
183: } finally {
184: ddr.close();
185: }
186: }
187: resolveServlets(pathMappings);
188: resolveServlets(extMappings);
189: resolveServlets(exactMappings);
190: loadInitialServlets();
191: }
192:
193: public void shutdown() {
194: synchronized (sessions) {
195: Enumeration e = sessions.keys();
196: while (e.hasMoreElements()) {
197: String sessionId = (String) e.nextElement();
198: HSession session = (HSession) getSession(sessionId);
199: session.invalidate();
200: }
201: }
202: if (docRoot != null) {
203: try {
204: docRoot.close();
205: } catch (Throwable t) {
206: }
207: }
208: }
209:
210: final void resolveServlets(Hashtable t) {
211: Enumeration e = t.keys();
212: while (e.hasMoreElements()) {
213: Object key = e.nextElement();
214: Object obj = t.get(key);
215: if (obj instanceof String) {
216: Object s = servlets.get(obj);
217: if (s == null) {
218: Debug.println("No servlet for mapping: " + obj);
219: t.remove(key);
220: } else {
221: t.put(key, s);
222: }
223: }
224: }
225: }
226:
227: void parseDeploymentDescriptor(Reader r) throws ServletException,
228: IOException {
229: try {
230: new DDParser().parse(r, this );
231: } catch (Exception e) {
232: throw new ServletException(e);
233: } finally {
234: r.close();
235: }
236: }
237:
238: public void setDisplayName(String name) {
239: this .displayName = name;
240: }
241:
242: public String getDisplayName() {
243: return displayName;
244: }
245:
246: public void addInitParam(String prop, String val) {
247: initParams.setProperty(prop, val);
248: }
249:
250: public Enumeration getInitParameterNames() {
251: return initParams.keys();
252: }
253:
254: public String getInitParameter(String name) {
255: return initParams.getProperty(name);
256: }
257:
258: public void addWelcomeFile(String s) {
259: welcomeFiles.add(s.trim());
260: }
261:
262: public List getWelcomeFiles() {
263: if (welcomeFiles.size() == 0) {
264: welcomeFiles.add("index.html");
265: welcomeFiles.add("index.jsp");
266: }
267: return welcomeFiles;
268: }
269:
270: public void addServlet(WebServlet servlet) {
271: //#ifdef DEBUG
272: if (Trace.level() > 1) {
273: Debug.println(toString() + ".addServlet("
274: + servlet.getServletName() + ")");
275: }
276: //#endif
277: servlets.put(servlet.getServletName(), servlet);
278: }
279:
280: WebServlet getServletForRequest(HttpDispatcher rd) {
281: String path = rd.getServletPath();
282:
283: WebServlet s = (WebServlet) exactMappings.get(path);
284: if (s != null) {
285: rd.setServlet(s, path);
286: } else {
287: int lastDot = -1;
288: for (int i = path.length() - 1; i >= 0; i--) {
289: char c = path.charAt(i);
290: if (c == '/') {
291: String subPath = path.substring(0, i);
292: s = (WebServlet) pathMappings.get(subPath);
293: if (s != null) {
294: rd.setServlet(s, subPath);
295: rd.setPathInfo(path.substring(i));
296: break;
297: }
298: } else if (c == '.' && lastDot < 0) {
299: lastDot = i;
300: }
301: }
302: if (s == null && lastDot >= 0) {
303: String ext = path.substring(lastDot + 1);
304: s = (WebServlet) extMappings.get(ext);
305: if (s != null) {
306: rd.setServlet(s, path);
307: }
308: }
309: if (s == null) {
310: s = defaultServlet;
311: rd.setServlet(s, path);
312: }
313: }
314: //#ifdef DEBUG
315: if (Trace.level() > 1) {
316: Debug.println(toString() + ".getServlet(" + path + ") = "
317: + s);
318: }
319: //#endif
320: return s;
321: }
322:
323: public void setSessionTimeout(int timeout) {
324: this .sessionTimeout = timeout;
325: }
326:
327: public int getSessionTimeout() {
328: return sessionTimeout;
329: }
330:
331: public void addServletMapping(String servletName, String urlPattern) {
332: //#ifdef DEBUG
333: if (Trace.level() > 0) {
334: Debug.println(this + ".addServletMapping(" + servletName
335: + ", " + urlPattern + ")");
336: }
337: //#endif
338:
339: if (urlPattern.equals("*.*")) {
340: defaultServlet = (WebServlet) servlets.get(servletName);
341: } else if (urlPattern.endsWith("*")) {
342: String path = urlPattern.substring(0,
343: urlPattern.length() - 2);
344: pathMappings.put(path, servletName);
345: } else if (urlPattern.startsWith("*.")) {
346: String ext = urlPattern.substring(2);
347: extMappings.put(ext, servletName);
348: } else {
349: exactMappings.put(urlPattern, servletName);
350: }
351: }
352:
353: public void setErrorPage(String errorPage) {
354: this .errorPage = errorPage;
355: }
356:
357: public String getErrorPage() {
358: return errorPage;
359: }
360:
361: public Object getAttribute(String name) {
362: return attributes.get(name);
363: }
364:
365: public Enumeration getAttributeNames() {
366: return attributes.keys();
367: }
368:
369: public void setAttribute(String name, Object val) {
370: attributes.put(name, val);
371: }
372:
373: public void removeAttribute(String name) {
374: attributes.remove(name);
375: }
376:
377: /**
378: * Returns the name and version of the network service under which the
379: * servlet is running.
380: *
381: * @return the server information string
382: */
383: public String getServerInfo() {
384: //> return \"Quadcap Web Server [defined VERSION]\";
385: //#autogen begin
386: return "Quadcap Web Server 3.4";
387: //#autogen end
388: }
389:
390: /**
391: * Return the server major version number
392: */
393: public int getMajorVersion() {
394: return 2;
395: }
396:
397: /**
398: * Return the server minor version number
399: */
400: public int getMinorVersion() {
401: return 2;
402: }
403:
404: public ServletContext getContext(String path) {
405: return server.getContext(path);
406: }
407:
408: /**
409: * @param path is a string that must begin with '/' and is interpreted
410: * relative to this context's root
411: */
412: public RequestDispatcher getRequestDispatcher(String path) {
413: HttpDispatcher rd = new HttpDispatcher(this , path);
414: WebServlet ws = getServletForRequest(rd);
415: return (ws != null) ? rd : null;
416: }
417:
418: public RequestDispatcher getNamedDispatcher(String name) {
419: WebServlet ws = (WebServlet) servlets.get(name);
420: if (ws != null) {
421: return new HttpDispatcher(ws, name);
422: } else {
423: return null;
424: }
425: }
426:
427: public RequestDispatcher getRelativeRequestDispatcher(String path,
428: HttpRequest base) {
429: if (path.charAt(0) == '/') {
430: return getRequestDispatcher(path);
431: } else {
432: String spath = base.getServletPath();
433: int spos = spath.length() - 1;
434: while (spos >= 0 && spath.charAt(spos) != '/')
435: spos--;
436:
437: while (path.startsWith("./") || path.startsWith("../")) {
438: if (path.startsWith("./")) {
439: path = path.substring(2);
440: } else {
441: if (spos > 0) {
442: spos--;
443: while (spos >= 0 && spath.charAt(spos) != '/')
444: spos--;
445: } else {
446: return null;
447: }
448: }
449: }
450: String absPath = spath.substring(0, spos + 1) + path;
451: return getRequestDispatcher(absPath);
452: }
453: }
454:
455: void addMimeMapping(String ext, String type) {
456: //#ifdef DEBUG
457: if (Trace.level() > 0) {
458: Debug.println(toString() + ".addMimeMapping(" + ext + ", "
459: + type + ")");
460: }
461: //#endif
462: mimeTypes.put(ext, type);
463: }
464:
465: public String getMimeType(String file) {
466: String type = null;
467: int lastDot = file.lastIndexOf('.');
468: int lastSep = file.lastIndexOf('/');
469: if (lastDot != -1 && (lastSep == -1 || lastDot > lastSep)) {
470: String ext = file.substring(lastDot + 1);
471: type = (String) mimeTypes.get(ext);
472: if (type == null) {
473: type = server.getMimeTypeForExt(ext);
474: }
475: }
476: return type;
477: }
478:
479: /**
480: * If a URI refers to a directory, use the welcome file list to find
481: * a resource in the directory to serve. Return the name of the file
482: * if found, otherwise, simply return the original directory name.
483: */
484: String resolveDirectory(String path) {
485: String ret = path;
486: String sav = path + "/";
487: if (sav.endsWith("//")) {
488: sav = sav.substring(0, sav.length() - 1);
489: }
490: Entry e = docRoot.getEntry(path);
491: if (e != null) {
492: if (e.isDirectory()) {
493: e = null;
494: List w = getWelcomeFiles();
495: for (int i = 0; e == null && i < w.size(); i++) {
496: e = docRoot.getEntry(path = sav + w.get(i));
497: }
498: }
499: }
500: if (e != null)
501: ret = path;
502: return ret;
503: }
504:
505: public String getRealPath(String path) {
506: String rpath = resolveDirectory(path);
507: String ret = docRoot.getRealPath(rpath);
508: return ret;
509: }
510:
511: public URL getResource(String path) throws MalformedURLException {
512: return docRoot.getURL(path);
513: }
514:
515: public InputStream getResourceAsStream(String path) {
516: try {
517: URL url = getResource(path);
518: if (url != null) {
519: return url.openStream();
520: }
521: } catch (Throwable t) {
522: }
523: return null;
524: }
525:
526: /**
527: * Returns the session bound to the specified session ID.
528: *
529: * @param sessionID the ID of a particular session object
530: * @return the session name. Returns null if the session ID does not refer
531: * to a valid session.
532: */
533: public HSession getSession(String sessionId) {
534: synchronized (sessions) {
535: return (HSession) sessions.get(sessionId);
536: }
537: }
538:
539: void removeSession(HSession session) {
540: //#ifdef DEBUG
541: if (Trace.level() > 1) {
542: Debug.println("removeSession(" + session.getId() + ")");
543: }
544: //#endif
545: synchronized (sessions) {
546: sessions.remove(session.getId());
547: }
548: }
549:
550: /**
551: * Create a new session
552: */
553: public HSession createSession() {
554: String sessionId = server.makeSessionId();
555: //#ifdef DEBUG
556: if (Trace.level() > 1) {
557: Debug.println("++++++ createSession(): " + sessionId);
558: }
559: //#endif
560: HSession session = new HSession(this , sessionId, sessionTimeout);
561: synchronized (sessions) {
562: sessions.put(sessionId, session);
563: }
564: return session;
565: }
566:
567: /**
568: * Reap any expired sessions (sessions not active during the last
569: * interval)
570: */
571: public void expireSessions() {
572: synchronized (sessions) {
573: Enumeration e = sessions.keys();
574: long now = System.currentTimeMillis();
575: while (e.hasMoreElements()) {
576: String sessionId = (String) e.nextElement();
577: HSession session = (HSession) getSession(sessionId);
578: if (now - session.getLastAccessedTime() > (session
579: .getMaxInactiveInterval() * 1000)) {
580: try {
581: session.invalidate();
582: } catch (Throwable t) {
583: Debug.print(t);
584: }
585: }
586: }
587: }
588: }
589:
590: /**
591: * Writes a message to the server's log file.
592: *
593: * @param msg the message
594: */
595: public void log(String msg) {
596: Debug.println(msg);
597: }
598:
599: /**
600: * Write the stack backtrace for the exception and the specified message
601: * to the server's log file.
602: *
603: * @param e the exception
604: * @param msg the message
605: * @deprecated
606: */
607: public void log(Exception e, String msg) {
608: Debug.println(msg);
609: Debug.print(e);
610: }
611:
612: /**
613: * Write the stack backtrace for the exception and the specified message
614: * to the server's log file.
615: *
616: * @param e the exception
617: * @param msg the message
618: */
619: public void log(String msg, Throwable e) {
620: Debug.println(msg);
621: Debug.print(e);
622: }
623:
624: /**
625: * @deprecated in 2.2
626: */
627: public Servlet getServlet(String name) {
628: return null;
629: }
630:
631: /**
632: * @deprecated in 2.2
633: */
634: public Enumeration getServletNames() {
635: return new Vector().elements();
636: }
637:
638: /**
639: * @deprecated in 2.2
640: */
641: public Enumeration getServlets() {
642: return new Vector().elements();
643: }
644:
645: public String getContextPath() {
646: return contextPath;
647: }
648:
649: /**
650: * Return the classpath for the context's libraries and classes
651: * under WEB-INF.
652: */
653: public String getContextClassPath() {
654: return classLoader.getClassPath();
655: }
656:
657: final ClassLoader getClassLoader() {
658: return classLoader;
659: }
660:
661: final void loadInitialServlets() throws ServletException {
662: ServletException ex = null;
663:
664: ArrayList l = new ArrayList();
665: l.addAll(servlets.values());
666: Collections.sort(l, new Comparator() {
667: public int compare(Object a, Object b) {
668: WebServlet wa = (WebServlet) a;
669: WebServlet wb = (WebServlet) b;
670: if (wa.getLoadOnStartup() < wb.getLoadOnStartup())
671: return -1;
672: if (wa.getLoadOnStartup() > wb.getLoadOnStartup())
673: return 1;
674: return 0;
675: }
676: });
677: for (int i = 0; i < l.size(); i++) {
678: WebServlet w = (WebServlet) l.get(i);
679: if (w.getLoadOnStartup() >= 0) {
680: try {
681: w.init();
682: } catch (ServletException e) {
683: ex = e;
684: Debug.print(e);
685: }
686: }
687: }
688:
689: if (ex != null)
690: throw ex;
691: }
692:
693: public WebServer getWebServer() {
694: return server;
695: }
696:
697: public String getRootPath() {
698: return docRoot.getRootPath();
699: }
700:
701: public boolean isDirectory(String d) {
702: try {
703: return docRoot.getEntry(d).isDirectory();
704: } catch (Throwable t) {
705: return false;
706: }
707: }
708:
709: //#ifdef DEBUG
710: public String toString() {
711: return "[" + contextPath + "]";
712: }
713: //#endif
714: }
|