KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > generation > VelocityGenerator


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.generation;
17
18 import java.beans.PropertyDescriptor JavaDoc;
19 import java.io.BufferedReader JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.StringReader JavaDoc;
23 import java.io.StringWriter JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Set JavaDoc;
31
32 import org.apache.avalon.framework.activity.Initializable;
33 import org.apache.avalon.framework.configuration.Configurable;
34 import org.apache.avalon.framework.configuration.Configuration;
35 import org.apache.avalon.framework.configuration.ConfigurationException;
36 import org.apache.avalon.framework.context.ContextException;
37 import org.apache.avalon.framework.context.DefaultContext;
38 import org.apache.avalon.framework.parameters.Parameters;
39 import org.apache.avalon.framework.service.ServiceException;
40 import org.apache.cocoon.ProcessingException;
41 import org.apache.cocoon.ResourceNotFoundException;
42 import org.apache.cocoon.components.flow.FlowHelper;
43 import org.apache.cocoon.components.flow.WebContinuation;
44 import org.apache.cocoon.environment.ObjectModelHelper;
45 import org.apache.cocoon.environment.Request;
46 import org.apache.cocoon.environment.Response;
47 import org.apache.cocoon.environment.Session;
48 import org.apache.cocoon.environment.SourceResolver;
49 import org.apache.commons.collections.ExtendedProperties;
50 import org.apache.commons.jxpath.DynamicPropertyHandler;
51 import org.apache.commons.jxpath.JXPathBeanInfo;
52 import org.apache.commons.jxpath.JXPathIntrospector;
53 import org.apache.commons.lang.StringUtils;
54 import org.apache.excalibur.source.Source;
55 import org.apache.excalibur.xml.sax.SAXParser;
56 import org.apache.velocity.VelocityContext;
57 import org.apache.velocity.app.VelocityEngine;
58 import org.apache.velocity.context.Context;
59 import org.apache.velocity.runtime.RuntimeConstants;
60 import org.apache.velocity.runtime.RuntimeServices;
61 import org.apache.velocity.runtime.log.LogSystem;
62 import org.apache.velocity.runtime.resource.Resource;
63 import org.apache.velocity.util.introspection.Info;
64 import org.apache.velocity.util.introspection.UberspectImpl;
65 import org.apache.velocity.util.introspection.VelMethod;
66 import org.apache.velocity.util.introspection.VelPropertyGet;
67 import org.apache.velocity.util.introspection.VelPropertySet;
68 import org.mozilla.javascript.JavaScriptException;
69 import org.mozilla.javascript.NativeArray;
70 import org.mozilla.javascript.ScriptRuntime;
71 import org.mozilla.javascript.Scriptable;
72 import org.mozilla.javascript.ScriptableObject;
73 import org.mozilla.javascript.Undefined;
74 import org.mozilla.javascript.Wrapper;
75 import org.xml.sax.InputSource JavaDoc;
76 import org.xml.sax.SAXException JavaDoc;
77 import org.xml.sax.SAXParseException JavaDoc;
78
79 /**
80  * <p>Cocoon {@link Generator} that produces dynamic XML SAX events
81  * from a Velocity template file.</p>
82  * If called from a Flowscript, the immediate properties of the context object from the Flowscript are available in the Velocity context.
83  * In that case, the current Web Continuation from the Flowscript
84  * is also available as a variable named <code>continuation</code>. You would
85  * typically access its <code>id</code>:
86  * <p><pre>
87  * &lt;form action="$continuation.id"&gt;
88  * </pre></p>
89  * <p>You can also reach previous continuations by using the <code>getContinuation()</code> function:</p>
90  * <p><pre>
91  * &lt;form action="$continuation.getContinuation(1).id}" >
92  * </pre></p>
93  *
94  * In addition the following implicit objects are always available in
95  * the Velocity context:
96  * <p>
97  * <dl>
98  * <dt><code>request</code> (<code>org.apache.cocoon.environment.Request</code>)</dt>
99  * <dd>The Cocoon current request</dd>
100  *
101  * <dt><code>response</code> (<code>org.apache.cocoon.environment.Response</code>)</dt>
102  * <dd>The Cocoon response associated with the current request</dd>
103  *
104  * <dt><code>session</code> (<code>org.apache.cocoon.environment.Session</code>)</dt>
105  * <dd>The Cocoon session associated with the current request</dd>
106  *
107  * <dt><code>context</code> (<code>org.apache.cocoon.environment.Context</code>)</dt>
108  * <dd>The Cocoon context associated with the current request</dd>
109  *
110  * <dt><code>parameters</code> (<code>org.apache.avalon.framework.parameters.Parameters</code>)</dt>
111  * <dd>Any parameters passed to the generator in the pipeline</dd>
112  * </dl>
113  * </p>
114  *
115  *
116  * <h2>Sitemap Configuration</h2>
117  *
118  * <p>
119  * Attributes:
120  * <dl>
121  * <dt>usecache (optional; default: 'false')</dt>
122  * <dd>set to 'true' to enable template caching on the 'cocoon'
123  * resource loader</dd>
124  *
125  * <dt>checkInterval (optional; default: '0')</dt>
126  * <dd>This is the number of seconds between modification checks when
127  * caching is turned on. When this is an integer &gt; 0, this represents
128  * the number of seconds between checks to see if the template was
129  * modified. If the template has been modified since last check, then
130  * it is reloaded and reparsed. Otherwise nothing is done. When &lt;= 0,
131  * no modification checks will take place, and assuming that the
132  * property cache (above) is true, once a template is loaded and
133  * parsed the first time it is used, it will not be checked or
134  * reloaded after that until the application or servlet engine is
135  * restarted.</dd>
136  * </dl>
137  * </p>
138  *
139  * <p>
140  * Child Elements:
141  *
142  * <dl>
143  * <dt>&lt;property name="propertyName" value="propertyValue"/&gt; (optional; 0..n)</dt>
144  * <dd>An additional property to pass along to the Velocity template
145  * engine during initialization</dd>
146  *
147  * <dt>&lt;resource-loader name="loaderName" class="javaClassName" &gt; (optional; 0..n; children: property*)</dt>
148  * <dd>The default configuration uses the 'cocoon' resource loader
149  * which resolves resources via the Cocoon SourceResolver. Additional
150  * resource loaders can be added with this configuration
151  * element. Configuration properties for the resource loader can be
152  * specified by adding a child property element of the resource-loader
153  * element. The prefix '&lt;name&gt;.resource.loader.' is
154  * automatically added to the property name.</dd>
155  *
156  * @version CVS $Id: VelocityGenerator.java 123808 2004-12-31 16:23:30Z antonio $
157  */

