KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > go > teaservlet > TeaServlet


1 /* ====================================================================
2  * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
3  * ====================================================================
4  * The Tea Software License, Version 1.1
5  *
6  * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Walt Disney Internet Group (http://opensource.go.com/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact opensource@dig.com.
31  *
32  * 5. Products derived from this software may not be called "Tea",
33  * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34  * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
35  * written permission of the Walt Disney Internet Group.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
41  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
43  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  * For more information about Tea, please see http://opensource.go.com/.
51  */

52
53 package com.go.teaservlet;
54
55 import java.io.IOException JavaDoc;
56 import java.io.PrintWriter JavaDoc;
57 import java.io.StringWriter JavaDoc;
58 import java.util.Collections JavaDoc;
59 import java.util.Enumeration JavaDoc;
60 import java.util.LinkedList JavaDoc;
61 import java.util.List JavaDoc;
62 import java.lang.reflect.Array JavaDoc;
63
64 import javax.servlet.ServletConfig JavaDoc;
65 import javax.servlet.ServletException JavaDoc;
66 import javax.servlet.UnavailableException JavaDoc;
67 import javax.servlet.http.Cookie JavaDoc;
68 import javax.servlet.http.HttpServlet JavaDoc;
69 import javax.servlet.http.HttpServletRequest JavaDoc;
70 import javax.servlet.http.HttpServletResponse JavaDoc;
71
72 import com.go.trove.io.CharToByteBuffer;
73 import com.go.trove.log.Log;
74 import com.go.trove.log.LogEvent;
75 import com.go.trove.log.LogListener;
76 import com.go.trove.util.Utils;
77 import com.go.trove.util.PropertyMap;
78 import com.go.tea.runtime.TemplateLoader;
79
80 /******************************************************************************
81  * The TeaServlet allows Tea templates to define dynamic web pages. The URI
82  * that is passed into the servlet determines what template will be called.
83  * Within the template, custom functions can be called that return JavaBeans.
84  * These functions are accessed via {@link Application Applications} that are
85  * configured to run in the TeaServlet.
86  * <p>
87  * The TeaServlet accepts the following initialization parameters:
88  * <ul>
89  * <li>properties.file - optional path to file with TeaServlet properties, in format used by {@link com.go.teaservlet.util.PropertyParser}
90  * <li>template.path - path to the templates
91  * <li>template.classes - directory to save compiled templates
92  * <li>template.default - the default name for templates
93  * <li>template.file.encoding - character encoding of template source files
94  * <li>template.exception.guardian - when true, runtime exceptions during template execution don't abort page output
95  * <li>separator.query - override the query separator of '?'
96  * <li>separator.parameter - override the parameter separator of '&'
97  * <li>separator.value - override the parameter separator of '='
98  * <li>log.enabled - turns on/off log (boolean)
99  * <li>log.debug - turns on/off log debug messages (boolean)
100  * <li>log.info - turns on/off log info messages (boolean)
101  * <li>log.warn - turns on/off log warning messages (boolean)
102  * <li>log.error - turns on/off log error messages (boolean)
103  * <li>log.max - the max log lines to keep in memory
104  * <li>applications.[name].class - the application class (required)
105  * <li>applications.[name].init.* - prefix for application specific initialization parameters
106  * <li>applications.[name].log.enabled - turns on/off application log (boolean)
107  * <li>applications.[name].log.debug - turns on/off application log debug messages (boolean)
108  * <li>applications.[name].log.info - turns on/off application log info messages (boolean)
109  * <li>applications.[name].log.warn - turns on/off application log warning messages (boolean)
110  * <li>applications.[name].log.error - turns on/off application log error messages (boolean)
111  * </ul>
112  *
113  * @author Reece Wilton
114  * @version
115  * <!--$$Revision:--> 103 <!-- $-->, <!--$$JustDate:--> 01/07/13 <!-- $-->
116  */

