Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » generation » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.cocoon.generation;
   18   
   19   import java.beans.PropertyDescriptor;
   20   import java.io.BufferedReader;
   21   import java.io.IOException;
   22   import java.io.InputStream;
   23   import java.io.StringReader;
   24   import java.io.StringWriter;
   25   import java.util.ArrayList;
   26   import java.util.HashMap;
   27   import java.util.HashSet;
   28   import java.util.Iterator;
   29   import java.util.List;
   30   import java.util.Map;
   31   import java.util.Set;
   32   
   33   import org.apache.avalon.framework.activity.Initializable;
   34   import org.apache.avalon.framework.configuration.Configurable;
   35   import org.apache.avalon.framework.configuration.Configuration;
   36   import org.apache.avalon.framework.configuration.ConfigurationException;
   37   import org.apache.avalon.framework.context.ContextException;
   38   import org.apache.avalon.framework.context.DefaultContext;
   39   import org.apache.avalon.framework.parameters.Parameters;
   40   import org.apache.avalon.framework.service.ServiceException;
   41   import org.apache.cocoon.ProcessingException;
   42   import org.apache.cocoon.ResourceNotFoundException;
   43   import org.apache.cocoon.components.flow.FlowHelper;
   44   import org.apache.cocoon.components.flow.WebContinuation;
   45   import org.apache.cocoon.environment.ObjectModelHelper;
   46   import org.apache.cocoon.environment.Request;
   47   import org.apache.cocoon.environment.Response;
   48   import org.apache.cocoon.environment.Session;
   49   import org.apache.cocoon.environment.SourceResolver;
   50   import org.apache.commons.collections.ExtendedProperties;
   51   import org.apache.commons.jxpath.DynamicPropertyHandler;
   52   import org.apache.commons.jxpath.JXPathBeanInfo;
   53   import org.apache.commons.jxpath.JXPathIntrospector;
   54   import org.apache.commons.lang.StringUtils;
   55   import org.apache.excalibur.source.Source;
   56   import org.apache.excalibur.xml.sax.SAXParser;
   57   import org.apache.velocity.VelocityContext;
   58   import org.apache.velocity.app.VelocityEngine;
   59   import org.apache.velocity.context.Context;
   60   import org.apache.velocity.runtime.RuntimeConstants;
   61   import org.apache.velocity.runtime.RuntimeServices;
   62   import org.apache.velocity.runtime.log.LogSystem;
   63   import org.apache.velocity.runtime.resource.Resource;
   64   import org.apache.velocity.util.introspection.Info;
   65   import org.apache.velocity.util.introspection.UberspectImpl;
   66   import org.apache.velocity.util.introspection.VelMethod;
   67   import org.apache.velocity.util.introspection.VelPropertyGet;
   68   import org.apache.velocity.util.introspection.VelPropertySet;
   69   import org.mozilla.javascript.JavaScriptException;
   70   import org.mozilla.javascript.NativeArray;
   71   import org.mozilla.javascript.ScriptRuntime;
   72   import org.mozilla.javascript.Scriptable;
   73   import org.mozilla.javascript.ScriptableObject;
   74   import org.mozilla.javascript.Undefined;
   75   import org.mozilla.javascript.Wrapper;
   76   import org.xml.sax.InputSource;
   77   import org.xml.sax.SAXException;
   78   import org.xml.sax.SAXParseException;
   79   
   80   /**
   81    * <p>Cocoon {@link Generator} that produces dynamic XML SAX events
   82    * from a Velocity template file.</p>
   83    * If called from a Flowscript, the immediate properties of the context object from the Flowscript are available in the Velocity context.
   84    * In that case, the current Web Continuation from the Flowscript 
   85    * is also available as a variable named <code>continuation</code>. You would 
   86    * typically access its <code>id</code>:
   87    * <p><pre>
   88    *    &lt;form action="$continuation.id"&gt;
   89    * </pre></p>
   90    * <p>You can also reach previous continuations by using the <code>getContinuation()</code> function:</p>
   91    * <p><pre>
   92    *     &lt;form action="$continuation.getContinuation(1).id}" >
   93    * </pre></p>
   94    * 
   95    * In addition the following implicit objects are always available in
   96    * the Velocity context:
   97    * <p>
   98    * <dl>
   99    * <dt><code>request</code> (<code>org.apache.cocoon.environment.Request</code>)</dt>
  100    * <dd>The Cocoon current request</dd>
  101    *
  102    * <dt><code>response</code> (<code>org.apache.cocoon.environment.Response</code>)</dt>
  103    * <dd>The Cocoon response associated with the current request</dd>
  104    *
  105    * <dt><code>session</code> (<code>org.apache.cocoon.environment.Session</code>)</dt>
  106    * <dd>The Cocoon session associated with the current request</dd>
  107    *
  108    * <dt><code>context</code> (<code>org.apache.cocoon.environment.Context</code>)</dt>
  109    * <dd>The Cocoon context associated with the current request</dd>
  110    *
  111    * <dt><code>parameters</code> (<code>org.apache.avalon.framework.parameters.Parameters</code>)</dt>
  112    * <dd>Any parameters passed to the generator in the pipeline</dd>
  113    * </dl>
  114    * </p>
  115    *
  116    *
  117    * <h2>Sitemap Configuration</h2>
  118    *
  119    * <p>
  120    * Attributes:
  121    * <dl>
  122    * <dt>usecache (optional; default: 'false')</dt>
  123    * <dd>set to 'true' to enable template caching on the 'cocoon'
  124    * resource loader</dd>
  125    *
  126    * <dt>checkInterval (optional; default: '0')</dt>
  127    * <dd>This is the number of seconds between modification checks when
  128    * caching is turned on.  When this is an integer &gt; 0, this represents
  129    * the number of seconds between checks to see if the template was
  130    * modified. If the template has been modified since last check, then
  131    * it is reloaded and reparsed. Otherwise nothing is done. When &lt;= 0,
  132    * no modification checks will take place, and assuming that the
  133    * property cache (above) is true, once a template is loaded and
  134    * parsed the first time it is used, it will not be checked or
  135    * reloaded after that until the application or servlet engine is
  136    * restarted.</dd>
  137    * </dl>
  138    * </p>
  139    *
  140    * <p>
  141    * Child Elements:
  142    *
  143    * <dl>
  144    * <dt>&lt;property name="propertyName" value="propertyValue"/&gt; (optional; 0..n)</dt>
  145    * <dd>An additional property to pass along to the Velocity template
  146    * engine during initialization</dd>
  147    *
  148    * <dt>&lt;resource-loader name="loaderName" class="javaClassName" &gt; (optional; 0..n; children: property*)</dt>
  149    * <dd>The default configuration uses the 'cocoon' resource loader
  150    * which resolves resources via the Cocoon SourceResolver. Additional
  151    * resource loaders can be added with this configuration
  152    * element. Configuration properties for the resource loader can be
  153    * specified by adding a child property element of the resource-loader
  154    * element. The prefix '&lt;name&gt;.resource.loader.' is
  155    * automatically added to the property name.</dd>
  156    *
  157    * @version CVS $Id: VelocityGenerator.java 433543 2006-08-22 06:22:54Z crossley $
  158    */
  159   public class VelocityGenerator extends ServiceableGenerator
  160           implements Initializable, Configurable, LogSystem {
  161   
  162       /**
  163        * <p>Velocity context implementation specific to the Servlet environment.</p>
  164        *
  165        * <p>It provides the following special features:</p>
  166        * <ul>
  167        *   <li>puts the request, response, session, and servlet context objects
  168        *       into the Velocity context for direct access, and keeps them 
  169        *       read-only</li>
  170        *   <li>supports a read-only toolbox of view tools</li>
  171        *   <li>auto-searches servlet request attributes, session attributes and
  172        *       servlet context attribues for objects</li>
  173        * </ul>
  174        *
  175        * <p>The {@link #internalGet(String key)} method implements the following search order
  176        * for objects:</p>
  177        * <ol>
  178        *   <li>servlet request, servlet response, servlet session, servlet context</li>
  179        *   <li>toolbox</li>
  180        *   <li>local hashtable of objects (traditional use)</li>
  181        *   <li>servlet request attribues, servlet session attribute, servlet context
  182        *     attributes</li>
  183        * </ol> 
  184        *
  185        * <p>The purpose of this class is to make it easy for web designer to work 
  186        * with Java servlet based web applications. They do not need to be concerned 
  187        * with the concepts of request, session or application attributes and the 
  188        * live time of objects in these scopes.</p>
  189        *  
  190        * <p>Note that the put() method always puts objects into the local hashtable.
  191        * </p>
  192        *
  193        * <p>Acknowledge: the source code is borrowed from the jakarta-velocity-tools
  194        * project with slight modifications.</p>
  195        *
  196        * @author <a href="mailto:albert@charcoalgeneration.com">Albert Kwong</a>
  197        * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  198        * @author <a href="mailto:sidler@teamup.com">Gabe Sidler</a>
  199        * @author <a href="mailto:albert@charcoalgeneration.com">Albert Kwong</a>
  200        */
  201       public static class ChainedContext extends VelocityContext
  202       {
  203           
  204           /**
  205            * A local reference to the current servlet request.
  206            */ 
  207           private Request request;
  208           
  209           /**
  210            * A local reference to the current servlet response.
  211            */
  212           private Response response;
  213           
  214           /**
  215            * A local reference to the servlet session.
  216            */
  217           private Session session;
  218           
  219           /**
  220            * A local reference to the servlet context.
  221            */
  222           private org.apache.cocoon.environment.Context application;
  223           
  224           /**
  225            * A local reference to pipeline parameters.
  226            */
  227           private Parameters parameters;
  228           
  229           /**
  230            * Key to the HTTP request object.
  231            */
  232           public static final String REQUEST = "request";
  233           
  234           /**
  235            * Key to the HTTP response object.
  236            */
  237           public static final String RESPONSE = "response";
  238           
  239           /**
  240            * Key to the HTTP session object.
  241            */
  242           public static final String SESSION = "session";
  243           
  244           /**
  245            * Key to the servlet context object.
  246            */
  247           public static final String APPLICATION = "context";
  248           
  249           /**
  250            * Key to the servlet context object.
  251            */
  252           public static final String PARAMETERS = "parameters";
  253           
  254           
  255           /**
  256            * Default constructor.
  257            */
  258           public ChainedContext(org.apache.velocity.context.Context ctx, 
  259                                 Request request,
  260                                 Response response,
  261                                 org.apache.cocoon.environment.Context application,
  262                                 Parameters parameters)
  263           {
  264               super(null, ctx);
  265               this.request = request;
  266               this.response = response;
  267               this.session = request.getSession(false);
  268               this.application = application;
  269               this.parameters = parameters;
  270           }
  271           
  272           
  273           /**
  274            * <p>Looks up and returns the object with the specified key.</p>
  275            * 
  276            * <p>See the class documentation for more details.</p>
  277            *
  278            * @param key the key of the object requested
  279            * 
  280            * @return the requested object or null if not found
  281            */
  282           public Object internalGet( String key )
  283           {
  284               // make the four scopes of the Apocalypse Read only
  285               if ( key.equals( REQUEST ))
  286                   {
  287                       return request;
  288                   }
  289               else if( key.equals(RESPONSE) )
  290                   {
  291                       return response;
  292                   }
  293               else if ( key.equals(SESSION) )
  294                   {
  295                       return session;
  296                   }
  297               else if ( key.equals(APPLICATION))
  298                   {
  299                       return application;
  300                   }
  301               else if ( key.equals(PARAMETERS))
  302                   {
  303                       return parameters;
  304                   }
  305               
  306               Object o = null;
  307               
  308               // try the local hashtable
  309               o = super.internalGet( key );
  310               
  311               // if not found, wander down the scopes...
  312               if (o == null)
  313                   {
  314                       o = request.getAttribute( key );
  315                       
  316                       if ( o == null )
  317                           {
  318                               if ( session != null )
  319                                   {
  320                                       o = session.getAttribute( key );
  321                                   }
  322                               
  323                               if ( o == null )
  324                                   {
  325                                       o = application.getAttribute( key );
  326                                   }
  327                           }
  328                   }
  329               
  330               return o;
  331           }
  332   
  333           
  334       }  // ChainedContext
  335   
  336       /**
  337        * Velocity Introspector that supports Rhino JavaScript objects
  338        * as well as Java Objects
  339        *
  340        */
  341       public static class JSIntrospector extends UberspectImpl {
  342           
  343           public static class JSMethod implements VelMethod {
  344               
  345               Scriptable scope;
  346               String name;
  347               
  348               public JSMethod(Scriptable scope, String name) {
  349                   this.scope = scope;
  350                   this.name = name;
  351               }
  352               
  353               public Object invoke(Object thisArg, Object[] args)
  354                   throws Exception {
  355                   org.mozilla.javascript.Context cx = org.mozilla.javascript.Context.enter();
  356                   try {
  357                       Object result; 
  358                       Scriptable thisObj;
  359                       if (!(thisArg instanceof Scriptable)) {
  360                           thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope);
  361                       } else {
  362                           thisObj = (Scriptable)thisArg;
  363                       }
  364                       result = ScriptableObject.getProperty(thisObj, name);
  365                       Object[] newArgs = null;
  366                       if (args != null) {
  367                           newArgs = new Object[args.length];
  368                           for (int i = 0; i < args.length; i++) {
  369                               newArgs[i] = args[i];
  370                               if (args[i] != null && 
  371                                   !(args[i] instanceof Number) &&
  372                                   !(args[i] instanceof Boolean) &&
  373                                   !(args[i] instanceof String) &&
  374                                   !(args[i] instanceof Scriptable)) {
  375                                   newArgs[i] = org.mozilla.javascript.Context.toObject(args[i], scope);
  376                               }
  377                           }
  378                       }
  379                       result = ScriptRuntime.call(cx, result, thisObj, 
  380                                                   newArgs, scope);
  381                       if (result == Undefined.instance ||
  382                           result == Scriptable.NOT_FOUND) {
  383                           result = null;
  384                       } else while (result instanceof Wrapper) {
  385                           result = ((Wrapper)result).unwrap();
  386                       }
  387                       return result;
  388                   } catch (JavaScriptException e) {
  389                       throw new java.lang.reflect.InvocationTargetException(e);
  390                   } finally {
  391                       org.mozilla.javascript.Context.exit();
  392                   }
  393               }
  394               
  395               public boolean isCacheable() {
  396                   return false;
  397               }
  398               
  399               public String getMethodName() {
  400                   return name;
  401               }
  402               
  403               public Class getReturnType() {
  404                   return Object.class;
  405               }
  406               
  407           }
  408           
  409           public static class JSPropertyGet implements VelPropertyGet {
  410               
  411               Scriptable scope;
  412               String name;
  413               
  414               public JSPropertyGet(Scriptable scope, String name) {
  415                   this.scope = scope;
  416                   this.name = name;
  417               }
  418               
  419               public Object invoke(Object thisArg) throws Exception {
  420                   org.mozilla.javascript.Context.enter();
  421                   try {
  422                       Scriptable thisObj;
  423                       if (!(thisArg instanceof Scriptable)) {
  424                           thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope);
  425                       } else {
  426                           thisObj = (Scriptable)thisArg;
  427                       }
  428                       Object result = ScriptableObject.getProperty(thisObj, name);
  429                       if (result == Undefined.instance || 
  430                           result == Scriptable.NOT_FOUND) {
  431                           result = null;
  432                       } else while (result instanceof Wrapper) {
  433                           result = ((Wrapper)result).unwrap();
  434                       }
  435                       return result;
  436                   } finally {
  437                       org.mozilla.javascript.Context.exit();
  438                   }
  439               }
  440               
  441               public boolean isCacheable() {
  442                   return false;
  443               }
  444               
  445               public String getMethodName() {
  446                   return name;
  447               }
  448               
  449           }
  450           
  451           public static class JSPropertySet implements VelPropertySet {
  452               
  453               Scriptable scope;
  454               String name;
  455               
  456               public JSPropertySet(Scriptable scope, String name) {
  457                   this.scope = scope;
  458                   this.name = name;
  459               }
  460               
  461               public Object invoke(Object thisArg, Object rhs) throws Exception {
  462                   org.mozilla.javascript.Context.enter();
  463                   try {
  464                       Scriptable thisObj;
  465                       Object arg = rhs;
  466                       if (!(thisArg instanceof Scriptable)) {
  467                           thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope);
  468                       } else {
  469                           thisObj = (Scriptable)thisArg;
  470                       }
  471                       if (arg != null && 
  472                           !(arg instanceof Number) &&
  473                           !(arg instanceof Boolean) &&
  474                           !(arg instanceof String) &&
  475                           !(arg instanceof Scriptable)) {
  476                           arg = org.mozilla.javascript.Context.toObject(arg, scope);
  477                       }
  478                       ScriptableObject.putProperty(thisObj, name, arg);
  479                       return rhs;
  480                   } finally {
  481                       org.mozilla.javascript.Context.exit();
  482                   }
  483               }
  484               
  485               public boolean isCacheable() {
  486                   return false;
  487               }
  488               
  489               public String getMethodName() {
  490                   return name;        
  491               }
  492           }
  493           
  494           public static class NativeArrayIterator implements Iterator {
  495               
  496               NativeArray arr;
  497               int index;
  498               
  499               public NativeArrayIterator(NativeArray arr) {
  500                   this.arr = arr;
  501                   this.index = 0;
  502               }
  503               
  504               public boolean hasNext() {
  505                   return index < (int)arr.jsGet_length();
  506               }
  507               
  508               public Object next() {
  509                   org.mozilla.javascript.Context.enter();
  510                   try {
  511                       Object result = arr.get(index++, arr);
  512                       if (result == Undefined.instance ||
  513                           result == Scriptable.NOT_FOUND) {
  514                           result = null;
  515                       } else while (result instanceof Wrapper) {
  516                           result = ((Wrapper)result).unwrap();
  517                       }
  518                       return result;
  519                   } finally {
  520                       org.mozilla.javascript.Context.exit();
  521                   }
  522               }
  523               
  524               public void remove() {
  525                   arr.delete(index);
  526               }
  527           }
  528           
  529           public static class ScriptableIterator implements Iterator {
  530               
  531               Scriptable scope;
  532               Object[] ids;
  533               int index;
  534               
  535               public ScriptableIterator(Scriptable scope) {
  536                   this.scope = scope;
  537                   this.ids = scope.getIds();
  538                   this.index = 0;
  539               }
  540               
  541               public boolean hasNext() {
  542                   return index < ids.length;
  543               }
  544               
  545               public Object next() {
  546                   org.mozilla.javascript.Context.enter();
  547                   try {
  548                       Object result = 
  549                           ScriptableObject.getProperty(scope, 
  550                                                        ids[index++].toString());
  551                       if (result == Undefined.instance ||
  552                           result == Scriptable.NOT_FOUND) {
  553                           result = null;
  554                       } else while (result instanceof Wrapper) {
  555                           result = ((Wrapper)result).unwrap();
  556                       }
  557                       return result;
  558                   } finally {
  559                       org.mozilla.javascript.Context.exit();
  560                   }
  561               }
  562               
  563               public void remove() {
  564                   org.mozilla.javascript.Context.enter();
  565                   try {
  566                       scope.delete(ids[index].toString());
  567                   } finally {
  568                       org.mozilla.javascript.Context.exit();
  569                   }
  570               }
  571           }
  572           
  573           public Iterator getIterator(Object obj, Info i)
  574               throws Exception {
  575               if (!(obj instanceof Scriptable)) {
  576                   return super.getIterator(obj, i);
  577               }
  578               if (obj instanceof NativeArray) {
  579                   return new NativeArrayIterator((NativeArray)obj);
  580               }
  581               return new ScriptableIterator((Scriptable)obj);
  582           }
  583           
  584           public VelMethod getMethod(Object obj, String methodName, 
  585                                      Object[] args, Info i)
  586               throws Exception {
  587               if (!(obj instanceof Scriptable)) {
  588                   return super.getMethod(obj, methodName, args, i);
  589               }
  590               return new JSMethod((Scriptable)obj, methodName);
  591           }
  592           
  593           public VelPropertyGet getPropertyGet(Object obj, String identifier, 
  594                                                Info i)
  595               throws Exception {
  596               if (!(obj instanceof Scriptable)) {
  597                   return super.getPropertyGet(obj, identifier, i);
  598               }
  599               return new JSPropertyGet((Scriptable)obj, identifier);
  600           }
  601           
  602           public VelPropertySet getPropertySet(Object obj, String identifier, 
  603                                                Object arg, Info i)
  604               throws Exception {
  605               if (!(obj instanceof Scriptable)) {
  606                   return super.getPropertySet(obj, identifier, arg, i);
  607               }
  608               return new JSPropertySet((Scriptable)obj, identifier);
  609           }
  610       }
  611   
  612       /**
  613        * Velocity {@link org.apache.velocity.runtime.resource.loader.ResourceLoader}
  614        * implementation to load template resources using Cocoon's
  615        *{@link SourceResolver}. This class is created by the Velocity
  616        * framework via the ResourceLoaderFactory.
  617        *
  618        * @see org.apache.velocity.runtime.resource.loader.ResourceLoader
  619        */
  620       public static class TemplateLoader
  621               extends org.apache.velocity.runtime.resource.loader.ResourceLoader {
  622   
  623           private org.apache.avalon.framework.context.Context resolverContext;
  624   
  625           /**
  626            * Initialize this resource loader. The 'context' property is
  627            * required and must be of type {@link Context}. The context
  628            * is used to pass the Cocoon SourceResolver for the current
  629            * pipeline.
  630            *
  631            * @param config the properties to configure this resource.
  632            * @throws IllegalArgumentException thrown if the required
  633            *         'context' property is not set.
  634            * @throws ClassCastException if the 'context' property is not
  635            *         of type {@link org.apache.avalon.framework.context.Context}.
  636            * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(ExtendedProperties)
  637            */
  638           public void init(ExtendedProperties config) {
  639               this.resolverContext = (org.apache.avalon.framework.context.Context) config.get("context");
  640               if (this.resolverContext == null) {
  641                   throw new IllegalArgumentException("Runtime Cocoon resolver context not specified in resource loader configuration.");
  642               }
  643           }
  644   
  645           /**
  646            * @param systemId the path to the resource
  647            * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getResourceStream(String)
  648            */
  649           public InputStream getResourceStream(String systemId)
  650                   throws org.apache.velocity.exception.ResourceNotFoundException {
  651               try {
  652                   return resolveSource(systemId).getInputStream();
  653               } catch (org.apache.velocity.exception.ResourceNotFoundException ex) {
  654                   throw ex;
  655               } catch (Exception ex) {
  656                   throw new org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: " + ex);
  657               }
  658           }
  659   
  660           /**
  661            * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#isSourceModified(Resource)
  662            */
  663           public boolean isSourceModified(Resource resource) {
  664               long lastModified = 0;
  665               try {
  666                   lastModified = resolveSource(resource.getName()).getLastModified();
  667               } catch (Exception ex) {
  668                   super.rsvc.warn("Unable to determine last modified for resource: "
  669                                   + resource.getName() + ": " + ex);
  670               }
  671   
  672               return lastModified > 0 ? lastModified != resource.getLastModified() : true;
  673           }
  674   
  675           /**
  676            * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getLastModified(Resource)
  677            */
  678           public long getLastModified(Resource resource) {
  679               long lastModified = 0;
  680               try {
  681                   lastModified = resolveSource(resource.getName()).getLastModified();
  682               } catch (Exception ex) {
  683                   super.rsvc.warn("Unable to determine last modified for resource: "
  684                                   + resource.getName() + ": " + ex);
  685               }
  686   
  687               return lastModified;
  688           }
  689   
  690           /**
  691            * Store all the Source objects we lookup via the SourceResolver so that they can be properly
  692            * recycled later.
  693            *
  694            * @param systemId the path to the resource
  695            */
  696           private Source resolveSource(String systemId) throws org.apache.velocity.exception.ResourceNotFoundException {
  697               Map sourceCache;
  698               try {
  699                   sourceCache = (Map) this.resolverContext.get(CONTEXT_SOURCE_CACHE_KEY);
  700               } catch (ContextException ignore) {
  701                   throw new org.apache.velocity.exception.ResourceNotFoundException("Runtime Cocoon source cache not specified in resource loader resolver context.");
  702               }
  703   
  704               Source source = (Source) sourceCache.get(systemId);
  705               if (source == null) {
  706                   try {
  707                       SourceResolver resolver = (SourceResolver) this.resolverContext.get(CONTEXT_RESOLVER_KEY);
  708                       source = resolver.resolveURI(systemId);
  709                   } catch (ContextException ex) {
  710                       throw new org.apache.velocity.exception.ResourceNotFoundException("No Cocoon source resolver associated with current request.");
  711                   } catch (Exception ex) {
  712                       throw new org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: " + ex);
  713                   }
  714               }
  715   
  716               sourceCache.put(systemId, source);
  717               return source;
  718           }
  719       }
  720   
  721       /**
  722        * Key to lookup the {@link SourceResolver} from the context of
  723        * the resource loader
  724        */
  725       final private static String CONTEXT_RESOLVER_KEY = "resolver";
  726   
  727       /**
  728        * Key to lookup the source cache {@link Map} from the context of
  729        * the resource loader
  730        */
  731       final private static String CONTEXT_SOURCE_CACHE_KEY = "source-cache";
  732   
  733       private VelocityEngine tmplEngine;
  734       private boolean tmplEngineInitialized;
  735       private DefaultContext resolverContext;
  736       private Context velocityContext;
  737       private boolean activeFlag;
  738   
  739       /**
  740        * Read any additional objects to export to the Velocity context
  741        * from the configuration.
  742        *
  743        * @param configuration the class configurations.
  744        * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
  745        */
  746       public void configure(Configuration configuration) throws ConfigurationException {
  747           this.resolverContext = new DefaultContext();
  748           this.tmplEngine = new VelocityEngine();
  749   
  750           // Set up a JavaScript introspector for the Cocoon flow layer
  751           this.tmplEngine.setProperty(org.apache.velocity.runtime.RuntimeConstants.UBERSPECT_CLASSNAME,
  752                                       JSIntrospector.class.getName());
  753           this.tmplEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, this);
  754   
  755           // First set up our default 'cocoon' resource loader
  756           this.tmplEngine.setProperty("cocoon.resource.loader.class",
  757                                       TemplateLoader.class.getName());
  758           this.tmplEngine.setProperty("cocoon.resource.loader.cache",
  759                                       configuration.getAttribute("usecache", "false"));
  760           this.tmplEngine.setProperty("cocoon.resource.loader.modificationCheckInterval",
  761                                       configuration.getAttribute("checkInterval", "0"));
  762           this.tmplEngine.setProperty("cocoon.resource.loader.context",
  763                                       this.resolverContext);
  764   
  765           // Read in any additional properties to pass to the VelocityEngine during initialization
  766           Configuration[] properties = configuration.getChildren("property");
  767           for (int i = 0; i < properties.length; ++i) {
  768               Configuration c = properties[i];
  769               String name = c.getAttribute("name");
  770   
  771               // Disallow setting of certain properties
  772               if (name.startsWith("runtime.log")
  773                       || name.indexOf(".resource.loader.") != -1) {
  774                   if (getLogger().isInfoEnabled()) {
  775                       getLogger().info("ignoring disallowed property '" + name + "'.");
  776                   }
  777                   continue;
  778               }
  779               this.tmplEngine.setProperty(name, c.getAttribute("value"));
  780           }
  781   
  782           // Now read in any additional Velocity resource loaders
  783           List resourceLoaders = new ArrayList();
  784           Configuration[] loaders = configuration.getChildren("resource-loader");
  785           for (int i = 0; i < loaders.length; ++i) {
  786               Configuration loader = loaders[i];
  787               String name = loader.getAttribute("name");
  788               if (name.equals("cocoon")) {
  789                   if (getLogger().isInfoEnabled()) {
  790                       getLogger().info("'cocoon' resource loader already defined.");
  791                   }
  792                   continue;
  793               }
  794               resourceLoaders.add(name);
  795               String prefix = name + ".resource.loader.";
  796               String type = loader.getAttribute("class");
  797               this.tmplEngine.setProperty(prefix + "class", type);
  798               Configuration[] loaderProperties = loader.getChildren("property");
  799               for (int j = 0; j < loaderProperties.length; j++) {
  800                   Configuration c = loaderProperties[j];
  801                   String propName = c.getAttribute("name");
  802                   this.tmplEngine.setProperty(prefix + propName, c.getAttribute("value"));
  803               }
  804           }
  805   
  806           // Velocity expects resource loaders as CSV list
  807           //
  808           StringBuffer buffer = new StringBuffer("cocoon");
  809           for (Iterator it = resourceLoaders.iterator(); it.hasNext();) {
  810               buffer.append(',');
  811               buffer.append((String) it.next());
  812           }
  813           tmplEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, buffer.toString());
  814       }
  815   
  816       /**
  817        * @see org.apache.avalon.framework.activity.Initializable#initialize()
  818        */
  819       public void initialize() throws Exception {
  820           //this.tmplEngine.init();
  821       }
  822   
  823       /**
  824        * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(SourceResolver, Map, String, Parameters)
  825        */
  826       public void setup(SourceResolver resolver, Map objectModel, String src, Parameters params)
  827               throws ProcessingException, SAXException, IOException {
  828           if (activeFlag) {
  829               throw new IllegalStateException("setup called on recyclable sitemap component before properly recycling previous state");
  830           }
  831   
  832           super.setup(resolver, objectModel, src, params);
  833   
  834           // Pass along the SourceResolver to the Velocity resource loader
  835           this.resolverContext.put(CONTEXT_RESOLVER_KEY, resolver);
  836           this.resolverContext.put(CONTEXT_SOURCE_CACHE_KEY, new HashMap());
  837   
  838           // FIXME: Initialize the Velocity context. Use objectModel to pass these
  839           final Object bean = FlowHelper.getContextObject(objectModel);
  840           if (bean != null) {
  841   
  842               final WebContinuation kont = FlowHelper.getWebContinuation(objectModel);
  843   
  844               // Hack? I use JXPath to determine the properties of the bean object
  845               final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
  846               DynamicPropertyHandler h = null;
  847               final PropertyDescriptor[] props;
  848               if (bi.isDynamic()) {
  849                   Class cl = bi.getDynamicPropertyHandlerClass();
  850                   try {
  851                       h = (DynamicPropertyHandler) cl.newInstance();
  852                   } catch (Exception exc) {
  853                       exc.printStackTrace();
  854                       h = null;
  855                   }
  856                   props = null;
  857               } else {
  858                   h = null;
  859                   props = bi.getPropertyDescriptors();
  860               }
  861               final DynamicPropertyHandler handler = h;
  862               
  863               this.velocityContext = new Context() {
  864                       public Object put(String key, Object value) {
  865                           if (key.equals("flowContext") 
  866                               || key.equals("continuation")) {
  867                               return value;
  868                           }
  869                           if (handler != null) {
  870                               handler.setProperty(bean, key, value);
  871                               return value;
  872                           } else {
  873                               for (int i = 0; i < props.length; i++) {
  874                                   if (props[i].getName().equals(key)) {
  875                                       try {
  876                                           return props[i].getWriteMethod().invoke(bean, new Object[]{value});
  877                                       } catch (Exception ignored) {
  878                                           break;
  879                                       }
  880                                   }
  881                               }
  882                               return value;
  883                           }
  884                       }
  885                       
  886                       public boolean containsKey(Object key) {
  887                           if (key.equals("flowContext") 
  888                               || key.equals("continuation")) {
  889                               return true;
  890                           }
  891                           if (handler != null) {
  892                               String[] result = handler.getPropertyNames(bean);
  893                               for (int i = 0; i < result.length; i++) {
  894                                   if (key.equals(result[i])) {
  895                                       return true;
  896                                   }
  897                               }
  898                           } else {
  899                               for (int i = 0; i < props.length; i++) {
  900                                   if (key.equals(props[i].getName())) {
  901                                       return true;
  902                                   }
  903                               }
  904                           }
  905                           return false;
  906                       }
  907                       
  908                       public Object[] getKeys() {
  909                           Object[] result = null;
  910                           if (handler != null) {
  911                               result = handler.getPropertyNames(bean);
  912                           } else {
  913                               result = new Object[props.length];
  914                               for (int i = 0; i < props.length; i++) {
  915                                   result[i] = props[i].getName();
  916                               }
  917                           }
  918                           Set set = new HashSet();
  919                           for (int i = 0; i < result.length; i++) {
  920                               set.add(result[i]);
  921                           }
  922                           set.add("flowContext");
  923                           set.add("continuation");
  924                           result = new Object[set.size()];
  925                           set.toArray(result);
  926                           return result;
  927                       }
  928                       
  929                       public Object get(String key) {
  930                           if (key.equals("flowContext")) {
  931                               return bean;
  932                           }
  933                           if (key.equals("continuation")) {
  934                               return kont;
  935                           }
  936                           if (handler != null) {
  937                               return handler.getProperty(bean, key);
  938                           } else {
  939                               for (int i = 0; i < props.length; i++) {
  940                                   if (props[i].getName().equals(key)) {
  941                                       try {
  942                                           return props[i].getReadMethod().invoke(bean, null);
  943                                       } catch (Exception ignored) {
  944                                           break;
  945                                       }
  946                                   }
  947                               }
  948                               return null;
  949                           }
  950                       }
  951                       
  952                       public Object remove(Object key) {
  953                           // not implemented
  954                           return key;
  955                       }
  956                   };
  957           }
  958           this.velocityContext = 
  959               new ChainedContext (this.velocityContext, 
  960                                   ObjectModelHelper.getRequest(objectModel), 
  961                                   ObjectModelHelper.getResponse(objectModel), 
  962                                   ObjectModelHelper.getContext(objectModel),
  963                                   params);
  964           this.velocityContext.put("template", src);
  965           this.activeFlag = true;
  966       }
  967       /**
  968        * Free up the VelocityContext associated with the pipeline, and
  969        * release any Source objects resolved by the resource loader.
  970        *
  971        * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
  972        */
  973       public void recycle() {
  974           this.activeFlag = false;
  975   
  976           // Recycle all the Source objects resolved/used by our resource loader
  977           try {
  978               Map sourceCache = (Map) this.resolverContext.get(CONTEXT_SOURCE_CACHE_KEY);
  979               for (Iterator it = sourceCache.values().iterator(); it.hasNext();) {
  980                   this.resolver.release((Source) it.next());
  981               }
  982           } catch (ContextException ignore) {
  983           }
  984   
  985           this.velocityContext = null;
  986           super.recycle();
  987       }
  988   
  989       /**
  990        * Generate XML data using Velocity template.
  991        *
  992        * @see org.apache.cocoon.generation.Generator#generate()
  993        */
  994       public void generate()
  995               throws IOException, SAXException, ProcessingException {
  996           // Guard against calling generate before setup.
  997           if (!activeFlag) {
  998               throw new IllegalStateException("generate called on sitemap component before setup.");
  999           }
 1000   
 1001           SAXParser parser = null;
 1002           StringWriter w = new StringWriter();
 1003           try {
 1004               parser = (SAXParser) this.manager.lookup(SAXParser.ROLE);
 1005               if (getLogger().isDebugEnabled()) {
 1006                   getLogger().debug("Processing File: " + super.source);
 1007               }
 1008               if (!tmplEngineInitialized) {
 1009                   tmplEngine.init();
 1010                   tmplEngineInitialized = true;
 1011               }
 1012               /* lets render a template */
 1013               this.tmplEngine.mergeTemplate(super.source, velocityContext, w);
 1014   
 1015               InputSource xmlInput =
 1016                       new InputSource(new StringReader(w.toString()));
 1017               xmlInput.setSystemId(super.source);
 1018               parser.parse(xmlInput, this.xmlConsumer);
 1019           } catch (IOException e) {
 1020               getLogger().warn("VelocityGenerator.generate()", e);
 1021               throw new ResourceNotFoundException("Could not get Resource for VelocityGenerator", e);
 1022           } catch (SAXParseException e) {
 1023               int line = e.getLineNumber();
 1024               int column = e.getColumnNumber();
 1025               if (line <= 0) {
 1026                   line = Integer.MAX_VALUE;
 1027               }
 1028               BufferedReader reader = 
 1029                   new BufferedReader(new StringReader(w.toString()));
 1030               StringBuffer message = new StringBuffer(e.getMessage());
 1031               message.append(" In generated document:\n");
 1032               for (int i = 0; i < line; i++) {
 1033                   String lineStr = reader.readLine();
 1034                   if (lineStr == null) {
 1035                       break;
 1036                   }
 1037                   message.append(lineStr);
 1038                   message.append("\n");
 1039               }
 1040               if (column > 0) {
 1041                   message.append(StringUtils.leftPad("^\n", column + 1));
 1042               }
 1043               SAXException pe = new SAXParseException(message.toString(), 
 1044                                                       e.getPublicId(),
 1045                                                       "(Document generated from template "+e.getSystemId() + ")",
 1046                                                       e.getLineNumber(),
 1047                                                       e.getColumnNumber(),
 1048                                                       null);
 1049               getLogger().error("VelocityGenerator.generate()", pe);
 1050               throw pe;
 1051           } catch (SAXException e) {
 1052               getLogger().error("VelocityGenerator.generate()", e);
 1053               throw e;
 1054           } catch (ServiceException e) {
 1055               getLogger().error("Could not get parser", e);
 1056               throw new ProcessingException("Exception in VelocityGenerator.generate()", e);
 1057           } catch (ProcessingException e) {
 1058               throw e;
 1059           } catch (Exception e) {
 1060               getLogger().error("Could not get parser", e);
 1061               throw new ProcessingException("Exception in VelocityGenerator.generate()", e);
 1062           } finally {
 1063               this.manager.release(parser);
 1064           }
 1065       }
 1066   
 1067   
 1068       /**
 1069        * This implementation does nothing.
 1070        *
 1071        * @see org.apache.velocity.runtime.log.LogSystem#init(RuntimeServices)
 1072        */
 1073       public void init(RuntimeServices rs) throws Exception {
 1074       }
 1075   
 1076       /**
 1077        * Pass along Velocity log messages to our configured logger.
 1078        *
 1079        * @see org.apache.velocity.runtime.log.LogSystem#logVelocityMessage(int, String)
 1080        */
 1081       public void logVelocityMessage(int level, String message) {
 1082           switch (level) {
 1083               case LogSystem.WARN_ID:
 1084                   getLogger().warn(message);
 1085                   break;
 1086               case LogSystem.INFO_ID:
 1087                   getLogger().info(message);
 1088                   break;
 1089               case LogSystem.DEBUG_ID:
 1090                   getLogger().debug(message);
 1091                   break;
 1092               case LogSystem.ERROR_ID:
 1093                   getLogger().error(message);
 1094                   break;
 1095               default :
 1096                   getLogger().info(message);
 1097                   break;
 1098           }
 1099       }
 1100   }

Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » generation » [javadoc | source]