158 public class VelocityGenerator extends ServiceableGenerator
159         implements Initializable, Configurable, LogSystem {
160
161     /**
162      * <p>Velocity context implementation specific to the Servlet environment.</p>
163      *
164      * <p>It provides the following special features:</p>
165      * <ul>
166      * <li>puts the request, response, session, and servlet context objects
167      * into the Velocity context for direct access, and keeps them
168      * read-only</li>
169      * <li>supports a read-only toolbox of view tools</li>
170      * <li>auto-searches servlet request attributes, session attributes and
171      * servlet context attribues for objects</li>
172      * </ul>
173      *
174      * <p>The {@link #internalGet(String key)} method implements the following search order
175      * for objects:</p>
176      * <ol>
177      * <li>servlet request, servlet response, servlet session, servlet context</li>
178      * <li>toolbox</li>
179      * <li>local hashtable of objects (traditional use)</li>
180      * <li>servlet request attribues, servlet session attribute, servlet context
181      * attributes</li>
182      * </ol>
183      *
184      * <p>The purpose of this class is to make it easy for web designer to work
185      * with Java servlet based web applications. They do not need to be concerned
186      * with the concepts of request, session or application attributes and the
187      * live time of objects in these scopes.</p>
188      *
189      * <p>Note that the put() method always puts objects into the local hashtable.
190      * </p>
191      *
192      * <p>Acknowledge: the source code is borrowed from the jakarta-velocity-tools
193      * project with slight modifications.</p>
194      *
195      * @author <a HREF="mailto:albert@charcoalgeneration.com">Albert Kwong</a>
196      * @author <a HREF="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
197      * @author <a HREF="mailto:sidler@teamup.com">Gabe Sidler</a>
198      * @author <a HREF="mailto:albert@charcoalgeneration.com">Albert Kwong</a>
199      */

200     public static class ChainedContext extends VelocityContext
201     {
202         
203         /**
204          * A local reference to the current servlet request.
205          */

206         private Request request;
207         
208         /**
209          * A local reference to the current servlet response.
210          */

211         private Response response;
212         
213         /**
214          * A local reference to the servlet session.
215          */

216         private Session session;
217         
218         /**
219          * A local reference to the servlet context.
220          */

221         private org.apache.cocoon.environment.Context application;
222         
223         /**
224          * A local reference to pipeline parameters.
225          */

226         private Parameters parameters;
227         
228         /**
229          * Key to the HTTP request object.
230          */

231         public static final String JavaDoc REQUEST = "request";
232         
233         /**
234          * Key to the HTTP response object.
235          */

236         public static final String JavaDoc RESPONSE = "response";
237         
238         /**
239          * Key to the HTTP session object.
240          */

241         public static final String JavaDoc SESSION = "session";
242         
243         /**
244          * Key to the servlet context object.
245          */

246         public static final String JavaDoc APPLICATION = "context";
247         
248         /**
249          * Key to the servlet context object.
250          */

251         public static final String JavaDoc PARAMETERS = "parameters";
252         
253         
254         /**
255          * Default constructor.
256          */

257         public ChainedContext(org.apache.velocity.context.Context ctx,
258                               Request request,
259                               Response response,
260                               org.apache.cocoon.environment.Context application,
261                               Parameters parameters)
262         {
263             super(null, ctx);
264             this.request = request;
265             this.response = response;
266             this.session = request.getSession(false);
267             this.application = application;
268             this.parameters = parameters;
269         }
270         
271         
272         /**
273          * <p>Looks up and returns the object with the specified key.</p>
274          *
275          * <p>See the class documentation for more details.</p>
276          *
277          * @param key the key of the object requested
278          *
279          * @return the requested object or null if not found
280          */

281         public Object JavaDoc internalGet( String JavaDoc key )
282         {
283             // make the four scopes of the Apocalypse Read only
284
if ( key.equals( REQUEST ))
285                 {
286                     return request;
287                 }
288             else if( key.equals(RESPONSE) )
289                 {
290                     return response;
291                 }
292             else if ( key.equals(SESSION) )
293                 {
294                     return session;
295                 }
296             else if ( key.equals(APPLICATION))
297                 {
298                     return application;
299                 }
300             else if ( key.equals(PARAMETERS))
301                 {
302                     return parameters;
303                 }
304             
305             Object JavaDoc o = null;
306             
307             // try the local hashtable
308
o = super.internalGet( key );
309             
310             // if not found, wander down the scopes...
311
if (o == null)
312                 {
313                     o = request.getAttribute( key );
314                     
315                     if ( o == null )
316                         {
317                             if ( session != null )
318                                 {
319                                     o = session.getAttribute( key );
320                                 }
321                             
322                             if ( o == null )
323                                 {
324                                     o = application.getAttribute( key );
325                                 }
326                         }
327                 }
328             
329             return o;
330         }
331
332         
333     } // ChainedContext
334

335     /**
336      * Velocity Introspector that supports Rhino JavaScript objects
337      * as well as Java Objects
338      *
339      */

340     public static class JSIntrospector extends UberspectImpl {
341         
342         public static class JSMethod implements VelMethod {
343             
344             Scriptable scope;
345             String JavaDoc name;
346             
347             public JSMethod(Scriptable scope, String JavaDoc name) {
348                 this.scope = scope;
349                 this.name = name;
350             }
351             
352             public Object JavaDoc invoke(Object JavaDoc thisArg, Object JavaDoc[] args)
353                 throws Exception JavaDoc {
354                 org.mozilla.javascript.Context cx = org.mozilla.javascript.Context.enter();
355                 try {
356                     Object JavaDoc result;
357                     Scriptable thisObj;
358                     if (!(thisArg instanceof Scriptable)) {
359                         thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope);
360                     } else {
361                         thisObj = (Scriptable)thisArg;
362                     }
363                     result = ScriptableObject.getProperty(thisObj, name);
364                     Object JavaDoc[] newArgs = null;
365                     if (args != null) {
366                         newArgs = new Object JavaDoc[args.length];
367                         for (int i = 0; i < args.length; i++) {
368                             newArgs[i] = args[i];
369                             if (args[i] != null &&
370                                 !(args[i] instanceof Number JavaDoc) &&
371                                 !(args[i] instanceof Boolean JavaDoc) &&
372                                 !(args[i] instanceof String JavaDoc) &&
373                                 !(args[i] instanceof Scriptable)) {
374                                 newArgs[i] = org.mozilla.javascript.Context.toObject(args[i], scope);
375                             }
376                         }
377                     }
378                     result = ScriptRuntime.call(cx, result, thisObj,
379                                                 newArgs, scope);
380                     if (result == Undefined.instance ||
381                         result == Scriptable.NOT_FOUND) {
382                         result = null;
383                     } else while (result instanceof Wrapper) {
384                         result = ((Wrapper)result).unwrap();
385                     }
386                     return result;
387                 } catch (JavaScriptException e) {
388                     throw new java.lang.reflect.InvocationTargetException JavaDoc(e);
389                 } finally {
390                     org.mozilla.javascript.Context.exit();
391                 }
392             }
393             
394             public boolean isCacheable() {
395                 return false;
396             }
397             
398             public String JavaDoc getMethodName() {
399                 return name;
400             }
401             
402             public Class JavaDoc getReturnType() {
403                 return Object JavaDoc.class;
404             }
405             
406         }
407         
408         public static class JSPropertyGet implements VelPropertyGet {
409             
410             Scriptable scope;
411             String JavaDoc name;
412             
413             public JSPropertyGet(Scriptable scope, String JavaDoc name) {
414                 this.scope = scope;
415                 this.name = name;
416             }
417             
418             public Object JavaDoc invoke(Object JavaDoc thisArg) throws Exception JavaDoc {
419                 org.mozilla.javascript.Context.enter();
420                 try {
421                     Scriptable thisObj;
422                     if (!(thisArg instanceof Scriptable)) {
423                         thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope);
424                     } else {
425                         thisObj = (Scriptable)thisArg;
426                     }
427                     Object JavaDoc result = ScriptableObject.getProperty(thisObj, name);
428                     if (result == Undefined.instance ||
429                         result == Scriptable.NOT_FOUND) {
430                         result = null;
431                     } else while (result instanceof Wrapper) {
432                         result = ((Wrapper)result).unwrap();
433                     }
434                     return result;
435                 } finally {
436                     org.mozilla.javascript.Context.exit();
437                 }
438             }
439             
440             public boolean isCacheable() {
441                 return false;
442             }
443             
444             public String JavaDoc getMethodName() {
445                 return name;
446             }
447             
448         }
449         
450         public static class JSPropertySet implements VelPropertySet {
451             
452             Scriptable scope;
453             String JavaDoc name;
454             
455             public JSPropertySet(Scriptable scope, String JavaDoc name) {
456                 this.scope = scope;
457                 this.name = name;
458             }
459             
460             public Object JavaDoc invoke(Object JavaDoc thisArg, Object JavaDoc rhs) throws Exception JavaDoc {
461                 org.mozilla.javascript.Context.enter();
462                 try {
463                     Scriptable thisObj;
464                     Object JavaDoc arg = rhs;
465                     if (!(thisArg instanceof Scriptable)) {
466                         thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope);
467                     } else {
468                         thisObj = (Scriptable)thisArg;
469                     }
470                     if (arg != null &&
471                         !(arg instanceof Number JavaDoc) &&
472                         !(arg instanceof Boolean JavaDoc) &&
473                         !(arg instanceof String JavaDoc) &&
474                         !(arg instanceof Scriptable)) {
475                         arg = org.mozilla.javascript.Context.toObject(arg, scope);
476                     }
477                     ScriptableObject.putProperty(thisObj, name, arg);
478                     return rhs;
479                 } finally {
480                     org.mozilla.javascript.Context.exit();
481                 }
482             }
483             
484             public boolean isCacheable() {
485                 return false;
486             }
487             
488             public String JavaDoc getMethodName() {
489                 return name;
490             }
491         }
492         
493         public static class NativeArrayIterator implements Iterator JavaDoc {
494             
495             NativeArray arr;
496             int index;
497             
498             public NativeArrayIterator(NativeArray arr) {
499                 this.arr = arr;
500                 this.index = 0;
501             }
502             
503             public boolean hasNext() {
504                 return index < (int)arr.jsGet_length();
505             }
506             
507             public Object JavaDoc next() {
508                 org.mozilla.javascript.Context.enter();
509                 try {
510                     Object JavaDoc result = arr.get(index++, arr);
511                     if (result == Undefined.instance ||
512                         result == Scriptable.NOT_FOUND) {
513                         result = null;
514                     } else while (result instanceof Wrapper) {
515                         result = ((Wrapper)result).unwrap();
516                     }
517                     return result;
518                 } finally {
519                     org.mozilla.javascript.Context.exit();
520                 }
521             }
522             
523             public void remove() {
524                 arr.delete(index);
525             }
526         }
527         
528         public static class ScriptableIterator implements Iterator JavaDoc {
529             
530             Scriptable scope;
531             Object JavaDoc[] ids;
532             int index;
533             
534             public ScriptableIterator(Scriptable scope) {
535                 this.scope = scope;
536                 this.ids = scope.getIds();
537                 this.index = 0;
538             }
539             
540             public boolean hasNext() {
541                 return index < ids.length;
542             }
543             
544             public Object JavaDoc next() {
545                 org.mozilla.javascript.Context.enter();
546                 try {
547                     Object JavaDoc result =
548                         ScriptableObject.getProperty(scope,
549                                                      ids[index++].toString());
550                     if (result == Undefined.instance ||
551                         result == Scriptable.NOT_FOUND) {
552                         result = null;
553                     } else while (result instanceof Wrapper) {
554                         result = ((Wrapper)result).unwrap();
555                     }
556                     return result;
557                 } finally {
558                     org.mozilla.javascript.Context.exit();
559                 }
560             }
561             
562             public void remove() {
563                 org.mozilla.javascript.Context.enter();
564                 try {
565                     scope.delete(ids[index].toString());
566                 } finally {
567                     org.mozilla.javascript.Context.exit();
568                 }
569             }
570         }
571         
572         public Iterator JavaDoc getIterator(Object JavaDoc obj, Info i)
573             throws Exception JavaDoc {
574             if (!(obj instanceof Scriptable)) {
575                 return super.getIterator(obj, i);
576             }
577             if (obj instanceof NativeArray) {
578                 return new NativeArrayIterator((NativeArray)obj);
579             }
580             return new ScriptableIterator((Scriptable)obj);
581         }
582         
583         public VelMethod getMethod(Object JavaDoc obj, String JavaDoc methodName,
584                                    Object JavaDoc[] args, Info i)
585             throws Exception JavaDoc {
586             if (!(obj instanceof Scriptable)) {
587                 return super.getMethod(obj, methodName, args, i);
588             }
589             return new JSMethod((Scriptable)obj, methodName);
590         }
591         
592         public VelPropertyGet getPropertyGet(Object JavaDoc obj, String JavaDoc identifier,
593                                              Info i)
594             throws Exception JavaDoc {
595             if (!(obj instanceof Scriptable)) {
596                 return super.getPropertyGet(obj, identifier, i);
597             }
598             return new JSPropertyGet((Scriptable)obj, identifier);
599         }
600         
601         public VelPropertySet getPropertySet(Object JavaDoc obj, String JavaDoc identifier,
602                                              Object JavaDoc arg, Info i)
603             throws Exception JavaDoc {
604             if (!(obj instanceof Scriptable)) {
605                 return super.getPropertySet(obj, identifier, arg, i);
606             }
607             return new JSPropertySet((Scriptable)obj, identifier);
608         }
609     }
610
611     /**
612      * Velocity {@link org.apache.velocity.runtime.resource.loader.ResourceLoader}
613      * implementation to load template resources using Cocoon's
614      *{@link SourceResolver}. This class is created by the Velocity
615      * framework via the ResourceLoaderFactory.
616      *
617      * @see org.apache.velocity.runtime.resource.loader.ResourceLoader
618      */

