KickJava   Java API By Example, From Geeks To Geeks.

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


1 /******************************************************************************
2  * ResponderCache.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.UnknownHostException JavaDoc;
14 import java.net.MalformedURLException JavaDoc;
15 import java.net.URL JavaDoc;
16 import java.util.Properties JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import javax.servlet.ServletConfig JavaDoc;
20 import javax.servlet.ServletException JavaDoc;
21 import javax.servlet.http.HttpServletRequest JavaDoc;
22 import javax.servlet.http.HttpServletResponse JavaDoc;
23 import javax.servlet.ServletOutputStream JavaDoc;
24 import org.openlaszlo.cache.RequestCache;
25 import org.openlaszlo.data.*;
26 import org.openlaszlo.media.MimeType;
27 import org.openlaszlo.server.LPS;
28 import org.openlaszlo.utils.LZHttpUtils;
29 import org.openlaszlo.utils.ChainedException;
30 import org.openlaszlo.xml.internal.XMLUtils;
31 import org.apache.commons.httpclient.URI;
32 import org.apache.commons.httpclient.URIException;
33 import org.apache.log4j.Logger;
34
35 public abstract class ResponderCache extends Responder
36 {
37     private static boolean mIsInitialized = false;
38
39     private static HashMap JavaDoc mDataSourceMap = new HashMap JavaDoc();
40     private static DataSource mHTTPDataSource = null;
41
42     private static Logger mLogger = Logger.getLogger(ResponderCache.class);
43
44     protected RequestCache mCache = null;
45
46     /**
47      * Keeps track of url stats.
48      */