117 public class TeaServlet extends HttpServlet JavaDoc {
118     private static final Object JavaDoc[] NO_PARAMS = new Object JavaDoc[0];
119     private static final boolean DEBUG = false;
120     private static final String JavaDoc ENGINE_ATTR =
121         "com.go.teaservlet.TeaServletEngine";
122
123     private ApplicationDepot mApplicationDepot;
124     private Log mLog;
125
126     /** Captured log events that were likely also written to a log file. */
127     private List JavaDoc mLogEvents;
128
129     private String JavaDoc mQuerySeparator;
130     private String JavaDoc mParameterSeparator;
131     private String JavaDoc mValueSeparator;
132
133     private boolean mUseSpiderableRequest;
134
135     /**
136      * Initializes the TeaServlet. Creates the logger and loads the user's
137      * application.
138      * @param config the servlet config
139      */

140     public void init(ServletConfig JavaDoc config) throws ServletException JavaDoc {
141         super.init(config);
142         String JavaDoc ver = System.getProperty("java.version");
143         if (ver.startsWith("0.") || ver.startsWith("1.0") ||
144             ver.startsWith("1.1")) {
145             System.err.println
146                 ("The TeaServlet requires Java 1.2 or higher to run properly");
147         }
148
149         ApplicationDepot applicationDepot =
150             new ApplicationDepot(this, getServletConfig(), mLog);
151
152         PropertyMap properties = applicationDepot.getProperties();
153         Log log = applicationDepot.getLog();
154
155         mQuerySeparator = properties.getString("separator.query", "?");
156         mParameterSeparator = properties.getString("separator.parameter", "&");
157         mValueSeparator = properties.getString("separator.value", "=");
158
159         mUseSpiderableRequest =
160             (!"?".equals(mQuerySeparator)) ||
161             (!"&".equals(mParameterSeparator)) ||
162             (!"=".equals(mValueSeparator));
163
164         if (mLog == null) {
165             mLog = log;
166
167             // Create memory log listener.
168
mLogEvents = Collections.synchronizedList(new LinkedList JavaDoc());
169
170             // The maximum number of log events to store in memory.
171
int max = 100;
172             String JavaDoc maxLogEventsParam = properties.getString("log.max");
173             if (maxLogEventsParam != null) {
174                 try {
175                     max = Integer.parseInt(maxLogEventsParam);
176                 }
177                 catch (NumberFormatException JavaDoc e) {
178                 }
179             }
180
181             final int logEventsMax = max;
182
183             mLog.addRootLogListener(new LogListener() {
184                 public void logMessage(LogEvent e) {
185                     checkSize();
186                     mLogEvents.add(e);
187                 }
188
189                 public void logException(LogEvent e) {
190                     checkSize();
191                     mLogEvents.add(e);
192                 }
193
194                 private void checkSize() {
195                     while (mLogEvents.size() >= logEventsMax) {
196                         mLogEvents.remove(0);
197                     }
198                 }
199             });
200
201             logVersionInfo(TeaServlet.class, "TeaServlet");
202             mLog.info("Copyright (C) 1999-2001 WDIG http://opensource.go.com");
203             logVersionInfo(TemplateLoader.class, "Tea");
204             mLog.info("Copyright (C) 1997-2001 WDIG http://opensource.go.com");
205         }
206
207         ApplicationDepot oldApp;
208         synchronized (this) {
209             oldApp = mApplicationDepot;
210             mApplicationDepot = applicationDepot;
211             mLog = log;
212             
213
214             Object JavaDoc tse = config.getServletContext().getAttribute(ENGINE_ATTR);
215             TeaServletEngine[] tsea;
216
217             if (tse == null || !(tse instanceof TeaServletEngine[])) {
218                 tsea = new TeaServletEngine[]{applicationDepot};
219             }
220             else {
221                 TeaServletEngine[] old_tsea = (TeaServletEngine[])tse;
222                 int old_length = old_tsea.length;
223                 tsea = new TeaServletEngine[old_length + 1];
224                 int i;
225                 for (i=0; i<old_length; i++) {
226                     tsea[i] = old_tsea[i];
227                 }
228                 tsea[i] = applicationDepot;
229             }
230
231             config.getServletContext().setAttribute(ENGINE_ATTR, tsea);
232         }
233
234         if (oldApp != null) {
235             oldApp.destroy();
236         }
237     }
238
239     /**
240      * Destroys the TeaServlet and the user's application.
241      */

242     public void destroy() {
243         if (mApplicationDepot != null) {
244             mLog.info("Destroying Application");
245             mApplicationDepot.destroy();
246         }
247         mLog.info("Destroying TeaServlet");
248         super.destroy();
249     }
250
251     /**
252      * Returns information about the TeaServlet.
253      */

254     public String JavaDoc getServletInfo() {
255         return "Tea template servlet";
256     }
257
258     /**
259      * Process the user's http post request.
260      */

261     protected void doPost(HttpServletRequest JavaDoc request,
262                           HttpServletResponse JavaDoc response)
263         throws ServletException JavaDoc, IOException JavaDoc
264     {
265         doGet(request, response);
266     }
267
268     /**
269      * Process the user's http get request. Process the template that maps to
270      * the URI that was hit.
271      * @param request the user's http request
272      * @param response the user's http response
273      */

274     protected void doGet(HttpServletRequest JavaDoc request,
275                          HttpServletResponse JavaDoc response)
276         throws ServletException JavaDoc, IOException JavaDoc
277     {
278         if (mUseSpiderableRequest) {
279             request = new SpiderableRequest(request,
280                                             mQuerySeparator,
281                                             mParameterSeparator,
282                                             mValueSeparator);
283         }
284
285         response.setContentType("text/html");
286
287         // Wrap the user's http response.
288
ApplicationResponse appResponse =
289             new ApplicationResponseImpl(response, mLog);
290
291         processTemplate(request, appResponse);
292
293         appResponse.finish();
294     }
295
296     /**
297      * Converts the given HTTP parameter value to the requested type so that
298      * it can be passed directly as a template parameter. This method is called
299      * if the template that is directly requested accepts non-String
300      * parameters, and the request provides non-null values for those
301      * parameters. The template may request an array of values for a parameter,
302      * in which case this method is called not to create the array, but rather
303      * to convert any elements put into the array.
304      *
305      * <p>This implementation supports converting parameters of the following
306      * types, and returns null for all others. If a conversion fails, null
307      * is returned.
308      *
309      * <ul>
310      * <li>Integer
311      * <li>Long
312      * <li>Float
313      * <li>Double
314      * <li>Number
315      * <li>Object
316      * </ul>
317      *
318      * When converting to Number, an instance of Integer, Long or Double is
319      * returned, depending on which parse succeeds first. A request for an
320      * Object returns either a Number or a String, depending on the success of
321      * a number parse.
322      *
323      * @param value non-null HTTP parameter value to convert
324      * @param toType Type to convert to
325      * @return an instance of toType or null
326      */

327     protected Object JavaDoc convertParameter(String JavaDoc value, Class JavaDoc toType) {
328         if (toType == Integer JavaDoc.class) {
329             try {
330                 return new Integer JavaDoc(value);
331             }
332             catch (NumberFormatException JavaDoc e) {
333                 return null;
334             }
335         }
336         else if (toType == Long JavaDoc.class) {
337             try {
338                 return new Long JavaDoc(value);
339             }
340             catch (NumberFormatException JavaDoc e) {
341                 return null;
342             }
343         }
344         else if (toType == Float JavaDoc.class) {
345             try {
346                 return new Float JavaDoc(value);
347             }
348             catch (NumberFormatException JavaDoc e) {
349                 return null;
350             }
351         }
352         else if (toType == Double JavaDoc.class) {
353             try {
354                 return new Double JavaDoc(value);
355             }
356             catch (NumberFormatException JavaDoc e) {
357                 return null;
358             }
359         }
360         else if (toType == Number JavaDoc.class || toType == Object JavaDoc.class) {
361             try {
362                 return new Integer JavaDoc(value);
363             }
364             catch (NumberFormatException JavaDoc e) {
365             }
366             try {
367                 return new Long JavaDoc(value);
368             }
369             catch (NumberFormatException JavaDoc e) {
370             }
371             try {
372                 return new Double JavaDoc(value);
373             }
374             catch (NumberFormatException JavaDoc e) {
375                 return (toType == Object JavaDoc.class) ? value : null;
376             }
377         }
378         else {
379             return null;
380         }
381     }
382
383     /**
384      * Returns the ApplicationDepot, which is used by the admin functions.
385      */

386     ApplicationDepot getApplicationDepot() {
387         return mApplicationDepot;
388     }
389
390     /**
391      * Returns the Log for the TeaServlet, which is used by the admin
392      * functions.
393      */

394     Log getLog() {
395         return mLog;
396     }
397
398     /**
399      * Returns the lines that have been written to the log file. This is used
400      * by the admin functions.
401      */

402     LogEvent[] getLogEvents() {
403         if (mLogEvents == null) {
404             return new LogEvent[0];
405         }
406         else {
407             LogEvent[] events = new LogEvent[mLogEvents.size()];
408             return (LogEvent[])mLogEvents.toArray(events);
409         }
410     }
411
412     /**
413      * Finds the template to execute based on the user's request and runs it.
414      *
415      * @param request the user's http request
416      * @param response the user's http response
417      */

418     private void processTemplate(HttpServletRequest JavaDoc request,
419                                  ApplicationResponse appResponse)
420         throws IOException JavaDoc
421     {
422         // get template path
423
String JavaDoc path;
424         if ((path = request.getPathInfo()) == null) {
425             if ((path = request.getServletPath()) == null) {
426                 path = "/";
427             }
428             else {
429                 // Strip off any extension.
430
int index = path.lastIndexOf('.');
431                 if (index >= 0) {
432                     path = path.substring(0, index);
433                 }
434             }
435         }
436
437         if (DEBUG) {
438             // add some comic relief!
439
mLog.debug("aagggghhh... i've been hit! (" + path + ")");
440             mLog.debug("Finding template for " + path);
441         }
442
443         try {
444             // Find the matching template.
445
TemplateLoader.Template template =
446                 mApplicationDepot.findTemplate(path, request, appResponse);
447             if (appResponse.isRedirectOrError()) {
448                 return;
449             }
450
451             if (template == null) {
452                 appResponse.sendError
453                     (appResponse.SC_NOT_FOUND, request.getRequestURI());
454                 return;
455             }
456
457             // Fill in the parameters to pass to the template.
458
Object JavaDoc[] params;
459             Class JavaDoc[] paramTypes = template.getParameterTypes();
460             if (paramTypes.length == 0) {
461                 params = NO_PARAMS;
462             }
463             else {
464                 params = new Object JavaDoc[paramTypes.length];
465                 String JavaDoc[] paramNames = template.getParameterNames();
466                 for (int i=0; i<paramNames.length; i++) {
467                     String JavaDoc paramName = paramNames[i];
468                     if (paramName == null) {
469                         continue;
470                     }
471
472                     Class JavaDoc paramType = paramTypes[i];
473
474                     if (!paramType.isArray()) {
475                         String JavaDoc value = request.getParameter(paramName);
476                         if (value == null || paramType == String JavaDoc.class) {
477                             params[i] = value;
478                         }
479                         else {
480                             params[i] = convertParameter(value, paramType);
481                         }
482                     }
483                     else {
484                         String JavaDoc[] values =
485                             request.getParameterValues(paramName);
486                         if (values == null || paramType == String JavaDoc[].class) {
487                             params[i] = values;
488                         }
489                         else {
490                             paramType = paramType.getComponentType();
491                             Object JavaDoc converted =
492                                 Array.newInstance(paramType, values.length);
493                             params[i] = converted;
494                             for (int j=0; j<values.length; j++) {
495                                 Array.set
496                                     (converted, j,
497                                      convertParameter(values[j], paramType));
498                             }
499                         }
500                     }
501                 }
502             }
503
504             ApplicationRequest appRequest =
505                 new ApplicationRequestImpl(request, template);
506
507             HttpContext context =
508                 mApplicationDepot.createContext(appRequest, appResponse);
509
510             if (DEBUG) {
511                 mLog.debug("Executing template");
512             }
513             try {
514                 template.execute(context, params);
515             }
516             catch (AbortTemplateException e) {
517                 if (DEBUG) {
518                     mLog.debug("Template execution aborted!");
519                 }
520             }
521             catch (RuntimeException JavaDoc e) {
522                 if (mApplicationDepot.getTemplateDepot().
523                     isExceptionGuardianEnabled()) {
524                     // Just log the error and use what the template wrote out.
525
mLog.error(e);
526                 }
527                 else {
528                     throw new ServletException JavaDoc(e);
529                 }
530             }
531             catch (IOException JavaDoc e) {
532                 throw e;
533             }
534             catch (ServletException JavaDoc e) {
535                 throw e;
536             }
537             catch (Exception JavaDoc e) {
538                 throw new ServletException JavaDoc(e);
539             }
540
541             if (DEBUG) {
542                 mLog.debug("Finished executing template");
543             }
544         }
545         catch (ServletException JavaDoc e) {
546             // Log exception
547
StringBuffer JavaDoc msg = new StringBuffer JavaDoc();
548             msg.append("Error processing request for ");
549             msg.append(request.getRequestURI());
550             if (request.getQueryString() != null) {
551                 msg.append('?');
552                 msg.append(request.getQueryString());
553             }
554             mLog.error(msg.toString());
555
556             Throwable JavaDoc t = e;
557             while (t instanceof ServletException JavaDoc) {
558                 e = (ServletException JavaDoc)t;
559                 if (e.getRootCause() != null) {
560                     String JavaDoc message = e.getMessage();
561                     if (message != null && message.length() > 0) {
562                         mLog.error(message);
563                     }
564                     mLog.error(t = e.getRootCause());
565                 }
566                 else {
567                     mLog.error(e);
568                     break;
569                 }
570             }
571
572             // Internal server error unless header is already set
573
if (!appResponse.isRedirectOrError()) {
574                 String JavaDoc displayMessage = e.getLocalizedMessage();
575                 if (displayMessage == null || displayMessage.length() == 0) {
576                     appResponse.sendError
577                         (appResponse.SC_INTERNAL_SERVER_ERROR);
578                 }
579                 else {
580                     appResponse.sendError
581                         (appResponse.SC_INTERNAL_SERVER_ERROR, displayMessage);
582                 }
583             }
584         }
585     }
586
587     private void logVersionInfo(Class JavaDoc clazz, String JavaDoc title) {
588         Package JavaDoc pack = clazz.getPackage();
589         String JavaDoc version = null;
590         if (pack != null) {
591             if (pack.getImplementationTitle() != null) {
592                 title = pack.getImplementationTitle();
593             }
594             version = pack.getImplementationVersion();
595         }
596         if (version == null) {
597             try {
598                 String JavaDoc classname = clazz.getName();
599                 Class JavaDoc packinf = Class.forName(
600                     classname.substring(0,classname.lastIndexOf('.'))
601                     + ".PackageInfo");
602                 java.lang.reflect.Method JavaDoc mo = packinf.getMethod(
603                     "getProductVersion",null);
604                 version = mo.invoke(null,null).toString();
605             }
606             catch (Exception JavaDoc pie) {
607                 mLog.info("PackageInfo not found");
608                 mLog.debug(pie);
609             }
610             if (version == null) {
611                 version = "<unknown>";
612             }
613         }
614         mLog.info(title + " version " + version);
615     }
616 }
617
Popular Tags