619     public static class TemplateLoader
620             extends org.apache.velocity.runtime.resource.loader.ResourceLoader {
621
622         private org.apache.avalon.framework.context.Context resolverContext;
623
624         /**
625          * Initialize this resource loader. The 'context' property is
626          * required and must be of type {@link Context}. The context
627          * is used to pass the Cocoon SourceResolver for the current
628          * pipeline.
629          *
630          * @param config the properties to configure this resource.
631          * @throws IllegalArgumentException thrown if the required
632          * 'context' property is not set.
633          * @throws ClassCastException if the 'context' property is not
634          * of type {@link org.apache.avalon.framework.context.Context}.
635          * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(ExtendedProperties)
636          */

637         public void init(ExtendedProperties config) {
638             this.resolverContext = (org.apache.avalon.framework.context.Context) config.get("context");
639             if (this.resolverContext == null) {
640                 throw new IllegalArgumentException JavaDoc("Runtime Cocoon resolver context not specified in resource loader configuration.");
641             }
642         }
643
644         /**
645          * @param systemId the path to the resource
646          * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getResourceStream(String)
647          */

648         public InputStream JavaDoc getResourceStream(String JavaDoc systemId)
649                 throws org.apache.velocity.exception.ResourceNotFoundException {
650             try {
651                 return resolveSource(systemId).getInputStream();
652             } catch (org.apache.velocity.exception.ResourceNotFoundException ex) {
653                 throw ex;
654             } catch (Exception JavaDoc ex) {
655                 throw new org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: " + ex);
656             }
657         }
658
659         /**
660          * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#isSourceModified(Resource)
661          */

662         public boolean isSourceModified(Resource resource) {
663             long lastModified = 0;
664             try {
665                 lastModified = resolveSource(resource.getName()).getLastModified();
666             } catch (Exception JavaDoc ex) {
667                 super.rsvc.warn("Unable to determine last modified for resource: "
668                                 + resource.getName() + ": " + ex);
669             }
670
671             return lastModified > 0 ? lastModified != resource.getLastModified() : true;
672         }
673
674         /**
675          * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getLastModified(Resource)
676          */

677         public long getLastModified(Resource resource) {
678             long lastModified = 0;
679             try {
680                 lastModified = resolveSource(resource.getName()).getLastModified();
681             } catch (Exception JavaDoc ex) {
682                 super.rsvc.warn("Unable to determine last modified for resource: "
683                                 + resource.getName() + ": " + ex);
684             }
685
686             return lastModified;
687         }
688
689         /**
690          * Store all the Source objects we lookup via the SourceResolver so that they can be properly
691          * recycled later.
692          *
693          * @param systemId the path to the resource
694          */

695         private Source resolveSource(String JavaDoc systemId) throws org.apache.velocity.exception.ResourceNotFoundException {
696             Map JavaDoc sourceCache;
697             try {
698                 sourceCache = (Map JavaDoc) this.resolverContext.get(CONTEXT_SOURCE_CACHE_KEY);
699             } catch (ContextException ignore) {
700                 throw new org.apache.velocity.exception.ResourceNotFoundException("Runtime Cocoon source cache not specified in resource loader resolver context.");
701             }
702
703             Source source = (Source) sourceCache.get(systemId);
704             if (source == null) {
705                 try {
706                     SourceResolver resolver = (SourceResolver) this.resolverContext.get(CONTEXT_RESOLVER_KEY);
707                     source = resolver.resolveURI(systemId);
708                 } catch (ContextException ex) {
709                     throw new org.apache.velocity.exception.ResourceNotFoundException("No Cocoon source resolver associated with current request.");
710                 } catch (Exception JavaDoc ex) {
711                     throw new org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: " + ex);
712                 }
713             }
714
715             sourceCache.put(systemId, source);
716             return source;
717         }
718     }
719
720     /**
721      * Key to lookup the {@link SourceResolver} from the context of
722      * the resource loader
723      */