49     public class URLStat
50     {
51         String JavaDoc mName;
52
53         final static public int ERRTYPE_NONE = -1;
54         final static public int ERRTYPE_CONVERSION = 0;
55         final static public int ERRTYPE_DATA_SOURCE = 1;
56         final static public int ERRTYPE_UNKNOWN_HOST = 2;
57         final static public int ERRTYPE_MALFORMED_URL = 3;
58         final static public int ERRTYPE_IO = 4;
59         final static public int ERRTYPE_ILLEGAL_ARGUMENT = 5;
60         final static public int ERRTYPE_TIMEOUT = 6;
61         final static public int ERRTYPE_FORBIDDEN = 7;
62         final static public int ERRTYPE_OTHER = 8;
63         final static public int NUM_ERRTYPES = 9;
64
65
66         HashMap JavaDoc mURLs = new HashMap JavaDoc();
67         HashMap JavaDoc mErrorURLs = new HashMap JavaDoc();
68
69         int mSuccessCount;
70         /**
71          * 0: mConversionException, 1: mDataSourceException, 2: mUnknownHostException,
72          * 3: mMalformedURLException, 4: mIOException, 5: mIllegalArgumentException,
73          * 6: mInterrupedIOException, 7 mException
74          */

75         int[] mErrorCount = new int[NUM_ERRTYPES];
76
77         
78         boolean mDoURLCollection = false;
79         
80
81         /**
82          * Create an URLStat with a name.
83          */

84         public URLStat(String JavaDoc name)
85         {
86             mName = name;
87             clear();
88         }
89         
90         /**
91          * @param doCollection whether the object should collect unique url
92          * info.
93          */

94         void doURLCollection(boolean doCollection)
95         {
96             if (mDoURLCollection != doCollection) {
97                 mDoURLCollection = doCollection;
98                 clear();
99             }
100         }
101
102         /**
103          * Increment stat on url with successful status.
104          * @param url successful url.
105          */

106         void success(String JavaDoc url)
107         {
108             int x = url.indexOf('?');
109             if (x != -1) {
110                 url = url.substring(0, x);
111             }
112             synchronized (mURLs) {
113                 if (mDoURLCollection) {
114                     // Add unique urls
115
int[] s = (int[]) mURLs.get(url);
116                     if (s == null) {
117                         s = new int[1];
118                         mURLStat.mURLs.put(url, s);
119                     }
120                     ++s[0];
121                 }
122                 ++mSuccessCount;
123             }
124         }
125
126
127         /**
128          * Increment stat on url with error status.
129          *
130          * @param errType see ERRTYPEs.
131          * @param url error url.
132          */

133         void error(int errType, String JavaDoc url)
134         {
135             int x = url.indexOf('?');
136             if (x != -1) {
137                 url = url.substring(0, x);
138             }
139             synchronized (mErrorURLs) {
140                 if (mDoURLCollection) {
141                     int[] e = (int[]) mErrorURLs.get(url);
142                     if (e == null) {
143                         e = new int[NUM_ERRTYPES];
144                         mErrorURLs.put(url, e);
145                     }
146                     ++e[errType];
147                 }
148                 ++mErrorCount[errType];
149             }
150
151         }
152
153         /**
154          * Clear URL stats.
155          */

156         void clear()
157         {
158             synchronized (mURLs) {
159                 mURLs.clear();
160                 mSuccessCount = 0;
161             }
162
163             synchronized (mErrorURLs) {
164                 mErrorURLs.clear();
165                 for (int i=0; i < mErrorCount.length; i++)
166                     mErrorCount[i] = 0;
167             }
168         }
169
170         /**
171          * Return url statistics in XML string.
172          * @return xml info.
173          */

174         public String JavaDoc toXML()
175         {
176             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
177             synchronized (mURLs) {
178                 buf.append("<").append(mName).append("-urls ")
179                     .append(" unique=\"").append(mURLs.size()).append("\"")
180                     .append(">\n");
181
182                 buf.append("<success")
183                     .append(" total-requests=\"").append(mSuccessCount).append("\"")
184                     .append(">\n");
185                 if (mDoURLCollection)
186                 {
187                     Iterator JavaDoc iter = mURLs.keySet().iterator();
188                     while (iter.hasNext()) {
189                         String JavaDoc k = (String JavaDoc)iter.next();
190                         int[] success = (int[])mURLs.get(k);
191                         buf.append("<url")
192                             .append(" requests=\"").append(success[0]).append("\"")
193                             .append(" HREF=\"").append(XMLUtils.escapeXml(k)).append("\" />");
194                     }
195                 }
196                 buf.append("</success>\n");
197             }
198
199             synchronized (mErrorURLs) {
200                 int errTotal = 0;
201                 for (int i=0; i < mErrorCount.length; i++)
202                     errTotal += mErrorCount[i];
203                 buf.append("<errors")
204                     .append(" total-errors=\"").append(errTotal).append("\"")
205                     .append(" conversion=\"").append(mErrorCount[ERRTYPE_CONVERSION]).append("\"")
206                     .append(" datasource=\"").append(mErrorCount[ERRTYPE_DATA_SOURCE]).append("\"")
207                     .append(" unknownhost=\"").append(mErrorCount[ERRTYPE_UNKNOWN_HOST]).append("\"")
208                     .append(" malformedurl=\"").append(mErrorCount[ERRTYPE_MALFORMED_URL]).append("\"")
209                     .append(" ioexception=\"").append(mErrorCount[ERRTYPE_IO]).append("\"")
210                     .append(" illegalargument=\"").append(mErrorCount[ERRTYPE_ILLEGAL_ARGUMENT]).append("\"")
211                     .append(" timeout=\"").append(mErrorCount[ERRTYPE_TIMEOUT]).append("\"")
212                     .append(" forbidden=\"").append(mErrorCount[ERRTYPE_FORBIDDEN]).append("\"")
213                     .append(" uncaught-exception=\"").append(mErrorCount[ERRTYPE_OTHER]).append("\"")
214                     .append(">\n");
215                 if (mDoURLCollection)
216                 {
217                     Iterator JavaDoc iter = mErrorURLs.keySet().iterator();
218                     while (iter.hasNext()) {
219                         String JavaDoc k = (String JavaDoc)iter.next();
220                         int[] e = (int[])mErrorURLs.get(k);
221                         buf.append("<url")
222                             .append(" conversion=\"").append(e[ERRTYPE_CONVERSION]).append("\"")
223                             .append(" datasource=\"").append(e[ERRTYPE_DATA_SOURCE]).append("\"")
224                             .append(" unknownhost=\"").append(e[ERRTYPE_UNKNOWN_HOST]).append("\"")
225                             .append(" malformedurl=\"").append(e[ERRTYPE_MALFORMED_URL]).append("\"")
226                             .append(" ioexception=\"").append(e[ERRTYPE_IO]).append("\"")
227                             .append(" illegalargument=\"").append(e[ERRTYPE_ILLEGAL_ARGUMENT]).append("\"")
228                             .append(" timeout=\"").append(e[ERRTYPE_TIMEOUT]).append("\"")
229                             .append(" forbidden=\"").append(e[ERRTYPE_FORBIDDEN]).append("\"")
230                             .append(" uncaught-exception=\"").append(e[ERRTYPE_OTHER]).append("\"")
231                             .append(" HREF=\"").append(XMLUtils.escapeXml(k)).append("\"")
232                             .append(" />\n");
233                     }
234                 }
235                 buf.append("</errors>\n");
236             }
237
238             buf.append("</").append(mName).append("-urls>\n");
239
240             return buf.toString();
241         }
242     }
243
244     public URLStat mURLStat = null;
245
246     synchronized public void init(String JavaDoc reqName, ServletConfig JavaDoc config,
247             RequestCache cache, Properties JavaDoc prop)
248         throws ServletException JavaDoc, IOException
249     {
250         super.init(reqName, config, prop);
251
252         String JavaDoc reqProp = reqName.toLowerCase() + "Request.collectURL";
253         boolean doURLCollection =
254             prop.getProperty(reqProp, "false").intern() == "true";
255
256         mURLStat = new URLStat(reqName);
257         mURLStat.doURLCollection(doURLCollection);
258
259         if (! mIsInitialized) {
260             //------------------------------------------------------------
261
// Well-known data sources
262
//------------------------------------------------------------
263
mHTTPDataSource = new HTTPDataSource();
264
265             mDataSourceMap.put("http", mHTTPDataSource);
266
267             // For security reasons, we are now mapping file => http
268
mDataSourceMap.put("file", mHTTPDataSource);
269
270             /*
271             try {
272                 mDataSourceMap.put("file", new FileDataSource());
273             } catch (Throwable e) {
274                 mLogger.warn("can't load file datasource", e);
275             }
276             */

277
278             try {
279                 mDataSourceMap.put("java", new JavaDataSource());
280             } catch (Throwable JavaDoc e) {
281                 mLogger.warn("can't load java datasource", e);
282             }
283
284             try {
285                 mDataSourceMap.put("soap", new SOAPDataSource());
286             } catch (Throwable JavaDoc e) {
287                 mLogger.warn("can't load soap datasource", e);
288             }
289
290             try {
291                 mDataSourceMap.put("xmlrpc", new XMLRPCDataSource());
292             } catch (Throwable JavaDoc e) {
293                 mLogger.warn("can't load xmlrpc datasource", e);
294             }
295
296             mIsInitialized = true;
297         }
298
299         mCache = cache;
300     }
301
302     protected void respondImpl(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc res) {
303
304         try {
305             req.setCharacterEncoding("UTF-8");
306         } catch (Exception JavaDoc e) { }
307
308         String JavaDoc path = req.getServletPath();
309         String JavaDoc url;
310         try {
311             url = DataSource.getURL(req);
312         } catch (java.net.MalformedURLException JavaDoc e) {
313             respondWithErrorSWF(res, "bad url: " + e.getMessage());
314             if (mCollectStat) {
315                 mURLStat.error(URLStat.ERRTYPE_MALFORMED_URL, "bad-url");
316             }
317             return;
318         }
319
320         if (path.endsWith(".lzo")) {
321             path = path.substring(0, path.length() - 1) + "x";
322         }
323
324         if (req.getMethod().intern() == "POST") {
325             float fpv = getFlashPlayerVersion(req);
326             String JavaDoc ua = req.getHeader(LZHttpUtils.USER_AGENT);
327             mLogger.debug("POST request, flash player version: " + fpv);
328             if (fpv < 6.47 &&
329                 LPS.configuration.optionAllows("disable-post-keep-alive", ua)) {
330                 // Prevent browser keep-alive to get around bug 4048.
331
mLogger.debug("Disabling keep-alive for " + ua);
332                 res.setHeader("Connection", "close");
333                 res.setHeader("Keep-Alive", "close");
334             }
335         }
336
337         if ( ! LPS.configuration.optionAllows(path, "proxy-security-urls", url) ) {
338             String JavaDoc err = "Forbidden url: " + url;
339             respondWithError(res, err, HttpServletResponse.SC_FORBIDDEN);
340             mLogger.error(err);
341             if (mCollectStat) {
342                 mURLStat.error(URLStat.ERRTYPE_FORBIDDEN, url);
343             }
344             return;
345         }
346
347         int errType = URLStat.ERRTYPE_NONE;
348
349         try {
350
351             DataSource source = getDataSource(req, res);
352             if (source == null) {
353                 return;
354             }
355
356             res.setContentType(MimeType.SWF);
357
358             String JavaDoc app = LZHttpUtils.getRealPath(mContext, req);
359             boolean isClientCacheable = DataSource.isClientCacheable(req);
360             if (mCache.isCacheable(req)) {
361                 if (isClientCacheable) {
362                     mLogger.info("proxying " + url + ", cacheable on server and client");
363                 } else {
364                     mLogger.info("proxying " + url + ", cacheable on server and not client");
365                 }
366                 mCache.getAsSWF(app, req, res, source);
367             } else {
368                 if (isClientCacheable) {
369                     mLogger.info("proxying " + url + ", not cacheable on server and cacheable on the client");
370                 } else {
371                     mLogger.info("proxying " + url + ", not cacheable on server or client");
372                 }
373                 source.getAsSWF(app, req, res, getConverter());
374             }
375         } catch (ConversionException e) {
376             respondWithErrorSWF(res, "data conversion error for " + url +
377                                      ": " + e.getMessage());
378             errType = URLStat.ERRTYPE_CONVERSION;
379         } catch (DataSourceException e) {
380                 respondWithErrorSWF(res, "data source error for " + url +
381                                          ": " + e.getMessage());
382             errType = URLStat.ERRTYPE_DATA_SOURCE;
383         } catch (UnknownHostException JavaDoc e) {
384             respondWithErrorSWF(res, "unknown host for " + url +
385                                      ": " + e.getMessage());
386             errType = URLStat.ERRTYPE_UNKNOWN_HOST;
387         } catch (URIException e) {
388             respondWithErrorSWF(res, "bad url: " + e.getMessage());
389             errType = URLStat.ERRTYPE_MALFORMED_URL;
390         } catch (MalformedURLException JavaDoc e) {
391             respondWithErrorSWF(res, "bad url: " + e.getMessage());
392             errType = URLStat.ERRTYPE_MALFORMED_URL;
393         } catch (InterruptedIOException e) {
394             respondWithErrorSWF(res, "backend timeout for " + url +
395                                      ": " + e.getMessage());
396             errType = URLStat.ERRTYPE_TIMEOUT;
397         } catch (IOException e) {
398             // Handle SocketTimeoutExceptions as timeouts instead of IO issues
399
Class JavaDoc stec = null;
400             try {
401                 stec = Class.forName("java.net.SocketTimeoutException");
402             } catch (ClassNotFoundException JavaDoc cfne) {
403             }
404             if (stec != null && stec.isAssignableFrom(e.getClass())) {
405                 errType = URLStat.ERRTYPE_TIMEOUT;
406                 respondWithErrorSWF(res, "backend timeout for " + url +
407                                          ": " + e.getMessage());
408             } else {
409                 respondWithExceptionSWF(res, e);
410                 errType = URLStat.ERRTYPE_IO;
411             }
412         } catch (IllegalArgumentException JavaDoc e) {
413             respondWithExceptionSWF(res, e);
414             errType = URLStat.ERRTYPE_ILLEGAL_ARGUMENT;
415         } catch (Throwable JavaDoc e) {
416             // Makes much easier to debug runtime exceptions
417
// but perhaps not strictly correct.
418
respondWithExceptionSWF(res, e);
419             errType = URLStat.ERRTYPE_OTHER;
420         }
421
422         if (mCollectStat) {
423             if (errType == URLStat.ERRTYPE_NONE)
424                 mURLStat.success(url);
425             else
426                 mURLStat.error(errType, url);
427         }
428     }
429
430     /**
431      * @return the datasource for this request
432      */

