KickJava   Java API By Example, From Geeks To Geeks.

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


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.BufferedReader JavaDoc;
56 import java.io.File JavaDoc;
57 import java.io.IOException JavaDoc;
58 import java.util.Collection JavaDoc;
59 import java.util.Iterator JavaDoc;
60 import java.util.Map JavaDoc;
61 import java.util.HashMap JavaDoc;
62 import java.util.TreeSet JavaDoc;
63 import java.util.Arrays JavaDoc;
64 import java.util.ArrayList JavaDoc;
65 import java.util.Vector JavaDoc;
66
67 import javax.servlet.ServletContext JavaDoc;
68 import javax.servlet.http.HttpServletRequest JavaDoc;
69
70 import com.go.trove.log.Log;
71 import com.go.trove.util.ClassInjector;
72 import com.go.trove.io.LinePositionReader;
73
74 import com.go.tea.compiler.CompilationUnit;
75 import com.go.tea.compiler.ErrorEvent;
76 import com.go.tea.compiler.ErrorListener;
77 import com.go.tea.compiler.SourceInfo;
78 import com.go.tea.runtime.TemplateLoader;
79 import com.go.tea.util.AbstractFileCompiler;
80 import com.go.tea.util.FileCompiler;
81 import com.go.teaservlet.util.RemoteCompiler;
82
83 /******************************************************************************
84  * The TemplateDepot stores the templates that have been loaded in the
85  * Application.
86  * <p>
87  * depot -- a place for storing goods
88  *
89  * @author Reece Wilton
90  * @version
91  * <!--$$Revision:--> 68 <!-- $-->, <!--$$JustDate:--> 01/07/10 <!-- $-->
92  */