724     final private static String JavaDoc CONTEXT_RESOLVER_KEY = "resolver";
725
726     /**
727      * Key to lookup the source cache {@link Map} from the context of
728      * the resource loader
729      */

730     final private static String JavaDoc CONTEXT_SOURCE_CACHE_KEY = "source-cache";
731
732     private VelocityEngine tmplEngine;
733     private boolean tmplEngineInitialized;
734     private DefaultContext resolverContext;
735     private Context velocityContext;
736     private boolean activeFlag;
737
738     /**
739      * Read any additional objects to export to the Velocity context
740      * from the configuration.
741      *
742      * @param configuration the class configurations.
743      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
744      */

745     public void configure(Configuration configuration) throws ConfigurationException {
746         this.resolverContext = new DefaultContext();
747         this.tmplEngine = new VelocityEngine();
748
749         // Set up a JavaScript introspector for the Cocoon flow layer
750
this.tmplEngine.setProperty(org.apache.velocity.runtime.RuntimeConstants.UBERSPECT_CLASSNAME,
751                                     JSIntrospector.class.getName());
752         this.tmplEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, this);
753
754         // First set up our default 'cocoon' resource loader
755
this.tmplEngine.setProperty("cocoon.resource.loader.class",
756                                     TemplateLoader.class.getName());
757         this.tmplEngine.setProperty("cocoon.resource.loader.cache",
758                                     configuration.getAttribute("usecache", "false"));
759         this.tmplEngine.setProperty("cocoon.resource.loader.modificationCheckInterval",
760                                     configuration.getAttribute("checkInterval", "0"));
761         this.tmplEngine.setProperty("cocoon.resource.loader.context",
762                                     this.resolverContext);
763
764         // Read in any additional properties to pass to the VelocityEngine during initialization
765
Configuration[] properties = configuration.getChildren("property");
766         for (int i = 0; i < properties.length; ++i) {
767             Configuration c = properties[i];
768             String JavaDoc name = c.getAttribute("name");
769
770             // Disallow setting of certain properties
771
if (name.startsWith("runtime.log")
772                     || name.indexOf(".resource.loader.") != -1) {
773                 if (getLogger().isInfoEnabled()) {
774                     getLogger().info("ignoring disallowed property '" + name + "'.");
775                 }
776                 continue;
777             }
778             this.tmplEngine.setProperty(name, c.getAttribute("value"));
779         }
780
781         // Now read in any additional Velocity resource loaders
782
List JavaDoc resourceLoaders = new ArrayList JavaDoc();
783         Configuration[] loaders = configuration.getChildren("resource-loader");
784         for (int i = 0; i < loaders.length; ++i) {
785             Configuration loader = loaders[i];
786             String JavaDoc name = loader.getAttribute("name");
787             if (name.equals("cocoon")) {
788                 if (getLogger().isInfoEnabled()) {
789                     getLogger().info("'cocoon' resource loader already defined.");
790                 }
791                 continue;
792             }
793             resourceLoaders.add(name);
794             String JavaDoc prefix = name + ".resource.loader.";
795             String JavaDoc type = loader.getAttribute("class");
796             this.tmplEngine.setProperty(prefix + "class", type);
797             Configuration[] loaderProperties = loader.getChildren("property");
798             for (int j = 0; j < loaderProperties.length; j++) {
799                 Configuration c = loaderProperties[j];
800                 String JavaDoc propName = c.getAttribute("name");
801                 this.tmplEngine.setProperty(prefix + propName, c.getAttribute("value"));
802             }
803         }
804
805         // Velocity expects resource loaders as CSV list
806
//
807
StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("cocoon");
808         for (Iterator JavaDoc it = resourceLoaders.iterator(); it.hasNext();) {
809             buffer.append(',');
810             buffer.append((String JavaDoc) it.next());
811         }
812         tmplEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, buffer.toString());
813     }
814
815     /**
816      * @see org.apache.avalon.framework.activity.Initializable#initialize()
817      */

