KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > servlets > responders > Responder


1 /******************************************************************************
2  * Responder.java
3  * ****************************************************************************/

4
5 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
6 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
7 * Use is subject to license terms. *
8 * J_LZ_COPYRIGHT_END *********************************************************/

9
10 package org.openlaszlo.servlets.responders;
11
12 import java.io.*;
13 import java.net.InetAddress JavaDoc;
14 import java.net.UnknownHostException JavaDoc;
15 import java.util.Date JavaDoc;
16 import java.util.Properties JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import javax.servlet.ServletConfig JavaDoc;
19 import javax.servlet.ServletContext JavaDoc;
20 import javax.servlet.ServletException JavaDoc;
21 import javax.servlet.ServletOutputStream JavaDoc;
22 import javax.servlet.http.HttpServletRequest JavaDoc;
23 import javax.servlet.http.HttpServletResponse JavaDoc;
24 import javax.servlet.http.HttpUtils JavaDoc;
25
26 import org.openlaszlo.compiler.Canvas;
27 import org.openlaszlo.compiler.CompilationError;
28 import org.openlaszlo.media.MimeType;
29 import org.openlaszlo.utils.ChainedException;
30 import org.openlaszlo.utils.FileUtils;
31 import org.openlaszlo.utils.LZHttpUtils;
32 import org.openlaszlo.utils.SWFUtils;
33 import org.openlaszlo.server.LPS;
34 import org.openlaszlo.xml.internal.DataCompiler;
35 import org.openlaszlo.xml.internal.XMLUtils;
36 import org.openlaszlo.servlets.LoadCount;
37 import org.apache.log4j.Logger;
38
39 public abstract class Responder
40 {
41     public static final int MIME_TYPE_SWF = 0;
42     public static final int MIME_TYPE_HTML = 1;
43     public static final int MIME_TYPE_XML = 2;
44
45     public static final String JavaDoc LZCOOKIE = "lzc";
46
47     /** Checks to see if request should be allowed. */
48     private boolean mAllowRequest = true;
49
50     /** Default request authorization string. */
51     protected String JavaDoc mAllowRequestDefaultProperty = "true";
52
53     // Class properties.
54
protected static ServletContext JavaDoc mContext = null;
55
56     private static int mErrorSWFCount = 0;
57     private static Object JavaDoc mErrorSWFCountLock = new Object JavaDoc();
58     private static boolean mIsInitialized = false;
59     private static boolean mEmitErrorHeader = false;
60     private static boolean mUseBogusErrorCode = false;
61     private static Logger mLogger = Logger.getLogger(Responder.class);
62
63     protected int mSWFVersionNum = -1;
64
65     // Special logger for exceptions
66
private static Logger mExceptionStackTraceLogger =
67         Logger.getLogger("org.openlaszlo.exceptions");
68
69     //------------------------------------------------------------
70
// For statistics
71
//------------------------------------------------------------
72
public static Date JavaDoc mSTAT_startDate = new Date JavaDoc();
73
74     public static LoadCount mSTAT_otherLoadCount = new LoadCount(10);
75     public static LoadCount mSTAT_allLoadCount = new LoadCount(10);
76     public static LoadCount mSTAT_compileLoadCount = new LoadCount(10);
77     public static LoadCount mSTAT_mediaLoadCount = new LoadCount(10);
78     public static LoadCount mSTAT_dataLoadCount = new LoadCount(10);
79
80     // FIXME: [2003-25-07] bloch for pkang - this should be rearchitected
81
public static Class JavaDoc mSTAT_adminClass;
82     public static Class JavaDoc mSTAT_connectClass;
83     public static Class JavaDoc mSTAT_compileClass;
84     public static Class JavaDoc mSTAT_mediaClass;
85     public static Class JavaDoc mSTAT_dataClass;
86
87     public static Class JavaDoc mCompilerClass;
88
89     protected static boolean mCollectStat = true;
90
91     /** This actually implements the responder. */
92     protected abstract void respondImpl(HttpServletRequest JavaDoc req,
93                                         HttpServletResponse JavaDoc res)
94         throws IOException;
95
96     /**
97      * This is the mime-type of the responder. At the moment, this only gets
98      * used by the base class to determine how to respond with errors and
99      * exceptions.
100      * @return integer indicating mime-type. See MIME_TYPE properties in this
101      * class.
102      */

103     public abstract int getMimeType();
104
105
106     /** This needs to get called after the instantiation of the class object. */
107     public synchronized void init(String JavaDoc reqName, ServletConfig JavaDoc config,
108                                   Properties JavaDoc prop)
109         throws ServletException JavaDoc, IOException
110     {
111         mAllowRequest =
112             prop.getProperty("allowRequest" + reqName.toUpperCase(),
113                              mAllowRequestDefaultProperty).intern() == "true";
114
115         if (! mIsInitialized) {
116
117             mContext = config.getServletContext();
118
119             try {
120
121                 // Some of these class objects are used by subclasses.
122
mSTAT_adminClass =
123                     Class.forName("org.openlaszlo.servlets.responders.ResponderAdmin");
124                 mSTAT_connectClass =
125                     Class.forName("org.openlaszlo.servlets.responders.ResponderCONNECT");
126                 mSTAT_compileClass =
127                     Class.forName("org.openlaszlo.servlets.responders.ResponderCompile");
128                 mSTAT_mediaClass =
129                     Class.forName("org.openlaszlo.servlets.responders.ResponderMEDIA");
130                 mSTAT_dataClass =
131                     Class.forName("org.openlaszlo.servlets.responders.ResponderDATA");
132
133                 mCompilerClass =
134                     Class.forName("org.openlaszlo.servlets.responders.ResponderCompile");
135
136             } catch (ClassNotFoundException JavaDoc e) {
137                 throw new ServletException JavaDoc(e.getMessage());
138             }
139
140             mEmitErrorHeader =
141                 prop.getProperty("emitErrorHeader", "false").intern() == "true";
142             mUseBogusErrorCode =
143                 prop.getProperty("useBogusErrorCode", "false").intern() == "true";
144
145             mCollectStat =
146                 prop.getProperty("collectStat", "true").intern() == "true";
147
148             mIsInitialized = true;
149         }
150     }
151
152     /**
153      * @return a file handle for the given directory string
154      * @throws IOException if there is a problem with the directory
155      */

156     protected File checkDirectory(String JavaDoc cacheDir) throws IOException {
157
158         File dir = new File(cacheDir);
159         try {
160             dir.mkdirs();
161         } catch (SecurityException JavaDoc e) { }
162         try {
163             dir.mkdir();
164         } catch (SecurityException JavaDoc e) { }
165         if ( !dir.isDirectory() ) {
166             throw new IOException(cacheDir + " is not a directory.");
167         }
168         try {
169             if ( ! dir.canRead() ) {
170                 throw new IOException("can't read " + cacheDir);
171             }
172         } catch (SecurityException JavaDoc e) {
173             throw new IOException("can't read " + cacheDir);
174         }
175         try {
176             if ( ! dir.canWrite() ) {
177                 mLogger.warn(cacheDir + " is not writable.");
178             }
179         } catch (SecurityException JavaDoc e) {
180             // This is info because it's likely configged to be that way.
181
mLogger.info(cacheDir + " is not writable.");
182         }
183         return dir;
184     }
185
186     public void respond(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc res)
187     {
188         try {
189
190             if ( ! mAllowRequest ) {
191                 String JavaDoc lzt = req.getParameter("lzt");
192                 String JavaDoc msg = "Forbidden request type: " + lzt;
193                 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
194                 mLogger.info(msg);
195                 return;
196             }
197
198             // Don't include admin and connection reqs in stats.
199
if ( mCollectStat &&
200                  ! mSTAT_adminClass.isInstance(this) &&
201                  ! mSTAT_connectClass.isInstance(this)) {
202
203                 LoadCount lc = mSTAT_otherLoadCount;
204                 if (mSTAT_compileClass.isInstance(this))
205                     lc = mSTAT_compileLoadCount;
206                 else if (mSTAT_mediaClass.isInstance(this))
207                     lc = mSTAT_mediaLoadCount;
208                 else if (mSTAT_dataClass.isInstance(this))
209                     lc = mSTAT_dataLoadCount;
210
211                 long t0 = new Date JavaDoc().getTime();
212                 mSTAT_allLoadCount.increment();
213                 lc.increment();
214
215                 try {
216                     mSWFVersionNum = LPS.getSWFVersionNum(req);
217                     respondImpl(req, res);
218                 } finally {
219                     long t1 = new Date JavaDoc().getTime();
220                     int d = (int) (t1 - t0);
221                     mSTAT_allLoadCount.decrement( d );
222                     lc.decrement( d );
223                 }
224
225             } else {
226
227                 respondImpl(req, res);
228
229             }
230
231         } catch (CompilationError e) {
232             respondWithError(res, e.getMessage(), 0);
233         } catch (IOException e) {
234             respondWithException(res, e);
235         } catch (Exception JavaDoc e) {
236             respondWithException(res, e);
237         }
238     }
239
240     protected void respondWithError(HttpServletResponse JavaDoc res, String JavaDoc m, int status)
241     {
242         switch (getMimeType()) {
243
244             case MIME_TYPE_SWF :
245                 respondWithErrorSWF(res, m);
246                 break;
247             case MIME_TYPE_HTML :
248                 respondWithErrorHTML(res, m);
249                 break;
250             case MIME_TYPE_XML :
251                 respondWithErrorXML(res, xmlErrorMsg(status, m));
252                 break;
253             default:
254                 throw new ChainedException("Responder mime type unknown");
255         }
256     }
257
258     public void respondWithMessage(HttpServletResponse JavaDoc res, String JavaDoc msg)
259         throws IOException {
260         String JavaDoc surl;
261
262         switch (getMimeType()) {
263             case MIME_TYPE_SWF :
264                 respondWithMessageSWF(res, msg);
265                 break;
266             case MIME_TYPE_HTML :
267                 respondWithErrorHTML(res, msg);
268                 break;
269             case MIME_TYPE_XML :
270                 respondWithErrorXML(res, msg);
271                 break;
272             default:
273                 throw new ChainedException("Responder mime type unknown");
274         }
275     }
276
277
278     /**
279      * Send a SWF response indicating the error.
280      */

281     protected void respondWithErrorSWF(HttpServletResponse JavaDoc res, String JavaDoc s)
282     {
283         mLogger.error("Responding with error SWF: " + s);
284         ServletOutputStream JavaDoc out = null;
285         InputStream in = null;
286         try {
287             res.setContentType(MimeType.SWF);
288             if (mEmitErrorHeader) {
289                 // Make sure not to put newlines in the header
290
res.setHeader("X-LPS", s.replace('\n', '_'));
291             }
292             if (mUseBogusErrorCode)
293                 res.setStatus(700);
294
295             synchronized (mErrorSWFCountLock) {
296                 mErrorSWFCount++;
297             }
298
299             // Unknown.
300
// res.setContentLength(output.length);
301

302             out = res.getOutputStream();
303             String JavaDoc buf = xmlErrorMsg(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, s);
304             in = DataCompiler.compile(buf.toString(), mSWFVersionNum);
305             FileUtils.sendToStream(in, out);
306         } catch (FileUtils.StreamWritingException e) {
307             mLogger.warn("StreamWritingException while sending error: " + e.getMessage());
308         } catch (Exception JavaDoc e) {
309             mLogger.warn("Exception while sending error: " + e.getMessage());
310             mExceptionStackTraceLogger.error("exception", e);
311         } finally {
312             FileUtils.close(in);
313             FileUtils.close(out);
314         }
315     }
316
317     /**
318      * Send a SWF response indicating the error.
319      */

320     public static void respondWithMessageSWF(HttpServletResponse JavaDoc res, String JavaDoc s)
321     {
322         if (mUseBogusErrorCode)
323             res.setStatus(700);
324
325         synchronized (mErrorSWFCountLock) {
326            mErrorSWFCount++;
327         }
328
329         ServletOutputStream JavaDoc out = null;
330         InputStream in = null;
331         try {
332             out = res.getOutputStream();
333             in = SWFUtils.getErrorMessageSWF(s);
334             if (in != null) {
335                 res.setContentType(MimeType.SWF);
336                 FileUtils.sendToStream(in, out);
337             }
338         } catch (FileUtils.StreamWritingException e) {
339             mLogger.warn("StreamWritingException while sending message: " + e.getMessage());
340         } catch (Exception JavaDoc e) {
341             mLogger.warn("Exception while sending message: " + e.getMessage());
342             mExceptionStackTraceLogger.error("exception", e);
343         } finally {
344             FileUtils.close(in);
345             FileUtils.close(out);
346         }
347     }
348
349
350     /**
351      * Send a SWF response indicating the exception
352      */

353     protected void respondWithExceptionSWF(HttpServletResponse JavaDoc res, Throwable JavaDoc e)
354     {
355         mExceptionStackTraceLogger.error(e.getMessage(), e);
356         StringWriter s = new StringWriter();
357         PrintWriter p = new PrintWriter(s);
358         e.printStackTrace(p);
359         respondWithErrorSWF(res, e.getMessage() + " : Exception stack: " + s.toString());
360     }
361
362
363     /**
364      * Send an HTML response indicating the error.
365      */

366     public static void respondWithErrorHTML(HttpServletResponse JavaDoc res, String JavaDoc s)
367     {
368         mLogger.info("Responding with error (text/html): " + s);
369         ServletOutputStream JavaDoc out = null;
370         try {
371             res.setContentType ("text/html");
372             out = res.getOutputStream();
373             writeHeader(out, null);
374             out.print("<pre>");
375             out.println("Error: " + XMLUtils.escapeXmlForSWFHTML(s));
376             out.println("</pre>");
377             writeFooter(out);
378         } catch (Exception JavaDoc e) {
379             mLogger.warn("Exception while sending error HTML: " + e.getMessage());
380             mExceptionStackTraceLogger.error("exception", e);
381         } finally {
382             if (out != null) {
383                 try {
384                     out.close();
385                 } catch (Exception JavaDoc e) {
386                 }
387             }
388         }
389     }
390
391     /**
392      * Send an XML response indicating the error.
393      */

394     protected void respondWithErrorXML(HttpServletResponse JavaDoc res, String JavaDoc s)
395     {
396         mLogger.info("Responding with error (text/xml): " + s);
397         ServletOutputStream JavaDoc out = null;
398         try {
399             res.setContentType ("text/xml");
400             out = res.getOutputStream();
401             out.println("<lps-error>" + XMLUtils.escapeXml(s) + "</lps-error>");
402         } catch (Exception JavaDoc e) {
403             mLogger.warn("Exception while sending error XML: " + e.getMessage());
404             mExceptionStackTraceLogger.error("exception", e);
405         } finally {
406             FileUtils.close(out);
407         }
408     }
409
410
411     /**
412      * Send an XML response.
413      */

414     protected void respondWithXML(HttpServletResponse JavaDoc res, String JavaDoc xml)
415         throws IOException
416     {
417         ServletOutputStream JavaDoc out = null;
418         try {
419             res.setContentType ("text/xml");
420             out = res.getOutputStream();
421             out.println("<lps>");
422             out.println(xml);
423             out.println("</lps>");
424         } catch (Exception JavaDoc e) {
425             mLogger.warn("Exception while sending XML: " + e.getMessage());
426             mExceptionStackTraceLogger.error("exception", e);
427         } finally {
428             FileUtils.close(out);
429         }
430     }
431
432
433     /**
434      * Send a SWF response indicating the exception.
435      */

436     protected void respondWithException(HttpServletResponse JavaDoc res, Exception JavaDoc e)
437     {
438         String JavaDoc m = e.getMessage();
439         StringWriter s = new StringWriter();
440         PrintWriter p = new PrintWriter(s);
441         e.printStackTrace(p);
442         if (m == null) {
443             m = s.toString();
444         } else {
445             m += s.toString();
446         }
447         
448         respondWithError(res, m,
449                          HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
450     }
451
452
453     /**
454      * Sends a successful SWF status response.
455      *
456      * @param res client's servlet response
457      * @param status status code (should be SC_OK)
458      * @param mesg status message
459      * @param serial serial number of request to echo back
460      */

461     protected void respondWithStatusSWF(HttpServletResponse JavaDoc res, int status,
462                                         String JavaDoc mesg, int serial)
463     {
464         respondWithStatusSWF(res, status, mesg, null, serial);
465     }
466
467
468     /**
469      * Sends a successful SWF status response w/arbitrary xml.
470      *
471      * @param res client's servlet response
472      * @param status status code (should be SC_OK)
473      * @param mesg status message
474      * @param xmlBody arbitrary xml
475      * @param serial serial number of request to echo back
476      */

477     protected void respondWithStatusSWF(HttpServletResponse JavaDoc res, int status,
478                                         String JavaDoc mesg, String JavaDoc xmlBody, int serial)
479     {
480         res.setContentType(MimeType.SWF);
481
482         String JavaDoc _mesg = XMLUtils.escapeXml(mesg);
483
484         // Successful response codes end up as an attribute of resultset. We
485
// should standardize where the response code ends up.
486
String JavaDoc xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
487             + "<!DOCTYPE laszlo-data>"
488             + "<resultset s=\"" + serial + "\">"
489             + "<success code=\"" + status + "\" msg=\"" + _mesg + "\" />"
490             + ( xmlBody!=null ? xmlBody : "" )
491             + "</resultset>";
492
493         mLogger.debug("respondWithStatusSWF: " + xml);
494
495         ServletOutputStream JavaDoc sos = null;
496         try {
497             sos = res.getOutputStream();
498             InputStream swfbytes = DataCompiler.compile(xml, mSWFVersionNum);
499             FileUtils.sendToStream(swfbytes, sos);
500         } catch (FileUtils.StreamWritingException e) {
501             mLogger.warn("StreamWritingException while sending status: " + e.getMessage());
502         } catch (Exception JavaDoc e) {
503             mLogger.warn("Exception while sending status: " + e.getMessage());
504             mExceptionStackTraceLogger.error("exception", e);
505         } finally {
506             FileUtils.close(sos);
507         }
508     }
509
510     /**
511      * Respond with an "over limit" message
512      */

513     void respondWithOverLimitMessage(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc res)
514         throws IOException {
515         StringBuffer JavaDoc url = HttpUtils.getRequestURL(req);
516         String JavaDoc msg = LPS.getProperty("messages.over-limit",
517             "The Laszlo Presentation Server that is responsible for serving " +
518              url.toString() + " is over its license limit. The site administrator has been notified.");
519          respondWithMessage(res, msg);
520      }
521
522     /**
523      * Creates an XML error message.
524      *
525      * @param status integer status. Using HTTP status codes, for now, but
526      * Laszlo status codes would be better.
527      * @param msg message to display.
528      */

529     protected String JavaDoc xmlErrorMsg(int status, String JavaDoc msg)
530     {
531         return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
532             "<!DOCTYPE laszlo-data>" +
533             "<resultset><error status=\"" + status + "\" msg=\"" +
534             XMLUtils.escapeXml(msg) +
535             "\"/></resultset>";
536     }
537
538     /**
539      * Writes the html header tags
540      * @param out A PrintWriter
541      */

542     protected static void writeHeader(ServletOutputStream JavaDoc out, Canvas c)
543         throws IOException
544     {
545         String JavaDoc bgc = "";
546         String JavaDoc title = "";
547         if (c != null) {
548             bgc = "bgcolor=\"" + c.getBGColorString() + "\"";
549             title = c.getTitle();
550         }
551
552         // Add in title and link
553
out.println("<html><head><title>" + title + "</title>");
554
555         String JavaDoc ico = LPS.getProperty("shortcut.icon", "http://www.laszlosystems.com/images/laszlo.ico");
556         out.println("<link rel=\"SHORTCUT ICON\" HREF=\"" + ico +
557                     "\"></head>\n");
558         out.println("<body " + bgc +
559         " marginwidth=\"0\" marginheight=\"0\" topmargin=\"0\" leftmargin=\"0\">");
560     }
561     
562     /**
563      * Writes the html footer tags
564      */

565     protected static void writeFooter(ServletOutputStream JavaDoc out)
566         throws IOException
567     {
568         out.println ("</body></html>");
569     }
570
571     protected static int getErrorSWFCount()
572     {
573         synchronized (mErrorSWFCountLock) {
574             return mErrorSWFCount;
575         }
576     }
577
578     protected static void clearErrorSWFCount()
579     {
580         synchronized (mErrorSWFCountLock) {
581             mErrorSWFCount = 0;
582         }
583     }
584 }
585
Popular Tags