93 class TemplateDepot {
94     /** Package for templates */
95     public final static String JavaDoc TEMPLATE_PACKAGE = "com.go.teaservlet.template";
96
97     /** Short package for system templates */
98     public final static String JavaDoc SYSTEM_PACKAGE = "system";
99
100     /** Full package for system templates */
101     public final static String JavaDoc SYSTEM_TEMPLATE_PACKAGE = TEMPLATE_PACKAGE + '.' + SYSTEM_PACKAGE;
102
103     private Log mLog;
104
105     private Class JavaDoc mContextClass;
106
107     private ClassLoader JavaDoc mParentLoader;
108
109     private String JavaDoc mDefaultTemplate;
110
111     /** The destination directory to write compiled templates to. */
112     private File JavaDoc mDestinationDir;
113
114     /** The optional character encoding of template source files. */
115     private String JavaDoc mEncoding;
116
117     private boolean mExceptionGuardian;
118
119     private boolean mPreloadTemplates;
120
121     /** The template loader to use to load template classes. */
122     private TemplateLoader mLoader;
123
124     /** When true, reject recompiled templates if any errors. */
125     private boolean mRejectErrors = false;
126
127     private final Object JavaDoc mLoadLock = new Object JavaDoc();
128
129     /**
130      * Constructs the TemplateDepot and creates a templates store. The parent
131      * class loader will be used to load all non-template classes.
132      *
133      * @param log the logger
134      * @param contextClass runtime context class for templates
135      * @param defaultTemplate the name of the default template (null if none)
136      * @param destDir optional destination directory to write compiled
137      * templates to
138      */

139     TemplateDepot(Log log, Class JavaDoc contextClass,
140                   String JavaDoc defaultTemplate, File JavaDoc destDir, String JavaDoc encoding,
141                   boolean exceptionGuardian,boolean preloadTemplates) {
142         mLog = log;
143         mContextClass = contextClass;
144         mParentLoader = contextClass.getClassLoader();
145         mDefaultTemplate = defaultTemplate;
146         mDestinationDir = destDir;
147         mEncoding = encoding;
148         mExceptionGuardian = exceptionGuardian;
149         mPreloadTemplates = preloadTemplates;
150     }
151
152     public boolean isExceptionGuardianEnabled() {
153         return mExceptionGuardian;
154     }
155
156     /**
157      * Finds a template based on the given URI. If path ends in a slash, revert
158      * to loading default template. If default not found or not specified,
159      * return null. A redirect will occur if there is a default template but
160      * the URI doesn't end in a slash.
161      *
162      * @param uri the URI of the template to find
163      * @return the template that maps to the URI or null if no template maps to
164      * the URI
165      * @throws NoSuchMethodException when found template class is invalid
166      * @throws LinkageError when found template class couldn't be loaded
167      */

168     public TemplateLoader.Template findTemplate(String JavaDoc uri,
169                                                 HttpServletRequest JavaDoc request,
170                                                 ApplicationResponse response)
171         throws NoSuchMethodException JavaDoc, IOException JavaDoc, LinkageError JavaDoc
172     {
173         TemplateLoader loader = getTemplateLoader();
174         TemplateLoader.Template template = null;
175
176         // If path ends in a slash, revert to loading default template. If
177
// default not found or not specified, return null.
178
boolean useDefault = uri.endsWith("/");
179
180         // Trim slashes and replace with dots.
181
while (uri.startsWith("/")) {
182             uri = uri.substring(1);
183         }
184         while (uri.endsWith("/")) {
185             uri = uri.substring(0, uri.length() - 1);
186         }
187         String JavaDoc name = uri.replace('/', '.');
188
189         // Look up template if not trying to use default.
190
if (!useDefault) {
191             // Find template that matches the uri
192
try {
193                 template = loader.getTemplate(name);
194             }
195             catch (ClassNotFoundException JavaDoc e) {
196                 mLog.debug("Can't find template \"" + name + "\": " + e);
197                 template = null;
198             }
199         }
200
201         // Use default if no template found so far.
202
if ((template == null) && (mDefaultTemplate != null)) {
203             if (name.length() == 0) {
204                 name = mDefaultTemplate;
205             }
206             else {
207                 name = name + '.' + mDefaultTemplate;
208             }
209
210             try {
211                 template = loader.getTemplate(name);
212
213                 // Redirect if no slash on end of URI.
214
if (template != null && !useDefault) {
215                     StringBuffer JavaDoc location = new StringBuffer JavaDoc(request.getRequestURI());
216                     int length = location.length();
217                     if (length == 0 || location.charAt(length - 1) != '/') {
218                         location.append('/');
219                     }
220                     String JavaDoc query = request.getQueryString();
221                     if (query != null) {
222                         location.append('?').append(query);
223                     }
224                     response.setStatus(response.SC_MOVED_PERMANENTLY);
225                     response.sendRedirect(location.toString());
226                 }
227             }
228             catch (ClassNotFoundException JavaDoc e) {
229                 mLog.debug("Can't find default template \"" +
230                            name + "\": " + e);
231             }
232         }
233
234         return template;
235     }
236
237     /**
238      * Preloads all the tea template files that exist in the given directory
239      * and its sub-directories while re-compiling any that are out of date. If
240      * there were no compilation errors, templates that were currently loaded
241      * are removed. If any pre-compiled templates failed to load properly, all
242      * of the templates are re-compiled, even those that are up to date.
243      *
244      * @param directory the directory to find the tea files
245      * @return the names of all the templates that were compiled
246      */

247     public TemplateLoadResult loadTemplates(String JavaDoc directory) {
248         return loadTemplates(new String JavaDoc[]{directory}, false);
249     }
250
251     /**
252      * Preloads all the tea template files that exist in the given directories
253      * and its sub-directories while re-compiling any that are out of date. If
254      * there were no compilation errors, templates that were currently loaded
255      * are removed. If any pre-compiled templates failed to load properly, all
256      * of the templates are re-compiled, even those that are up to date.
257      *
258      * @param directory the directory to find the tea files
259      * @return the names of all the templates that were compiled
260      */

261     public TemplateLoadResult loadTemplates(String JavaDoc[] directories) {
262         return loadTemplates(directories, false);
263     }
264
265     /**
266      * Preloads all the tea template files that exist in the given directory
267      * and its sub-directories while re-compiling any that are out of date. If
268      * there were no compilation errors, templates that were currently loaded
269      * are removed. If any pre-compiled templates failed to load properly, or
270      * the force option is true, all of the templates are re-compiled, even
271      * those that are up to date.
272      *
273      * @param directory the directory to find the tea files
274      * @param force when true, force compile all templates
275      * @return the names of all the templates that were compiled
276      */

277     public TemplateLoadResult loadTemplates(String JavaDoc directory, boolean force) {
278         return loadTemplates(new String JavaDoc[]{directory}, force);
279     }
280
281     /**
282      * Preloads all the tea template files that exist in the given directories
283      * and its sub-directories while re-compiling any that are out of date. If
284      * there were no compilation errors, templates that were currently loaded
285      * are removed. If any pre-compiled templates failed to load properly, or
286      * the force option is true, all of the templates are re-compiled, even
287      * those that are up to date.
288      *
289      * @param directory the directory to find the tea files
290      * @param force when true, force compile all templates
291      * @return the names of all the templates that were compiled
292      */

293     public TemplateLoadResult loadTemplates(String JavaDoc[] directories,
294                                             boolean force) {
295         // Only one thread may load templates at a time.
296
synchronized (mLoadLock) {
297             return loadTemplates0(directories, force);
298         }
299     }
300
301     private TemplateLoadResult loadTemplates0(String JavaDoc[] directories,
302                                               boolean force) {
303         if (directories.length == 0) {
304             setTemplateLoader(null);
305             mRejectErrors = false;
306             return new TemplateLoadResult();
307         }
308         Vector JavaDoc remoteVec = new Vector JavaDoc();
309         Vector JavaDoc localVec = new Vector JavaDoc();
310         
311         // Sort out the local directories from those using http.
312
for (int i=0; i<directories.length; i++) {
313             if (directories[i].startsWith("http://")) {
314                 remoteVec.add(directories[i]);
315             }
316             else {
317                 localVec.add(new File JavaDoc(directories[i]));
318             }
319         }
320         File JavaDoc[] localDirs = (File JavaDoc[])localVec.toArray(new File JavaDoc[localVec.size()]);
321         String JavaDoc[] remoteDirs = (String JavaDoc[])remoteVec.toArray
322             (new String JavaDoc[remoteVec.size()]);
323                                         
324         ClassInjector injector = new ClassInjector
325             (mParentLoader, mDestinationDir, TEMPLATE_PACKAGE);
326         ClassInjector mainLine = null;
327         if (mPreloadTemplates) {
328             mainLine = injector;
329         }
330
331         // Setup the tea compilers. TODO: It might make more sense for the
332
// remote compiler to merge with the file compiler.
333
FileCompiler lCompiler = new FileCompiler(localDirs,
334                                                   TEMPLATE_PACKAGE,
335                                                   mDestinationDir,
336                                                   mainLine,
337                                                   mEncoding);
338         
339         RemoteCompiler rCompiler = new RemoteCompiler(remoteDirs,
340                                                       TEMPLATE_PACKAGE,
341                                                       mDestinationDir,
342                                                       mainLine,
343                                                       mEncoding);
344            
345         lCompiler.setForceCompile(force);
346         rCompiler.setForceCompile(force);
347
348         TemplateLoadResult result = compileTemplates("local", lCompiler);
349         TemplateLoadResult remoteResult = compileTemplates("remote",rCompiler);
350         result.mergeResults(remoteResult);
351         
352         if (mRejectErrors && !result.isSuccessful()) {
353             injector = null;
354             mLog.warn("Templates not reloaded");
355             return result;
356         }
357
358         TemplateLoader loader =
359             new TemplateAdapter(mContextClass, injector, TEMPLATE_PACKAGE);
360         setTemplateLoader(loader);
361
362         String JavaDoc[] lNames, rNames;
363         try {
364             lNames = lCompiler.getAllTemplateNames();
365         }
366         catch (IOException JavaDoc e) {
367             mLog.warn(e);
368             lNames = new String JavaDoc[0];
369         }
370
371         rNames = rCompiler.getAllTemplateNames();
372         
373         String JavaDoc[] allNames = new String JavaDoc[lNames.length + rNames.length];
374         System.arraycopy(lNames, 0, allNames, 0, lNames.length);
375         System.arraycopy(rNames, 0, allNames, lNames.length, rNames.length);
376
377         boolean recompile = false;
378         if (mPreloadTemplates) {
379             for (int i=0; i<allNames.length; i++) {
380                 try {
381                     loader.getTemplate(allNames[i]);
382                 }
383                 catch (ClassNotFoundException JavaDoc e) {
384                     mLog.debug(e.toString());
385                 }
386                 catch (NoSuchMethodException JavaDoc e) {
387                     if (!force) {
388                         mLog.debug(e.toString());
389                         recompile = true;
390                         break;
391                     }
392                     else {
393                         mLog.warn(e);
394                     }
395                 }
396                 catch (LinkageError JavaDoc e) {
397                     if (!force) {
398                         mLog.debug(e.toString());
399                         recompile = true;
400                         break;
401                     }
402                     else {
403                         mLog.warn(e);
404                     }
405                 }
406             }
407         }
408
409         // If there were errors, try again with force compilation.
410
if (recompile) {
411             mLog.info("Re-compiling all templates");
412             result = loadTemplates(directories, true);
413         }
414
415         // If compile and load had no errors, reject future reloads unless
416
// error-free. This protects the system from getting into a bad
417
// state if someone recompiles a bad template in a live system.
418
if (result.isSuccessful()) {
419             mRejectErrors = true;
420         }
421
422         return result;
423     }
424
425     public synchronized TemplateLoader.Template[] getLoadedTemplates() {
426         return getTemplateLoader().getLoadedTemplates();
427     }
428
429     public synchronized TemplateLoader getTemplateLoader() {
430         if (mLoader == null) {
431             mLoader = new TemplateAdapter(mContextClass,
432                                           new ClassInjector(mParentLoader),
433                                           TEMPLATE_PACKAGE);
434         }
435         return mLoader;
436     }
437
438     private synchronized void setTemplateLoader(TemplateLoader loader) {
439         mLoader = loader;
440     }
441
442     /**
443      * Compile the tea files in the specified directory. If there were no
444      * compilation errors, then any currently loaded templates are removed so
445      * that new templates will be loaded in their place.
446      */

447     private TemplateLoadResult compileTemplates(String JavaDoc templateTypes,
448                                                 AbstractFileCompiler compiler) {
449         compiler.setRuntimeContext(mContextClass);
450         compiler.setClassLoader(mParentLoader);
451         compiler.setExceptionGuardianEnabled(mExceptionGuardian);
452
453         ErrorRetriever retriever = new ErrorRetriever();
454         compiler.addErrorListener(retriever);
455
456         // Compile tea files.
457
mLog.info("Compiling " + templateTypes + " templates...");
458         
459         String JavaDoc[] templateNames = null;
460         TemplateError[] compileErrors = null;
461         boolean successfulReload = true;
462
463         try {
464             templateNames = compiler.compileAll();
465         }
466         catch (Exception JavaDoc e) {
467             successfulReload = false;
468             mLog.error("Error compiling " + templateTypes + " templates");
469             mLog.error(e);
470         }
471         finally {
472             compileErrors = retriever.getTemplateErrors();
473             retriever.finalize();
474         }
475
476         if (templateNames != null && templateNames.length > 0) {
477             for (int i = 0; i < templateNames.length; i++) {
478                 mLog.info(templateNames[i]);
479             }
480         }
481
482         // Display compile errors.
483
int errors = compiler.getErrorCount();
484         if (errors > 0) {
485             successfulReload = false;
486             if (errors == 1) {
487                 mLog.warn(errors + " error compiling " +
488                           templateTypes + " templates");
489             }
490             else {
491                 mLog.warn(errors + " errors compiling " +
492                           templateTypes + " templates");
493             }
494         }
495         else {
496             if (templateNames == null || templateNames.length == 0) {
497                 mLog.info("No templates needed to be compiled");
498             }
499             else {
500                 mLog.info("Templates compiled successfully");
501             }
502         }
503
504         java.util.Arrays.sort(templateNames, String.CASE_INSENSITIVE_ORDER);
505
506         return new TemplateLoadResult
507             (templateNames, compileErrors, successfulReload);
508     }
509
510     public static class TemplateLoadResult {
511         private boolean mSuccess;
512         private String JavaDoc[] mReloaded;
513         private TemplateError[] mErrors;
514         
515         public TemplateLoadResult() {
516             this(new String JavaDoc[0], new TemplateError[0], true);
517         }
518
519         public TemplateLoadResult(String JavaDoc[] recompiledTemplates,
520                                   TemplateError[] errorList,
521                                   boolean successfulReload) {
522             mSuccess = successfulReload;
523             mErrors = errorList;
524             mReloaded = recompiledTemplates;
525         }
526
527         public void mergeResults(TemplateLoadResult result) {
528             mSuccess = mSuccess && result.isSuccessful();
529  
530             TemplateError[] combinedErrors = new TemplateError
531                 [mErrors.length + result.getTemplateLoadErrors().length];
532             System.arraycopy(mErrors,0,combinedErrors,0,mErrors.length);
533             System.arraycopy(result.getTemplateLoadErrors(),
534                              0,combinedErrors,mErrors.length,
535                              result.getTemplateLoadErrors().length);
536             mErrors = combinedErrors;
537             
538             String JavaDoc[] combinedReloads = new String JavaDoc
539                 [mReloaded.length + result.getReloadedTemplates().length];
540             System.arraycopy(mReloaded,0,combinedReloads,0,mReloaded.length);
541             System.arraycopy(result.getReloadedTemplates(),
542                              0,combinedReloads,mReloaded.length,
543                              result.getReloadedTemplates().length);
544             mReloaded = combinedReloads;
545         }
546         
547         public boolean isSuccessful() {
548             return mSuccess;
549         }
550
551         public String JavaDoc[] getReloadedTemplates() {
552             return mReloaded;
553         }
554
555         public TemplateError[] getTemplateLoadErrors() {
556             return mErrors;
557         }
558     }
559
560     private class ErrorRetriever implements ErrorListener {
561         private Collection JavaDoc mTemplateErrors = new ArrayList JavaDoc();
562
563         /** Reads error line from template files */
564         private LinePositionReader mOpenReader;
565
566         private CompilationUnit mOpenUnit;
567
568         public TemplateError[] getTemplateErrors() {
569             return (TemplateError[])mTemplateErrors.toArray
570                 (new TemplateError[mTemplateErrors.size()]);
571         }
572
573         /**
574          * This method is called for each error that occurs while compiling
575          * templates. The error is reported in the log and in the error list.
576          */

577         public void compileError(ErrorEvent event) {
578             mLog.warn("Error in " + event.getDetailedErrorMessage());
579
580             SourceInfo info = event.getSourceInfo();
581             if (info == null) {
582                 mTemplateErrors.add(new TemplateError(event, "", 0, 0, 0));
583                 return;
584             }
585
586             CompilationUnit unit = event.getCompilationUnit();
587             if (unit == null) {
588                 mTemplateErrors.add(new TemplateError(event, "", 0, 0, 0));
589                 return;
590             }
591
592             int line = info.getLine();
593             int errorStartPos = info.getStartPosition();
594             int errorEndPos = info.getEndPosition();
595             int detailPos = info.getDetailPosition();
596
597             try {
598                 if (mOpenReader == null ||
599                     mOpenUnit != unit ||
600                     mOpenReader.getLineNumber() >= line) {
601
602                     if (mOpenReader != null) {
603                         mOpenReader.close();
604                     }
605                     mOpenUnit = unit;
606                     mOpenReader = new LinePositionReader
607                         (new BufferedReader JavaDoc(unit.getReader()));
608                 }
609
610                 mOpenReader.skipForwardToLine(line);
611                 int linePos = mOpenReader.getNextPosition();
612
613                 String JavaDoc lineStr = mOpenReader.readLine();
614                 lineStr = mOpenReader.cleanWhitespace(lineStr);
615
616                 TemplateError te = new TemplateError
617                     (event, lineStr,
618                      errorStartPos - linePos,
619                      detailPos - linePos,
620                      errorEndPos - linePos);
621
622                 mTemplateErrors.add(te);
623             }
624             catch (IOException JavaDoc ex) {
625                 mTemplateErrors.add(new TemplateError(event, "", 0, 0, 0));
626                 mLog.error(ex);
627             }
628         }
629
630         public void finalize() {
631             // Close the template error reader.
632
if (mOpenReader != null) {
633                 try {
634                     mOpenReader.close();
635                 }
636                 catch (IOException JavaDoc e) {
637                     mLog.error(e);
638                 }
639             }
640             mOpenReader = null;
641             mOpenUnit = null;
642         }
643     }
644 }
645
Popular Tags