818     public void initialize() throws Exception JavaDoc {
819         //this.tmplEngine.init();
820
}
821
822     /**
823      * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(SourceResolver, Map, String, Parameters)
824      */

825     public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src, Parameters params)
826             throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
827         if (activeFlag) {
828             throw new IllegalStateException JavaDoc("setup called on recyclable sitemap component before properly recycling previous state");
829         }
830
831         super.setup(resolver, objectModel, src, params);
832
833         // Pass along the SourceResolver to the Velocity resource loader
834
this.resolverContext.put(CONTEXT_RESOLVER_KEY, resolver);
835         this.resolverContext.put(CONTEXT_SOURCE_CACHE_KEY, new HashMap JavaDoc());
836
837         // FIXME: Initialize the Velocity context. Use objectModel to pass these
838
final Object JavaDoc bean = FlowHelper.getContextObject(objectModel);
839         if (bean != null) {
840
841             final WebContinuation kont = FlowHelper.getWebContinuation(objectModel);
842
843             // Hack? I use JXPath to determine the properties of the bean object
844
final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
845             DynamicPropertyHandler h = null;
846             final PropertyDescriptor JavaDoc[] props;
847             if (bi.isDynamic()) {
848                 Class JavaDoc cl = bi.getDynamicPropertyHandlerClass();
849                 try {
850                     h = (DynamicPropertyHandler) cl.newInstance();
851                 } catch (Exception JavaDoc exc) {
852                     exc.printStackTrace();
853                     h = null;
854                 }
855                 props = null;
856             } else {
857                 h = null;
858                 props = bi.getPropertyDescriptors();
859             }
860             final DynamicPropertyHandler handler = h;
861             
862             this.velocityContext = new Context() {
863                     public Object JavaDoc put(String JavaDoc key, Object JavaDoc value) {
864                         if (key.equals("flowContext")
865                             || key.equals("continuation")) {
866                             return value;
867                         }
868                         if (handler != null) {
869                             handler.setProperty(bean, key, value);
870                             return value;
871                         } else {
872                             for (int i = 0; i < props.length; i++) {
873                                 if (props[i].getName().equals(key)) {
874                                     try {
875                                         return props[i].getWriteMethod().invoke(bean, new Object JavaDoc[]{value});
876                                     } catch (Exception JavaDoc ignored) {
877                                         break;
878                                     }
879                                 }
880                             }
881                             return value;
882                         }
883                     }
884                     
885                     public boolean containsKey(Object JavaDoc key) {
886                         if (key.equals("flowContext")
887                             || key.equals("continuation")) {
888                             return true;
889                         }
890                         if (handler != null) {
891                             String JavaDoc[] result = handler.getPropertyNames(bean);
892                             for (int i = 0; i < result.length; i++) {
893                                 if (key.equals(result[i])) {
894                                     return true;
895                                 }
896                             }
897                         } else {
898                             for (int i = 0; i < props.length; i++) {
899                                 if (key.equals(props[i].getName())) {
900                                     return true;
901                                 }
902                             }
903                         }
904                         return false;
905                     }
906                     
907                     public Object JavaDoc[] getKeys() {
908                         Object JavaDoc[] result = null;
909                         if (handler != null) {
910                             result = handler.getPropertyNames(bean);
911                         } else {
912                             result = new Object JavaDoc[props.length];
913                             for (int i = 0; i < props.length; i++) {
914                                 result[i] = props[i].getName();
915                             }
916                         }
917                         Set JavaDoc set = new HashSet JavaDoc();
918                         for (int i = 0; i < result.length; i++) {
919                             set.add(result[i]);
920                         }
921                         set.add("flowContext");
922                         set.add("continuation");
923                         result = new Object JavaDoc[set.size()];
924                         set.toArray(result);
925                         return result;
926                     }
927                     
928                     public Object JavaDoc get(String JavaDoc key) {
929                         if (key.equals("flowContext")) {
930                             return bean;
931                         }
932                         if (key.equals("continuation")) {
933                             return kont;
934                         }
935                         if (handler != null) {
936                             return handler.getProperty(bean, key);
937                         } else {
938                             for (int i = 0; i < props.length; i++) {
939                                 if (props[i].getName().equals(key)) {
940                                     try {
941                                         return props[i].getReadMethod().invoke(bean, null);
942                                     } catch (Exception JavaDoc ignored) {
943                                         break;
944                                     }
945                                 }
946                             }
947                             return null;
948                         }
949                     }
950                     
951                     public Object JavaDoc remove(Object JavaDoc key) {
952                         // not implemented
953
return key;
954                     }
955                 };
956         }
957         this.velocityContext =
958             new ChainedContext (this.velocityContext,
959                                 ObjectModelHelper.getRequest(objectModel),
960                                 ObjectModelHelper.getResponse(objectModel),
961                                 ObjectModelHelper.getContext(objectModel),
962                                 params);
963         this.velocityContext.put("template", src);
964         this.activeFlag = true;
965     }
966     /**
967      * Free up the VelocityContext associated with the pipeline, and
968      * release any Source objects resolved by the resource loader.
969      *
970      * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
971      */

