001: /******************************************************************************
002: * LZServlet.java
003: * ****************************************************************************/package org.openlaszlo.servlets;
004:
005: import java.io.*;
006: import java.net.*;
007: import java.util.*;
008: import javax.servlet.*;
009: import javax.servlet.http.*;
010: import javax.servlet.ServletConfig.*;
011: import java.nio.charset.Charset;
012:
013: import org.openlaszlo.servlets.responders.*;
014: import org.openlaszlo.server.*;
015: import org.openlaszlo.utils.ChainedException;
016: import org.openlaszlo.utils.LZUtils;
017: import org.openlaszlo.utils.LZHttpUtils;
018: import org.openlaszlo.utils.MathUtils;
019:
020: import org.apache.log4j.*;
021:
022: /**
023: * LZServlet is the main entry point for the Laszlo Presentation Server
024: *
025: * The LZServlet will respond to GET (and POST) requests
026: * per the spec at
027: * <a href="../../../../lps-servlet-spec.html">
028: * $LPS_HOME/server/doc/lps-servlet-spec.html</a>
029: *
030: * Each request type (lzt query string variable) has
031: * a Responder object that is used to construct a responder.
032: *
033: */
034: public class LZServlet extends HttpServlet {
035: private static Logger mLogger = null;
036: private File mLogFile = null;
037:
038: private final static String DEFAULT_REQUEST_TYPE = "defaultRequestType";
039: private static String mDefaultRequestType = "html";
040:
041: private final static String CHARACTER_ENCODING = "characterEncoding";
042: private static String mCharacterEncoding = "UTF-8";
043:
044: /** Responder object map */
045: public static HashMap mResponderMap = new HashMap();
046:
047: /** Request counter */
048: private int mRequestCounter = 1;
049:
050: /** LPS properties */
051: private Properties mProperties = new Properties();
052:
053: /**
054: * @return true if init succeeded
055: * @param config ServletConfig object
056: */
057: public boolean initLPS(HttpServletRequest req,
058: HttpServletResponse res) {
059:
060: ServletConfig config = getServletConfig();
061: ServletContext ctxt = config.getServletContext();
062: String webappName = getServletContextName(ctxt);
063:
064: // Sanity check the servlet context version
065: if ((ctxt.getMajorVersion() < 2)
066: || (ctxt.getMajorVersion() == 2 && ctxt
067: .getMinorVersion() < 3)) {
068: respondWithInitError(req, res,
069: /* (non-Javadoc)
070: * @i18n.test
071: * @org-mes="Must be at least " + "Version 2.3 Servlet Container!"
072: */
073: org.openlaszlo.i18n.LaszloMessages.getMessage(
074: LZServlet.class.getName(), "051018-80"));
075: return false;
076: }
077:
078: // Check for LPS_HOME
079: String lhome = getInitParameter("LPS_HOME");
080: if (lhome == null) {
081: // Default to webapp
082: lhome = LZHttpUtils.getRealPath(ctxt, "/");
083:
084: // FIXME: [2003-04-28 bloch] remove this code when
085: // we fix bug 540. Safety check for now
086: if (lhome == null) {
087: respondWithInitError(req, res,
088: /* (non-Javadoc)
089: * @i18n.test
090: * @org-mes="LPS requires a servlet container" + " that can implements ServletContent.getRealPath()"
091: */
092: org.openlaszlo.i18n.LaszloMessages.getMessage(
093: LZServlet.class.getName(), "051018-100"));
094: return false;
095: }
096: }
097:
098: log("LPS_HOME is " + lhome);
099: LPS.setHome(lhome);
100:
101: // Configure logging
102: String lpsConfigDir = ConfigDir.get(lhome);
103: log(
104: /* (non-Javadoc)
105: * @i18n.test
106: * @org-mes="LPS config directory is: " + p[0]
107: */
108: org.openlaszlo.i18n.LaszloMessages
109: .getMessage(LZServlet.class.getName(), "051018-117",
110: new Object[] { lpsConfigDir }));
111:
112: try {
113: ResponderLOGCONFIG.configure(this );
114: } catch (Exception e) {
115: respondWithInitException(req, res, e);
116: return false;
117: }
118:
119: mLogger = Logger.getLogger(LZServlet.class);
120:
121: // TODO: 2004-09-08 bloch merge with code in LPS.getInfo()
122: // Log some basic information
123: {
124: logInitInfo("------------------------------------");
125: logInitInfo(
126: /* (non-Javadoc)
127: * @i18n.test
128: * @org-mes="Laszlo Presentation Server, " + p[0] + ", initialized"
129: */
130: org.openlaszlo.i18n.LaszloMessages.getMessage(
131: LZServlet.class.getName(), "051018-139",
132: new Object[] { LPS.getVersion() }));
133: logInitInfo(
134: /* (non-Javadoc)
135: * @i18n.test
136: * @org-mes="Running in context:" + p[0]
137: */
138: org.openlaszlo.i18n.LaszloMessages.getMessage(
139: LZServlet.class.getName(), "051018-147",
140: new Object[] { ctxt.getServerInfo() }));
141: logInitInfo("Build: " + LPS.getBuild());
142: logInitInfo("Built on: " + LPS.getBuildDate());
143: logInitInfo("Running against JRE "
144: + LPS.getSystemPropertyOrUnknowable("java.version"));
145: logInitInfo("Running with Java CLASSPATH: "
146: + LPS
147: .getSystemPropertyOrUnknowable("java.class.path"));
148: logInitInfo("Running on "
149: + LPS.getSystemPropertyOrUnknowable("os.name")
150: + " "
151: + LPS.getSystemPropertyOrUnknowable("os.version"));
152: logInitInfo("Running as user "
153: + LPS.getSystemPropertyOrUnknowable("user.name"));
154: final double MEG = 1024 * 1024;
155: final double mx = Runtime.getRuntime().maxMemory() / (MEG);
156: final double total = Runtime.getRuntime().totalMemory()
157: / (MEG);
158: final double avail = Runtime.getRuntime().freeMemory()
159: / (MEG);
160: logInitInfo(
161: /* (non-Javadoc)
162: * @i18n.test
163: * @org-mes="Max memory: " + p[0] + " MB"
164: */
165: org.openlaszlo.i18n.LaszloMessages.getMessage(
166: LZServlet.class.getName(), "051018-168",
167: new Object[] { MathUtils.formatDouble(mx, 2) }));
168: logInitInfo(
169: /* (non-Javadoc)
170: * @i18n.test
171: * @org-mes="Total memory: " + p[0] + " MB"
172: */
173: org.openlaszlo.i18n.LaszloMessages.getMessage(
174: LZServlet.class.getName(), "051018-176",
175: new Object[] { MathUtils.formatDouble(total, 2) }));
176: logInitInfo(
177: /* (non-Javadoc)
178: * @i18n.test
179: * @org-mes="Available memory: " + p[0] + " MB"
180: */
181: org.openlaszlo.i18n.LaszloMessages.getMessage(
182: LZServlet.class.getName(), "051018-184",
183: new Object[] { MathUtils.formatDouble(avail, 2) }));
184:
185: mLogger.info(
186: /* (non-Javadoc)
187: * @i18n.test
188: * @org-mes="LPS_HOME is: " + p[0]
189: */
190: org.openlaszlo.i18n.LaszloMessages.getMessage(
191: LZServlet.class.getName(), "051018-193",
192: new Object[] { lhome }));
193: }
194:
195: // JRE version detection
196: String jvmVersion = "unknown";
197: try {
198: jvmVersion = LPS
199: .getSystemPropertyOrUnknowable("java.specification.version");
200: int dot1Index = jvmVersion.indexOf('.');
201: if (dot1Index < 0) {
202: dot1Index = jvmVersion.length();
203: }
204: int dot2Index = jvmVersion.indexOf('.', dot1Index + 1);
205: if (dot2Index < 0) {
206: dot2Index = jvmVersion.length();
207: }
208:
209: int jvmMajor = Integer.parseInt(jvmVersion.substring(0,
210: dot1Index));
211: int jvmMinor = Integer.parseInt(jvmVersion.substring(
212: dot1Index + 1, dot2Index));
213: if ((jvmMajor == 1 && jvmMinor < 4) || jvmMajor < 1) {
214: String message =
215: /* (non-Javadoc)
216: * @i18n.test
217: * @org-mes="LPS running against JRE version < 1.4; this is *not* supported!"
218: */
219: org.openlaszlo.i18n.LaszloMessages.getMessage(
220: LZServlet.class.getName(), "051018-219");
221:
222: log(message);
223: mLogger.warn(message);
224: }
225: } catch (Exception e) {
226: mLogger.warn(
227: /* (non-Javadoc)
228: * @i18n.test
229: * @org-mes="Can't parse JRE specification version: " + p[0]
230: */
231: org.openlaszlo.i18n.LaszloMessages.getMessage(
232: LZServlet.class.getName(), "051018-231",
233: new Object[] { jvmVersion }));
234: }
235:
236: // Initialize
237: LPS.initialize();
238:
239: // Debugging
240: if (mLogger.isDebugEnabled()) {
241: Enumeration attrs = ctxt.getAttributeNames();
242: while (attrs.hasMoreElements()) {
243: String a = (String) attrs.nextElement();
244: mLogger.debug("Attribute: " + a + " : "
245: + ctxt.getAttribute(a));
246: }
247: }
248:
249: //------------------------------------------------------------
250: // LPS property values
251: //------------------------------------------------------------
252: mProperties = LPS.getProperties();
253:
254: LPS.setRuntimeDefault(LPS.getRuntimeDefault());
255:
256: // Create responders that create caches (media, data, and compiler)
257: String[] lzt = { "swf", "media", "data" };
258: int i = 0;
259: try {
260: for (; i < lzt.length; i++) {
261: if (getResponder(lzt[i]) == null) {
262: respondWithInitError(req, res,
263: /* (non-Javadoc)
264: * @i18n.test
265: * @org-mes="Initialization error: no request type: " + p[0]
266: */
267: org.openlaszlo.i18n.LaszloMessages.getMessage(
268: LZServlet.class.getName(), "051018-266",
269: new Object[] { lzt[i] }));
270: return false;
271: }
272: }
273: } catch (Throwable e) {
274: mLogger.error("Exception", e);
275: respondWithInitException(req, res, e);
276: return false;
277: }
278:
279: mDefaultRequestType = mProperties.getProperty(
280: DEFAULT_REQUEST_TYPE, mDefaultRequestType);
281: mLogger.info(
282: /* (non-Javadoc)
283: * @i18n.test
284: * @org-mes="Default request type is " + p[0]
285: */
286: org.openlaszlo.i18n.LaszloMessages.getMessage(LZServlet.class
287: .getName(), "051018-284",
288: new Object[] { mDefaultRequestType }));
289:
290: mCharacterEncoding = mProperties.getProperty(
291: CHARACTER_ENCODING, mCharacterEncoding);
292: try {
293: // check for valid character encoding.
294: Charset.forName(mCharacterEncoding);
295: } catch (Throwable e) {
296: mLogger.error("Exception", e);
297: respondWithInitException(req, res, e);
298: return false;
299: }
300: mLogger.info(
301: /* (non-Javadoc)
302: * @i18n.test
303: * @org-mes="Character encoding is " + p[0]
304: */
305: org.openlaszlo.i18n.LaszloMessages.getMessage(LZServlet.class
306: .getName(), "051018-302",
307: new Object[] { mCharacterEncoding }));
308:
309: return true;
310: }
311:
312: /**
313: * Log initialization details (to both servlet log and lps.log)
314: *
315: * @param s String to log
316: */
317: private void logInitInfo(String s) {
318: mLogger.info(s);
319: log(s);
320: }
321:
322: /**
323: * @param req @see HttpServletRequest
324: * @param res @see HttpServletResponse
325: */
326: public void doGet(HttpServletRequest req, HttpServletResponse res)
327: throws IOException, ServletException {
328: // If no encoding is specified, force it. Note that encoding has to be
329: // set before request parameters are accessed.
330: if (req.getCharacterEncoding() == null) {
331: req.setCharacterEncoding(mCharacterEncoding);
332: }
333:
334: // This forces clients to talk to us. We may still return NOT_MODIFIED.
335: // TODO: [bloch 2002-12-17] turn this into an lps.property
336: if (!isMacIE(req)) { // Set only if it's not Mac IE 5.2 (see bug 811)
337: res.setHeader("Expires", "Fri, 05 Oct 2001 00:00:00 GMT");
338: }
339:
340: int requestID;
341:
342: // Set up logger NDC
343: synchronized (this ) {
344: requestID = mRequestCounter;
345: if (requestID == 1) {
346: if (initLPS(req, res) == false) {
347: return;
348: }
349: }
350: mRequestCounter++;
351: }
352:
353: NDC.push(req.getRemoteAddr() + " " + (requestID));
354:
355: try {
356: _doGet(req, res);
357: } finally {
358:
359: mLogger.debug(
360: /* (non-Javadoc)
361: * @i18n.test
362: * @org-mes="Request " + p[0] + " finished"
363: */
364: org.openlaszlo.i18n.LaszloMessages.getMessage(
365: LZServlet.class.getName(), "051018-363",
366: new Object[] { new Integer(requestID) }));
367: NDC.pop();
368: NDC.remove();
369: }
370:
371: }
372:
373: /**
374: * @return lzt String
375: */
376: String getLZT(HttpServletRequest req) {
377: String lzt = req.getParameter("lzt");
378: if (lzt == null || lzt.equals(""))
379: lzt = mDefaultRequestType;
380:
381: return lzt;
382: }
383:
384: /**
385: * @param req @see HttpServletRequest
386: * @param res @see HttpServletResponse
387: */
388: private void _doGet(HttpServletRequest req, HttpServletResponse res)
389: throws IOException, ServletException {
390: ServletContext ctxt = getServletContext();
391: String fileName = LZHttpUtils.getRealPath(ctxt, req);
392:
393: if (mLogger.isDebugEnabled()) {
394: mLogger.debug("Request: servlet path "
395: + req.getServletPath());
396: mLogger.debug("Request: request uri "
397: + req.getRequestURI());
398: mLogger.debug("Request: query string "
399: + req.getQueryString());
400: mLogger.debug("Request: path info " + req.getPathInfo());
401: mLogger.debug("Request: path xlated "
402: + req.getPathTranslated());
403: mLogger
404: .debug("Request: server name "
405: + req.getServerName());
406: mLogger
407: .debug("Request: server port "
408: + req.getServerPort());
409: mLogger.debug("Request: is secure " + req.isSecure());
410: mLogger.debug("Request: charset "
411: + req.getCharacterEncoding());
412:
413: Enumeration headers = req.getHeaderNames();
414: if (headers != null) {
415: while (headers.hasMoreElements()) {
416: String h = (String) headers.nextElement();
417: mLogger.debug(" Header: " + h + " : "
418: + req.getHeader(h));
419: }
420: }
421: }
422:
423: mLogger.info(
424: /* (non-Javadoc)
425: * @i18n.test
426: * @org-mes="Request for " + p[0]
427: */
428: org.openlaszlo.i18n.LaszloMessages.getMessage(LZServlet.class
429: .getName(), "051018-420", new Object[] { fileName }));
430:
431: String lzt = getLZT(req);
432: Responder lzres = getResponder(lzt);
433:
434: if (lzres == null) {
435: mLogger.debug(
436: /* (non-Javadoc)
437: * @i18n.test
438: * @org-mes="No request type: " + p[0]
439: */
440: org.openlaszlo.i18n.LaszloMessages.getMessage(
441: LZServlet.class.getName(), "051018-433",
442: new Object[] { lzt }));
443: res.sendError(HttpServletResponse.SC_NOT_FOUND,
444: "No request type: " + lzt);
445: return;
446: }
447:
448: mLogger.debug("responding via: " + lzres.getClass().getName());
449: lzres.respond(req, res);
450: }
451:
452: /**
453: * Get a Responder object for the given lzt string
454: */
455: private synchronized Responder getResponder(String lzt)
456: throws ServletException {
457: Responder lzres = (Responder) mResponderMap.get(lzt);
458: String className = "org.openlaszlo.servlets.responders.Responder";
459:
460: if (lzres == null) {
461: Class c = null;
462: try {
463: className += lzt.toUpperCase();
464: c = Class.forName(className);
465: lzres = (Responder) c.newInstance();
466: lzres.init(lzt, getServletConfig(), mProperties);
467: mResponderMap.put(lzt, lzres);
468: } catch (InstantiationException e) {
469: mLogger.error("InstantiationException", e);
470: throw new ServletException("InstantiationException: "
471: + e.getMessage());
472: } catch (IllegalAccessException e) {
473: mLogger.error("IllegalAccessException", e);
474: throw new ServletException("IllegalAccessException: "
475: + e.getMessage());
476: } catch (ClassNotFoundException e) {
477: try {
478: lzres = TemplateResponder.getResponder(lzt);
479: } catch (Throwable t) {
480: mLogger
481: .error(
482: /* (non-Javadoc)
483: * @i18n.test
484: * @org-mes="Exception getting template responder"
485: */
486: org.openlaszlo.i18n.LaszloMessages
487: .getMessage(LZServlet.class
488: .getName(), "051018-476"),
489: t);
490: }
491: // The case where this returns null is handled by the caller.
492: if (lzres == null) {
493: mLogger.error(
494: /* (non-Javadoc)
495: * @i18n.test
496: * @org-mes="no matching template responder " + "or class named " + p[0]
497: */
498: org.openlaszlo.i18n.LaszloMessages.getMessage(
499: LZServlet.class.getName(), "051018-487",
500: new Object[] { className }));
501: }
502: } catch (Throwable e) {
503: // TODO: [pkang 2003-05-01] if a user-defined handler exists,
504: // use it.
505: mLogger.error("Throwable: ", e);
506: log("throwable: " + e.getMessage());
507: throw new ServletException(e);
508: }
509: }
510: return lzres;
511: }
512:
513: /**
514: * Check if it's Mac IE.
515: */
516: private boolean isMacIE(HttpServletRequest req) {
517: // TODO: [pkang 2003-01-30] this was only tested on IE 5.2. We need to
518: // test if this works for IE 5 and 5.1.
519: String ua = req.getHeader("User-Agent");
520: return (ua != null && ua.indexOf("MSIE") != -1 && ua
521: .indexOf("Mac_PowerPC") != -1);
522: }
523:
524: /**
525: * @param req @see HttpServletRequest
526: * @param res @see HttpServletResponse
527: */
528: public void doPost(HttpServletRequest req, HttpServletResponse res)
529: throws IOException, ServletException {
530:
531: doGet(req, res);
532:
533: }
534:
535: /**
536: * Send an error message response based on the request type
537: *
538: * @param req
539: * @param res
540: * @param message
541: */
542: private void respondWithInitError(HttpServletRequest req,
543: HttpServletResponse res, String message) {
544:
545: log(message);
546: try {
547: String lzt = null;
548: Responder lzres = null;
549: try {
550: lzt = getLZT(req);
551: lzres = getResponder(lzt);
552: } catch (Exception e) {
553: mLogger.error(
554: /* (non-Javadoc)
555: * @i18n.test
556: * @org-mes="Exception trying to respond with initialization error"
557: */
558: org.openlaszlo.i18n.LaszloMessages.getMessage(
559: LZServlet.class.getName(), "051018-550"), e);
560: }
561: if (lzres != null) {
562: lzres.respondWithMessage(res, message);
563: } else {
564: if ("swf".equalsIgnoreCase(lzt)) {
565: Responder.respondWithMessageSWF(res,
566: "LPS initialization error: " + message);
567: } else {
568: Responder.respondWithErrorHTML(res,
569: "LPS initialization error: " + message);
570: }
571: }
572: } catch (Throwable e) {
573: // Nothing we can do now :-(
574: }
575: }
576:
577: /**
578: * Send an error message response based on the request type
579: *
580: * @param req
581: * @param res
582: * @param e
583: */
584: private void respondWithInitException(HttpServletRequest req,
585: HttpServletResponse res, Throwable e) {
586:
587: StringWriter s = new StringWriter();
588: PrintWriter p = new PrintWriter(s);
589: e.printStackTrace(p);
590: respondWithInitError(req, res, s.toString());
591: }
592:
593: /**
594: * Convenience routine to return webapp name
595: */
596: public static String getServletContextName(ServletContext ctxt) {
597:
598: String webappPath = LZHttpUtils.getRealPath(ctxt, "/");
599: int index = webappPath.lastIndexOf(File.separator);
600: int length = webappPath.length();
601: if (index == length - 1) {
602: index = webappPath.lastIndexOf(File.separator, index - 1);
603: return webappPath.substring(index + 1, length - 1);
604: }
605:
606: return webappPath.substring(index + 1);
607: }
608:
609: /**
610: * Servlet is going away
611: */
612: public void destroy() {
613: ServletContext ctxt = getServletContext();
614: String webappName = getServletContextName(ctxt);
615: mLogger.info(
616: /* (non-Javadoc)
617: * @i18n.test
618: * @org-mes="LPS destroyed in context: " + p[0]
619: */
620: org.openlaszlo.i18n.LaszloMessages.getMessage(LZServlet.class
621: .getName(), "051018-612", new Object[] { webappName }));
622: }
623: }
|