001: /******************************************************************************
002: * Responder.java
003: * ****************************************************************************/package org.openlaszlo.servlets.responders;
004:
005: import java.io.*;
006: import java.net.InetAddress;
007: import java.net.UnknownHostException;
008: import java.util.Date;
009: import java.util.Properties;
010: import java.util.Iterator;
011: import javax.servlet.ServletConfig;
012: import javax.servlet.ServletContext;
013: import javax.servlet.ServletException;
014: import javax.servlet.ServletOutputStream;
015: import javax.servlet.http.HttpServletRequest;
016: import javax.servlet.http.HttpServletResponse;
017: import javax.servlet.http.HttpUtils;
018:
019: import org.openlaszlo.compiler.Canvas;
020: import org.openlaszlo.compiler.CompilationError;
021: import org.openlaszlo.media.MimeType;
022: import org.openlaszlo.utils.ChainedException;
023: import org.openlaszlo.utils.FileUtils;
024: import org.openlaszlo.utils.LZHttpUtils;
025: import org.openlaszlo.utils.SWFUtils;
026: import org.openlaszlo.server.LPS;
027: import org.openlaszlo.xml.internal.DataCompiler;
028: import org.openlaszlo.xml.internal.XMLUtils;
029: import org.openlaszlo.servlets.LoadCount;
030: import org.apache.log4j.Logger;
031:
032: public abstract class Responder {
033: public static final int MIME_TYPE_SWF = 0;
034: public static final int MIME_TYPE_HTML = 1;
035: public static final int MIME_TYPE_XML = 2;
036: public static final int MIME_TYPE_XMLDATA = 3;
037: public static final int MIME_TYPE_SVG = 4;
038:
039: public static final String LZCOOKIE = "lzc";
040:
041: /** Checks to see if request should be allowed. */
042: private boolean mAllowRequest = true;
043:
044: /** Default request authorization string. */
045: protected String mAllowRequestDefaultProperty = "true";
046:
047: // Class properties.
048: protected static ServletContext mContext = null;
049:
050: private static int mErrorSWFCount = 0;
051: private static Object mErrorSWFCountLock = new Object();
052: private static boolean mIsInitialized = false;
053: private static boolean mEmitErrorHeader = false;
054: private static boolean mUseBogusErrorCode = false;
055: private static Logger mLogger = Logger.getLogger(Responder.class);
056:
057: protected int mSWFVersionNum = -1;
058:
059: // Special logger for exceptions
060: private static Logger mExceptionStackTraceLogger = Logger
061: .getLogger("org.openlaszlo.exceptions");
062:
063: //------------------------------------------------------------
064: // For statistics
065: //------------------------------------------------------------
066: public static Date mSTAT_startDate = new Date();
067:
068: public static LoadCount mSTAT_otherLoadCount = new LoadCount(10);
069: public static LoadCount mSTAT_allLoadCount = new LoadCount(10);
070: public static LoadCount mSTAT_compileLoadCount = new LoadCount(10);
071: public static LoadCount mSTAT_mediaLoadCount = new LoadCount(10);
072: public static LoadCount mSTAT_dataLoadCount = new LoadCount(10);
073:
074: // FIXME: [2003-25-07] bloch for pkang - this should be rearchitected
075: public static Class mSTAT_adminClass;
076: public static Class mSTAT_connectClass;
077: public static Class mSTAT_compileClass;
078: public static Class mSTAT_mediaClass;
079: public static Class mSTAT_dataClass;
080:
081: public static Class mCompilerClass;
082:
083: protected static boolean mCollectStat = true;
084:
085: /** This actually implements the responder. */
086: protected abstract void respondImpl(HttpServletRequest req,
087: HttpServletResponse res) throws IOException;
088:
089: /**
090: * This is the mime-type of the responder. At the moment, this only gets
091: * used by the base class to determine how to respond with errors and
092: * exceptions.
093: * @return integer indicating mime-type. See MIME_TYPE properties in this
094: * class.
095: */
096: public int getMimeType() {
097: return MIME_TYPE_HTML;
098: }
099:
100: public int getMimeType(HttpServletRequest req) {
101: return MIME_TYPE_HTML;
102: }
103:
104: /** This needs to get called after the instantiation of the class object. */
105: public synchronized void init(String reqName, ServletConfig config,
106: Properties prop) throws ServletException, IOException {
107: mAllowRequest = prop.getProperty(
108: "allowRequest" + reqName.toUpperCase(),
109: mAllowRequestDefaultProperty).intern() == "true";
110:
111: if (!mIsInitialized) {
112:
113: mContext = config.getServletContext();
114:
115: try {
116:
117: // Some of these class objects are used by subclasses.
118: mSTAT_adminClass = Class
119: .forName("org.openlaszlo.servlets.responders.ResponderAdmin");
120: mSTAT_connectClass = Class
121: .forName("org.openlaszlo.servlets.responders.ResponderCONNECT");
122: mSTAT_compileClass = Class
123: .forName("org.openlaszlo.servlets.responders.ResponderCompile");
124: mSTAT_mediaClass = Class
125: .forName("org.openlaszlo.servlets.responders.ResponderMEDIA");
126: mSTAT_dataClass = Class
127: .forName("org.openlaszlo.servlets.responders.ResponderDATA");
128:
129: mCompilerClass = Class
130: .forName("org.openlaszlo.servlets.responders.ResponderCompile");
131:
132: } catch (ClassNotFoundException e) {
133: throw new ServletException(e.getMessage());
134: }
135:
136: mEmitErrorHeader = prop.getProperty("emitErrorHeader",
137: "false").intern() == "true";
138: mUseBogusErrorCode = prop.getProperty("useBogusErrorCode",
139: "false").intern() == "true";
140:
141: mCollectStat = prop.getProperty("collectStat", "true")
142: .intern() == "true";
143:
144: mIsInitialized = true;
145: }
146: }
147:
148: /**
149: * @return a file handle for the given directory string
150: * @throws IOException if there is a problem with the directory
151: */
152: protected File checkDirectory(String cacheDir) throws IOException {
153:
154: File dir = new File(cacheDir);
155: try {
156: dir.mkdirs();
157: } catch (SecurityException e) {
158: }
159: try {
160: dir.mkdir();
161: } catch (SecurityException e) {
162: }
163: if (!dir.isDirectory()) {
164: throw new IOException(
165: /* (non-Javadoc)
166: * @i18n.test
167: * @org-mes=p[0] + " is not a directory."
168: */
169: org.openlaszlo.i18n.LaszloMessages.getMessage(
170: Responder.class.getName(), "051018-171",
171: new Object[] { cacheDir }));
172: }
173: try {
174: if (!dir.canRead()) {
175: throw new IOException(
176: /* (non-Javadoc)
177: * @i18n.test
178: * @org-mes="can't read " + p[0]
179: */
180: org.openlaszlo.i18n.LaszloMessages.getMessage(
181: Responder.class.getName(), "051018-182",
182: new Object[] { cacheDir }));
183: }
184: } catch (SecurityException e) {
185: throw new IOException(
186: /* (non-Javadoc)
187: * @i18n.test
188: * @org-mes="can't read " + p[0]
189: */
190: org.openlaszlo.i18n.LaszloMessages.getMessage(
191: Responder.class.getName(), "051018-182",
192: new Object[] { cacheDir }));
193: }
194: try {
195: if (!dir.canWrite()) {
196: mLogger.warn(
197: /* (non-Javadoc)
198: * @i18n.test
199: * @org-mes=p[0] + " is not writable."
200: */
201: org.openlaszlo.i18n.LaszloMessages.getMessage(
202: Responder.class.getName(), "051018-203",
203: new Object[] { cacheDir }));
204: }
205: } catch (SecurityException e) {
206: // This is info because it's likely configged to be that way.
207: mLogger.info(
208: /* (non-Javadoc)
209: * @i18n.test
210: * @org-mes=p[0] + " is not writable."
211: */
212: org.openlaszlo.i18n.LaszloMessages.getMessage(
213: Responder.class.getName(), "051018-203",
214: new Object[] { cacheDir }));
215: }
216: return dir;
217: }
218:
219: public void respond(HttpServletRequest req, HttpServletResponse res) {
220: try {
221:
222: if (!mAllowRequest) {
223: String lzt = req.getParameter("lzt");
224: String msg =
225: /* (non-Javadoc)
226: * @i18n.test
227: * @org-mes="Forbidden request type: " + p[0]
228: */
229: org.openlaszlo.i18n.LaszloMessages.getMessage(
230: Responder.class.getName(), "051018-232",
231: new Object[] { lzt });
232: res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
233: mLogger.info(msg);
234: return;
235: }
236:
237: // Don't include admin and connection reqs in stats.
238: if (mCollectStat && !mSTAT_adminClass.isInstance(this )
239: && !mSTAT_connectClass.isInstance(this )) {
240:
241: LoadCount lc = mSTAT_otherLoadCount;
242: if (mSTAT_compileClass.isInstance(this ))
243: lc = mSTAT_compileLoadCount;
244: else if (mSTAT_mediaClass.isInstance(this ))
245: lc = mSTAT_mediaLoadCount;
246: else if (mSTAT_dataClass.isInstance(this ))
247: lc = mSTAT_dataLoadCount;
248:
249: long t0 = new Date().getTime();
250: mSTAT_allLoadCount.increment();
251: lc.increment();
252:
253: try {
254: mSWFVersionNum = LPS.getSWFVersionNum(req);
255: respondImpl(req, res);
256: } finally {
257: long t1 = new Date().getTime();
258: int d = (int) (t1 - t0);
259: mSTAT_allLoadCount.decrement(d);
260: lc.decrement(d);
261: }
262:
263: } else {
264:
265: respondImpl(req, res);
266:
267: }
268:
269: } catch (CompilationError e) {
270: respondWithError(res, e.getMessage(), 0);
271: } catch (IOException e) {
272: respondWithException(res, e);
273: } catch (Exception e) {
274: respondWithException(res, e);
275: }
276: }
277:
278: protected void respondWithError(HttpServletResponse res, String m,
279: int status) {
280: switch (getMimeType()) {
281:
282: case MIME_TYPE_SWF:
283: respondWithErrorSWF(res, m);
284: break;
285: case MIME_TYPE_HTML:
286: respondWithErrorHTML(res, m);
287: break;
288: case MIME_TYPE_XML:
289: case MIME_TYPE_SVG:
290: respondWithErrorXML(res, xmlErrorMsg(status, m));
291: break;
292: case MIME_TYPE_XMLDATA:
293: respondWithErrorXML(res, xmlErrorMsg(status, m), false);
294: break;
295: default:
296: throw new ChainedException(
297: /* (non-Javadoc)
298: * @i18n.test
299: * @org-mes="Responder mime type unknown"
300: */
301: org.openlaszlo.i18n.LaszloMessages.getMessage(
302: Responder.class.getName(), "051018-301"));
303: }
304: }
305:
306: public void respondWithMessage(HttpServletRequest req,
307: HttpServletResponse res, String msg) throws IOException {
308: String surl;
309:
310: switch (getMimeType(req)) {
311: case MIME_TYPE_SWF:
312: respondWithMessageSWF(res, msg);
313: break;
314: case MIME_TYPE_HTML:
315: respondWithErrorHTML(res, msg);
316: break;
317: case MIME_TYPE_XML:
318: case MIME_TYPE_SVG:
319: respondWithErrorXML(res, msg);
320: break;
321: case MIME_TYPE_XMLDATA:
322: respondWithErrorXML(res, msg, false);
323: break;
324: default:
325: throw new ChainedException(
326: /* (non-Javadoc)
327: * @i18n.test
328: * @org-mes="Responder mime type unknown"
329: */
330: org.openlaszlo.i18n.LaszloMessages.getMessage(
331: Responder.class.getName(), "051018-301"));
332: }
333: }
334:
335: public void respondWithMessage(HttpServletResponse res, String msg)
336: throws IOException {
337: String surl;
338:
339: switch (getMimeType()) {
340: case MIME_TYPE_SWF:
341: respondWithMessageSWF(res, msg);
342: break;
343: case MIME_TYPE_HTML:
344: respondWithErrorHTML(res, msg);
345: break;
346: case MIME_TYPE_XML:
347: case MIME_TYPE_SVG:
348: respondWithErrorXML(res, msg);
349: break;
350: case MIME_TYPE_XMLDATA:
351: respondWithErrorXML(res, msg, false);
352: break;
353: default:
354: throw new ChainedException(
355: /* (non-Javadoc)
356: * @i18n.test
357: * @org-mes="Responder mime type unknown"
358: */
359: org.openlaszlo.i18n.LaszloMessages.getMessage(
360: Responder.class.getName(), "051018-301"));
361: }
362: }
363:
364: /**
365: * Send a SWF response indicating the error.
366: */
367: protected void respondWithErrorSWF(HttpServletResponse res, String s) {
368: mLogger.error(
369: /* (non-Javadoc)
370: * @i18n.test
371: * @org-mes="Responding with error SWF: " + p[0]
372: */
373: org.openlaszlo.i18n.LaszloMessages.getMessage(Responder.class
374: .getName(), "051018-344", new Object[] { s }));
375: ServletOutputStream out = null;
376: InputStream in = null;
377: try {
378: res.setContentType(MimeType.SWF);
379: if (mEmitErrorHeader) {
380: // Make sure not to put newlines in the header
381: res.setHeader("X-LPS", s.replace('\n', '_'));
382: }
383: if (mUseBogusErrorCode)
384: res.setStatus(700);
385:
386: synchronized (mErrorSWFCountLock) {
387: mErrorSWFCount++;
388: }
389:
390: // Unknown.
391: // res.setContentLength(output.length);
392:
393: out = res.getOutputStream();
394: String buf = xmlErrorMsg(
395: HttpServletResponse.SC_INTERNAL_SERVER_ERROR, s);
396: in = DataCompiler.compile(buf.toString(), mSWFVersionNum);
397: FileUtils.sendToStream(in, out);
398: } catch (FileUtils.StreamWritingException e) {
399: mLogger.warn(
400: /* (non-Javadoc)
401: * @i18n.test
402: * @org-mes="StreamWritingException while sending error: " + p[0]
403: */
404: org.openlaszlo.i18n.LaszloMessages.getMessage(
405: Responder.class.getName(), "051018-375",
406: new Object[] { e.getMessage() }));
407: } catch (Exception e) {
408: mLogger.warn(
409: /* (non-Javadoc)
410: * @i18n.test
411: * @org-mes="Exception while sending error: " + p[0]
412: */
413: org.openlaszlo.i18n.LaszloMessages.getMessage(
414: Responder.class.getName(), "051018-384",
415: new Object[] { e.getMessage() }));
416: mExceptionStackTraceLogger.error("exception", e);
417: } finally {
418: FileUtils.close(in);
419: FileUtils.close(out);
420: }
421: }
422:
423: /**
424: * Send a SWF response indicating the error.
425: */
426: public static void respondWithMessageSWF(HttpServletResponse res,
427: String s) {
428: if (mUseBogusErrorCode)
429: res.setStatus(700);
430:
431: synchronized (mErrorSWFCountLock) {
432: mErrorSWFCount++;
433: }
434:
435: ServletOutputStream out = null;
436: InputStream in = null;
437: try {
438: out = res.getOutputStream();
439: in = SWFUtils.getErrorMessageSWF(s);
440: if (in != null) {
441: res.setContentType(MimeType.SWF);
442: FileUtils.sendToStream(in, out);
443: }
444: } catch (FileUtils.StreamWritingException e) {
445: mLogger.warn(
446: /* (non-Javadoc)
447: * @i18n.test
448: * @org-mes="StreamWritingException while sending message: " + p[0]
449: */
450: org.openlaszlo.i18n.LaszloMessages.getMessage(
451: Responder.class.getName(), "051018-421",
452: new Object[] { e.getMessage() }));
453: } catch (Exception e) {
454: mLogger.warn(
455: /* (non-Javadoc)
456: * @i18n.test
457: * @org-mes="Exception while sending message: " + p[0]
458: */
459: org.openlaszlo.i18n.LaszloMessages.getMessage(
460: Responder.class.getName(), "051018-430",
461: new Object[] { e.getMessage() }));
462: mExceptionStackTraceLogger.error("exception", e);
463: } finally {
464: FileUtils.close(in);
465: FileUtils.close(out);
466: }
467: }
468:
469: /**
470: * Send a SWF response indicating the exception
471: */
472: protected void respondWithExceptionSWF(HttpServletResponse res,
473: Throwable e) {
474: mExceptionStackTraceLogger.error(e.getMessage(), e);
475: StringWriter s = new StringWriter();
476: PrintWriter p = new PrintWriter(s);
477: e.printStackTrace(p);
478: respondWithErrorSWF(res, e.getMessage()
479: + " : Exception stack: " + s.toString());
480: }
481:
482: /**
483: * Send an HTML response indicating the error.
484: */
485: public static void respondWithErrorHTML(HttpServletResponse res,
486: String s) {
487: mLogger.info(
488: /* (non-Javadoc)
489: * @i18n.test
490: * @org-mes="Responding with error (text/html): " + p[0]
491: */
492: org.openlaszlo.i18n.LaszloMessages.getMessage(Responder.class
493: .getName(), "051018-464", new Object[] { s }));
494: ServletOutputStream out = null;
495: try {
496: res.setContentType("text/html");
497: out = res.getOutputStream();
498: writeHeader(out, null);
499: out.print("<pre>");
500: out.println("Error: " + XMLUtils.escapeXmlForSWFHTML(s));
501: out.println("</pre>");
502: writeFooter(out);
503: } catch (Exception e) {
504: mLogger.warn(
505: /* (non-Javadoc)
506: * @i18n.test
507: * @org-mes="Exception while sending error HTML: " + p[0]
508: */
509: org.openlaszlo.i18n.LaszloMessages.getMessage(
510: Responder.class.getName(), "051018-482",
511: new Object[] { e.getMessage() }));
512: mExceptionStackTraceLogger.error("exception", e);
513: } finally {
514: if (out != null) {
515: try {
516: out.close();
517: } catch (Exception e) {
518: }
519: }
520: }
521: }
522:
523: /**
524: * Send an XML response indicating the error.
525: */
526: protected void respondWithErrorXML(HttpServletResponse res, String s) {
527: respondWithErrorXML(res, s, true);
528: }
529:
530: /**
531: * Send an XML response indicating the error.
532: */
533: protected void respondWithErrorXML(HttpServletResponse res,
534: String s, boolean escape) {
535: mLogger.info(
536: /* (non-Javadoc)
537: * @i18n.test
538: * @org-mes="Responding with error (text/xml): " + p[0]
539: */
540: org.openlaszlo.i18n.LaszloMessages.getMessage(Responder.class
541: .getName(), "051018-506", new Object[] { s }));
542: ServletOutputStream out = null;
543: try {
544: res.setContentType("text/xml");
545: out = res.getOutputStream();
546: if (escape) {
547: out.println("<lps-error>" + XMLUtils.escapeXml(s)
548: + "</lps-error>");
549: } else {
550: out.println(s);
551: }
552: } catch (Exception e) {
553: mLogger.warn(
554: /* (non-Javadoc)
555: * @i18n.test
556: * @org-mes="Exception while sending error XML: " + p[0]
557: */
558: org.openlaszlo.i18n.LaszloMessages.getMessage(
559: Responder.class.getName(), "051018-520",
560: new Object[] { e.getMessage() }));
561: mExceptionStackTraceLogger.error("exception", e);
562: } finally {
563: FileUtils.close(out);
564: }
565: }
566:
567: /**
568: * Send an XML response.
569: */
570: protected void respondWithXML(HttpServletResponse res, String xml)
571: throws IOException {
572: ServletOutputStream out = null;
573: try {
574: res.setContentType("text/xml");
575: out = res.getOutputStream();
576: out.println("<lps>");
577: out.println(xml);
578: out.println("</lps>");
579: } catch (Exception e) {
580: mLogger.warn("Exception while sending XML: "
581: + e.getMessage());
582: mExceptionStackTraceLogger.error("exception", e);
583: } finally {
584: FileUtils.close(out);
585: }
586: }
587:
588: /**
589: * Send a SWF response indicating the exception.
590: */
591: protected void respondWithException(HttpServletResponse res,
592: Throwable e) {
593: String m = e.getMessage();
594: StringWriter s = new StringWriter();
595: PrintWriter p = new PrintWriter(s);
596: e.printStackTrace(p);
597: if (m == null) {
598: m = s.toString();
599: } else {
600: m += s.toString();
601: }
602:
603: respondWithError(res, m,
604: HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
605: }
606:
607: /**
608: * Sends a successful SWF status response.
609: *
610: * @param res client's servlet response
611: * @param status status code (should be SC_OK)
612: * @param mesg status message
613: * @param serial serial number of request to echo back
614: */
615: protected void respondWithStatusSWF(HttpServletResponse res,
616: int status, String mesg, int serial) {
617: respondWithStatusSWF(res, status, mesg, null, serial);
618: }
619:
620: /**
621: * Sends a successful SWF status response w/arbitrary xml.
622: *
623: * @param res client's servlet response
624: * @param status status code (should be SC_OK)
625: * @param mesg status message
626: * @param xmlBody arbitrary xml
627: * @param serial serial number of request to echo back
628: */
629: protected void respondWithStatusSWF(HttpServletResponse res,
630: int status, String mesg, String xmlBody, int serial) {
631: res.setContentType(MimeType.SWF);
632:
633: String _mesg = XMLUtils.escapeXml(mesg);
634:
635: // Successful response codes end up as an attribute of resultset. We
636: // should standardize where the response code ends up.
637: String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
638: + "<!DOCTYPE laszlo-data>" + "<resultset s=\"" + serial
639: + "\">" + "<success code=\"" + status + "\" msg=\""
640: + _mesg + "\" />" + (xmlBody != null ? xmlBody : "")
641: + "</resultset>";
642:
643: mLogger.debug("respondWithStatusSWF: " + xml);
644:
645: ServletOutputStream sos = null;
646: try {
647: sos = res.getOutputStream();
648: InputStream swfbytes = DataCompiler.compile(xml,
649: mSWFVersionNum);
650: FileUtils.sendToStream(swfbytes, sos);
651: } catch (FileUtils.StreamWritingException e) {
652: mLogger.warn(
653: /* (non-Javadoc)
654: * @i18n.test
655: * @org-mes="StreamWritingException while sending status: " + p[0]
656: */
657: org.openlaszlo.i18n.LaszloMessages.getMessage(
658: Responder.class.getName(), "051018-625",
659: new Object[] { e.getMessage() }));
660: } catch (Exception e) {
661: mLogger.warn(
662: /* (non-Javadoc)
663: * @i18n.test
664: * @org-mes="Exception while sending status: " + p[0]
665: */
666: org.openlaszlo.i18n.LaszloMessages.getMessage(
667: Responder.class.getName(), "051018-634",
668: new Object[] { e.getMessage() }));
669: mExceptionStackTraceLogger.error("exception", e);
670: } finally {
671: FileUtils.close(sos);
672: }
673: }
674:
675: /**
676: * Respond with an "over limit" message
677: */
678: void respondWithOverLimitMessage(HttpServletRequest req,
679: HttpServletResponse res) throws IOException {
680: StringBuffer url = HttpUtils.getRequestURL(req);
681: String msg = LPS
682: .getProperty(
683: "messages.over-limit",
684: "The Laszlo Presentation Server that is responsible for serving "
685: + url.toString()
686: + " is over its license limit. The site administrator has been notified.");
687: respondWithMessage(req, res, msg);
688: }
689:
690: /**
691: * Creates an XML error message.
692: *
693: * @param status integer status. Using HTTP status codes, for now, but
694: * Laszlo status codes would be better.
695: * @param msg message to display.
696: */
697: protected String xmlErrorMsg(int status, String msg) {
698: return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
699: + "<!DOCTYPE laszlo-data>"
700: + "<resultset><error status=\"" + status + "\" msg=\""
701: + XMLUtils.escapeXml(msg) + "\"/></resultset>";
702: }
703:
704: /**
705: * Writes the html header tags
706: * @param out A PrintWriter
707: */
708: protected static void writeHeader(ServletOutputStream out, Canvas c)
709: throws IOException {
710: String bgc = "";
711: String title = "";
712: if (c != null) {
713: bgc = "bgcolor=\"" + c.getBGColorString() + "\"";
714: title = c.getTitle();
715: }
716:
717: // Add in title and link
718: out.println("<html><head><title>" + title + "</title>");
719:
720: String ico = LPS.getProperty("shortcut.icon",
721: "http://www.laszlosystems.com/images/laszlo.ico");
722: out.println("<link rel=\"SHORTCUT ICON\" href=\"" + ico
723: + "\"></head>\n");
724: out
725: .println("<body "
726: + bgc
727: + " marginwidth=\"0\" marginheight=\"0\" topmargin=\"0\" leftmargin=\"0\">");
728: }
729:
730: /**
731: * Writes the html footer tags
732: */
733: protected static void writeFooter(ServletOutputStream out)
734: throws IOException {
735: out.println("</body></html>");
736: }
737:
738: protected static int getErrorSWFCount() {
739: synchronized (mErrorSWFCountLock) {
740: return mErrorSWFCount;
741: }
742: }
743:
744: protected static void clearErrorSWFCount() {
745: synchronized (mErrorSWFCountLock) {
746: mErrorSWFCount = 0;
747: }
748: }
749: }
|