972     public void recycle() {
973         this.activeFlag = false;
974
975         // Recycle all the Source objects resolved/used by our resource loader
976
try {
977             Map JavaDoc sourceCache = (Map JavaDoc) this.resolverContext.get(CONTEXT_SOURCE_CACHE_KEY);
978             for (Iterator JavaDoc it = sourceCache.values().iterator(); it.hasNext();) {
979                 this.resolver.release((Source) it.next());
980             }
981         } catch (ContextException ignore) {
982         }
983
984         this.velocityContext = null;
985         super.recycle();
986     }
987
988     /**
989      * Generate XML data using Velocity template.
990      *
991      * @see org.apache.cocoon.generation.Generator#generate()
992      */

993     public void generate()
994             throws IOException JavaDoc, SAXException JavaDoc, ProcessingException {
995         // Guard against calling generate before setup.
996
if (!activeFlag) {
997             throw new IllegalStateException JavaDoc("generate called on sitemap component before setup.");
998         }
999
1000        SAXParser parser = null;
1001        StringWriter JavaDoc w = new StringWriter JavaDoc();
1002        try {
1003            parser = (SAXParser) this.manager.lookup(SAXParser.ROLE);
1004            if (getLogger().isDebugEnabled()) {
1005                getLogger().debug("Processing File: " + super.source);
1006            }
1007            if (!tmplEngineInitialized) {
1008                tmplEngine.init();
1009                tmplEngineInitialized = true;
1010            }
1011            /* lets render a template */
1012            this.tmplEngine.mergeTemplate(super.source, velocityContext, w);
1013
1014            InputSource JavaDoc xmlInput =
1015                    new InputSource JavaDoc(new StringReader JavaDoc(w.toString()));
1016            xmlInput.setSystemId(super.source);
1017            parser.parse(xmlInput, this.xmlConsumer);
1018        } catch (IOException JavaDoc e) {
1019            getLogger().warn("VelocityGenerator.generate()", e);
1020            throw new ResourceNotFoundException("Could not get Resource for VelocityGenerator", e);
1021        } catch (SAXParseException JavaDoc e) {
1022            int line = e.getLineNumber();
1023            int column = e.getColumnNumber();
1024            if (line <= 0) {
1025                line = Integer.MAX_VALUE;
1026            }
1027            BufferedReader JavaDoc reader =
1028                new BufferedReader JavaDoc(new StringReader JavaDoc(w.toString()));
1029            StringBuffer JavaDoc message = new StringBuffer JavaDoc(e.getMessage());
1030            message.append(" In generated document:\n");
1031            for (int i = 0; i < line; i++) {
1032                String JavaDoc lineStr = reader.readLine();
1033                if (lineStr == null) {
1034                    break;
1035                }
1036                message.append(lineStr);
1037                message.append("\n");
1038            }
1039            if (column > 0) {
1040                message.append(StringUtils.leftPad("^\n", column + 1));
1041            }
1042            SAXException JavaDoc pe = new SAXParseException JavaDoc(message.toString(),
1043                                                    e.getPublicId(),
1044                                                    "(Document generated from template "+e.getSystemId() + ")",
1045                                                    e.getLineNumber(),
1046                                                    e.getColumnNumber(),
1047                                                    null);
1048            getLogger().error("VelocityGenerator.generate()", pe);
1049            throw pe;
1050        } catch (SAXException JavaDoc e) {
1051            getLogger().error("VelocityGenerator.generate()", e);
1052            throw e;
1053        } catch (ServiceException e) {
1054            getLogger().error("Could not get parser", e);
1055            throw new ProcessingException("Exception in VelocityGenerator.generate()", e);
1056        } catch (ProcessingException e) {
1057            throw e;
1058        } catch (Exception JavaDoc e) {
1059            getLogger().error("Could not get parser", e);
1060            throw new ProcessingException("Exception in VelocityGenerator.generate()", e);
1061        } finally {
1062            this.manager.release(parser);
1063        }
1064    }
1065
1066
1067    /**
1068     * This implementation does nothing.
1069     *
1070     * @see org.apache.velocity.runtime.log.LogSystem#init(RuntimeServices)
1071     */

1072    public void init(RuntimeServices rs) throws Exception JavaDoc {
1073    }
1074
1075    /**
1076     * Pass along Velocity log messages to our configured logger.
1077     *
1078     * @see org.apache.velocity.runtime.log.LogSystem#logVelocityMessage(int, String)
1079     */

1080    public void logVelocityMessage(int level, String JavaDoc message) {
1081        switch (level) {
1082            case LogSystem.WARN_ID:
1083                getLogger().warn(message);
1084                break;
1085            case LogSystem.INFO_ID:
1086                getLogger().info(message);
1087                break;
1088            case LogSystem.DEBUG_ID:
1089                getLogger().debug(message);
1090                break;
1091            case LogSystem.ERROR_ID:
1092                getLogger().error(message);
1093                break;
1094            default :
1095                getLogger().info(message);
1096                break;
1097        }
1098    }
1099}
1100
Popular Tags