433     protected DataSource getDataSource(HttpServletRequest JavaDoc req,
434                                        HttpServletResponse JavaDoc res)
435         throws MalformedURLException JavaDoc, URIException
436     {
437         String JavaDoc ds = "http";
438         String JavaDoc urlstr = DataSource.getURL(req);
439         if (urlstr != null) {
440             mLogger.debug("urlstr " + urlstr);
441             URI uri = LZHttpUtils.newURI(urlstr);
442             String JavaDoc protocol = uri.getScheme();
443             if (protocol != null && protocol.equals("https")) {
444                     protocol = "http";
445             }
446
447             ds = protocol;
448         }
449
450         mLogger.debug("ds is " + ds);
451
452         DataSource source = null;
453         if (ds == null) {
454             source = mHTTPDataSource;
455         } else {
456             source = (DataSource) mDataSourceMap.get(ds);
457             if (source == null)
458                 respondWithErrorSWF(res, "Can't find a data source for " + urlstr);
459         }
460         return source;
461     }
462
463     public int getMimeType()
464     {
465         return MIME_TYPE_SWF;
466     }
467
468     public float getFlashPlayerVersion(HttpServletRequest JavaDoc req) {
469         float fpv = (float)-1.0;
470         try {
471             String JavaDoc _fpv = req.getParameter("fpv");
472             if (_fpv != null)
473                 fpv = Float.parseFloat(_fpv);
474         } catch (NumberFormatException JavaDoc e) {
475             mLogger.debug(e.getMessage());
476         }
477         return fpv;
478     }
479
480     /**
481      * @return the converter to be used by this cache
482      */

483     public Converter getConverter() {
484         return mCache.getConverter();
485     }
486 }
487
Popular Tags