/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.generation; import java.beans.PropertyDescriptor; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.configuration.Configurable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.context.DefaultContext; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.service.ServiceException; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.ResourceNotFoundException; import org.apache.cocoon.components.flow.FlowHelper; import org.apache.cocoon.components.flow.WebContinuation; import org.apache.cocoon.environment.ObjectModelHelper; import org.apache.cocoon.environment.Request; import org.apache.cocoon.environment.Response; import org.apache.cocoon.environment.Session; import org.apache.cocoon.environment.SourceResolver; import org.apache.commons.collections.ExtendedProperties; import org.apache.commons.jxpath.DynamicPropertyHandler; import org.apache.commons.jxpath.JXPathBeanInfo; import org.apache.commons.jxpath.JXPathIntrospector; import org.apache.commons.lang.StringUtils; import org.apache.excalibur.source.Source; import org.apache.excalibur.xml.sax.SAXParser; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.context.Context; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.RuntimeServices; import org.apache.velocity.runtime.log.LogSystem; import org.apache.velocity.runtime.resource.Resource; import org.apache.velocity.util.introspection.Info; import org.apache.velocity.util.introspection.UberspectImpl; import org.apache.velocity.util.introspection.VelMethod; import org.apache.velocity.util.introspection.VelPropertyGet; import org.apache.velocity.util.introspection.VelPropertySet; import org.mozilla.javascript.JavaScriptException; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.Undefined; import org.mozilla.javascript.Wrapper; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * <p>Cocoon {@link Generator} that produces dynamic XML SAX events * from a Velocity template file.</p> * If called from a Flowscript, the immediate properties of the context object from the Flowscript are available in the Velocity context. * In that case, the current Web Continuation from the Flowscript * is also available as a variable named <code>continuation</code>. You would * typically access its <code>id</code>: * <p><pre> * <form action="$continuation.id"> * </pre></p> * <p>You can also reach previous continuations by using the <code>getContinuation()</code> function:</p> * <p><pre> * <form action="$continuation.getContinuation(1).id}" > * </pre></p> * * In addition the following implicit objects are always available in * the Velocity context: * <p> * <dl> * <dt><code>request</code> (<code>org.apache.cocoon.environment.Request</code>)</dt> * <dd>The Cocoon current request</dd> * * <dt><code>response</code> (<code>org.apache.cocoon.environment.Response</code>)</dt> * <dd>The Cocoon response associated with the current request</dd> * * <dt><code>session</code> (<code>org.apache.cocoon.environment.Session</code>)</dt> * <dd>The Cocoon session associated with the current request</dd> * * <dt><code>context</code> (<code>org.apache.cocoon.environment.Context</code>)</dt> * <dd>The Cocoon context associated with the current request</dd> * * <dt><code>parameters</code> (<code>org.apache.avalon.framework.parameters.Parameters</code>)</dt> * <dd>Any parameters passed to the generator in the pipeline</dd> * </dl> * </p> * * * <h2>Sitemap Configuration</h2> * * <p> * Attributes: * <dl> * <dt>usecache (optional; default: 'false')</dt> * <dd>set to 'true' to enable template caching on the 'cocoon' * resource loader</dd> * * <dt>checkInterval (optional; default: '0')</dt> * <dd>This is the number of seconds between modification checks when * caching is turned on. When this is an integer > 0, this represents * the number of seconds between checks to see if the template was * modified. If the template has been modified since last check, then * it is reloaded and reparsed. Otherwise nothing is done. When <= 0, * no modification checks will take place, and assuming that the * property cache (above) is true, once a template is loaded and * parsed the first time it is used, it will not be checked or * reloaded after that until the application or servlet engine is * restarted.</dd> * </dl> * </p> * * <p> * Child Elements: * * <dl> * <dt><property name="propertyName" value="propertyValue"/> (optional; 0..n)</dt> * <dd>An additional property to pass along to the Velocity template * engine during initialization</dd> * * <dt><resource-loader name="loaderName" class="javaClassName" > (optional; 0..n; children: property*)</dt> * <dd>The default configuration uses the 'cocoon' resource loader * which resolves resources via the Cocoon SourceResolver. Additional * resource loaders can be added with this configuration * element. Configuration properties for the resource loader can be * specified by adding a child property element of the resource-loader * element. The prefix '<name>.resource.loader.' is * automatically added to the property name.</dd> * * @version CVS $Id$ */ public class VelocityGenerator extends ServiceableGenerator implements Initializable, Configurable, LogSystem { /** * <p>Velocity context implementation specific to the Servlet environment.</p> * * <p>It provides the following special features:</p> * <ul> * <li>puts the request, response, session, and servlet context objects * into the Velocity context for direct access, and keeps them * read-only</li> * <li>supports a read-only toolbox of view tools</li> * <li>auto-searches servlet request attributes, session attributes and * servlet context attribues for objects</li> * </ul> * * <p>The {@link #internalGet(String key)} method implements the following search order * for objects:</p> * <ol> * <li>servlet request, servlet response, servlet session, servlet context</li> * <li>toolbox</li> * <li>local hashtable of objects (traditional use)</li> * <li>servlet request attribues, servlet session attribute, servlet context * attributes</li> * </ol> * * <p>The purpose of this class is to make it easy for web designer to work * with Java servlet based web applications. They do not need to be concerned * with the concepts of request, session or application attributes and the * live time of objects in these scopes.</p> * * <p>Note that the put() method always puts objects into the local hashtable. * </p> * * <p>Acknowledge: the source code is borrowed from the jakarta-velocity-tools * project with slight modifications.</p> * * @author <a href="mailto:albert@charcoalgeneration.com">Albert Kwong</a> * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> * @author <a href="mailto:sidler@teamup.com">Gabe Sidler</a> * @author <a href="mailto:albert@charcoalgeneration.com">Albert Kwong</a> */ public static class ChainedContext extends VelocityContext { /** * A local reference to the current servlet request. */ private Request request; /** * A local reference to the current servlet response. */ private Response response; /** * A local reference to the servlet session. */ private Session session; /** * A local reference to the servlet context. */ private org.apache.cocoon.environment.Context application; /** * A local reference to pipeline parameters. */ private Parameters parameters; /** * Key to the HTTP request object. */ public static final String REQUEST = "request"; /** * Key to the HTTP response object. */ public static final String RESPONSE = "response"; /** * Key to the HTTP session object. */ public static final String SESSION = "session"; /** * Key to the servlet context object. */ public static final String APPLICATION = "context"; /** * Key to the servlet context object. */ public static final String PARAMETERS = "parameters"; /** * Default constructor. */ public ChainedContext(org.apache.velocity.context.Context ctx, Request request, Response response, org.apache.cocoon.environment.Context application, Parameters parameters) { super(null, ctx); this.request = request; this.response = response; this.session = request.getSession(false); this.application = application; this.parameters = parameters; } /** * <p>Looks up and returns the object with the specified key.</p> * * <p>See the class documentation for more details.</p> * * @param key the key of the object requested * * @return the requested object or null if not found */ public Object internalGet( String key ) { // make the four scopes of the Apocalypse Read only if ( key.equals( REQUEST )) { return request; } else if( key.equals(RESPONSE) ) { return response; } else if ( key.equals(SESSION) ) { return session; } else if ( key.equals(APPLICATION)) { return application; } else if ( key.equals(PARAMETERS)) { return parameters; } Object o = null; // try the local hashtable o = super.internalGet( key ); // if not found, wander down the scopes... if (o == null) { o = request.getAttribute( key ); if ( o == null ) { if ( session != null ) { o = session.getAttribute( key ); } if ( o == null ) { o = application.getAttribute( key ); } } } return o; } } // ChainedContext /** * Velocity Introspector that supports Rhino JavaScript objects * as well as Java Objects * */ public static class JSIntrospector extends UberspectImpl { public static class JSMethod implements VelMethod { Scriptable scope; String name; public JSMethod(Scriptable scope, String name) { this.scope = scope; this.name = name; } public Object invoke(Object thisArg, Object[] args) throws Exception { org.mozilla.javascript.Context cx = org.mozilla.javascript.Context.enter(); try { Object result; Scriptable thisObj; if (!(thisArg instanceof Scriptable)) { thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope); } else { thisObj = (Scriptable)thisArg; } result = ScriptableObject.getProperty(thisObj, name); Object[] newArgs = null; if (args != null) { newArgs = new Object[args.length]; for (int i = 0; i < args.length; i++) { newArgs[i] = args[i]; if (args[i] != null && !(args[i] instanceof Number) && !(args[i] instanceof Boolean) && !(args[i] instanceof String) && !(args[i] instanceof Scriptable)) { newArgs[i] = org.mozilla.javascript.Context.toObject(args[i], scope); } } } result = ScriptRuntime.call(cx, result, thisObj, newArgs, scope); if (result == Undefined.instance || result == Scriptable.NOT_FOUND) { result = null; } else while (result instanceof Wrapper) { result = ((Wrapper)result).unwrap(); } return result; } catch (JavaScriptException e) { throw new java.lang.reflect.InvocationTargetException(e); } finally { org.mozilla.javascript.Context.exit(); } } public boolean isCacheable() { return false; } public String getMethodName() { return name; } public Class getReturnType() { return Object.class; } } public static class JSPropertyGet implements VelPropertyGet { Scriptable scope; String name; public JSPropertyGet(Scriptable scope, String name) { this.scope = scope; this.name = name; } public Object invoke(Object thisArg) throws Exception { org.mozilla.javascript.Context.enter(); try { Scriptable thisObj; if (!(thisArg instanceof Scriptable)) { thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope); } else { thisObj = (Scriptable)thisArg; } Object result = ScriptableObject.getProperty(thisObj, name); if (result == Undefined.instance || result == Scriptable.NOT_FOUND) { result = null; } else while (result instanceof Wrapper) { result = ((Wrapper)result).unwrap(); } return result; } finally { org.mozilla.javascript.Context.exit(); } } public boolean isCacheable() { return false; } public String getMethodName() { return name; } } public static class JSPropertySet implements VelPropertySet { Scriptable scope; String name; public JSPropertySet(Scriptable scope, String name) { this.scope = scope; this.name = name; } public Object invoke(Object thisArg, Object rhs) throws Exception { org.mozilla.javascript.Context.enter(); try { Scriptable thisObj; Object arg = rhs; if (!(thisArg instanceof Scriptable)) { thisObj = org.mozilla.javascript.Context.toObject(thisArg, scope); } else { thisObj = (Scriptable)thisArg; } if (arg != null && !(arg instanceof Number) && !(arg instanceof Boolean) && !(arg instanceof String) && !(arg instanceof Scriptable)) { arg = org.mozilla.javascript.Context.toObject(arg, scope); } ScriptableObject.putProperty(thisObj, name, arg); return rhs; } finally { org.mozilla.javascript.Context.exit(); } } public boolean isCacheable() { return false; } public String getMethodName() { return name; } } public static class NativeArrayIterator implements Iterator { NativeArray arr; int index; public NativeArrayIterator(NativeArray arr) { this.arr = arr; this.index = 0; } public boolean hasNext() { return index < (int)arr.jsGet_length(); } public Object next() { org.mozilla.javascript.Context.enter(); try { Object result = arr.get(index++, arr); if (result == Undefined.instance || result == Scriptable.NOT_FOUND) { result = null; } else while (result instanceof Wrapper) { result = ((Wrapper)result).unwrap(); } return result; } finally { org.mozilla.javascript.Context.exit(); } } public void remove() { arr.delete(index); } } public static class ScriptableIterator implements Iterator { Scriptable scope; Object[] ids; int index; public ScriptableIterator(Scriptable scope) { this.scope = scope; this.ids = scope.getIds(); this.index = 0; } public boolean hasNext() { return index < ids.length; } public Object next() { org.mozilla.javascript.Context.enter(); try { Object result = ScriptableObject.getProperty(scope, ids[index++].toString()); if (result == Undefined.instance || result == Scriptable.NOT_FOUND) { result = null; } else while (result instanceof Wrapper) { result = ((Wrapper)result).unwrap(); } return result; } finally { org.mozilla.javascript.Context.exit(); } } public void remove() { org.mozilla.javascript.Context.enter(); try { scope.delete(ids[index].toString()); } finally { org.mozilla.javascript.Context.exit(); } } } public Iterator getIterator(Object obj, Info i) throws Exception { if (!(obj instanceof Scriptable)) { return super.getIterator(obj, i); } if (obj instanceof NativeArray) { return new NativeArrayIterator((NativeArray)obj); } return new ScriptableIterator((Scriptable)obj); } public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) throws Exception { if (!(obj instanceof Scriptable)) { return super.getMethod(obj, methodName, args, i); } return new JSMethod((Scriptable)obj, methodName); } public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception { if (!(obj instanceof Scriptable)) { return super.getPropertyGet(obj, identifier, i); } return new JSPropertyGet((Scriptable)obj, identifier); } public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) throws Exception { if (!(obj instanceof Scriptable)) { return super.getPropertySet(obj, identifier, arg, i); } return new JSPropertySet((Scriptable)obj, identifier); } } /** * Velocity {@link org.apache.velocity.runtime.resource.loader.ResourceLoader} * implementation to load template resources using Cocoon's *{@link SourceResolver}. This class is created by the Velocity * framework via the ResourceLoaderFactory. * * @see org.apache.velocity.runtime.resource.loader.ResourceLoader */ public static class TemplateLoader extends org.apache.velocity.runtime.resource.loader.ResourceLoader { private org.apache.avalon.framework.context.Context resolverContext; /** * Initialize this resource loader. The 'context' property is * required and must be of type {@link Context}. The context * is used to pass the Cocoon SourceResolver for the current * pipeline. * * @param config the properties to configure this resource. * @throws IllegalArgumentException thrown if the required * 'context' property is not set. * @throws ClassCastException if the 'context' property is not * of type {@link org.apache.avalon.framework.context.Context}. * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(ExtendedProperties) */ public void init(ExtendedProperties config) { this.resolverContext = (org.apache.avalon.framework.context.Context) config.get("context"); if (this.resolverContext == null) { throw new IllegalArgumentException("Runtime Cocoon resolver context not specified in resource loader configuration."); } } /** * @param systemId the path to the resource * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getResourceStream(String) */ public InputStream getResourceStream(String systemId) throws org.apache.velocity.exception.ResourceNotFoundException { try { return resolveSource(systemId).getInputStream(); } catch (org.apache.velocity.exception.ResourceNotFoundException ex) { throw ex; } catch (Exception ex) { throw new org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: " + ex); } } /** * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#isSourceModified(Resource) */ public boolean isSourceModified(Resource resource) { long lastModified = 0; try { lastModified = resolveSource(resource.getName()).getLastModified(); } catch (Exception ex) { super.rsvc.warn("Unable to determine last modified for resource: " + resource.getName() + ": " + ex); } return lastModified > 0 ? lastModified != resource.getLastModified() : true; } /** * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getLastModified(Resource) */ public long getLastModified(Resource resource) { long lastModified = 0; try { lastModified = resolveSource(resource.getName()).getLastModified(); } catch (Exception ex) { super.rsvc.warn("Unable to determine last modified for resource: " + resource.getName() + ": " + ex); } return lastModified; } /** * Store all the Source objects we lookup via the SourceResolver so that they can be properly * recycled later. * * @param systemId the path to the resource */ private Source resolveSource(String systemId) throws org.apache.velocity.exception.ResourceNotFoundException { Map sourceCache; try { sourceCache = (Map) this.resolverContext.get(CONTEXT_SOURCE_CACHE_KEY); } catch (ContextException ignore) { throw new org.apache.velocity.exception.ResourceNotFoundException("Runtime Cocoon source cache not specified in resource loader resolver context."); } Source source = (Source) sourceCache.get(systemId); if (source == null) { try { SourceResolver resolver = (SourceResolver) this.resolverContext.get(CONTEXT_RESOLVER_KEY); source = resolver.resolveURI(systemId); } catch (ContextException ex) { throw new org.apache.velocity.exception.ResourceNotFoundException("No Cocoon source resolver associated with current request."); } catch (Exception ex) { throw new org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: " + ex); } } sourceCache.put(systemId, source); return source; } } /** * Key to lookup the {@link SourceResolver} from the context of * the resource loader */ final private static String CONTEXT_RESOLVER_KEY = "resolver"; /** * Key to lookup the source cache {@link Map} from the context of * the resource loader */ final private static String CONTEXT_SOURCE_CACHE_KEY = "source-cache"; private VelocityEngine tmplEngine; private boolean tmplEngineInitialized; private DefaultContext resolverContext; private Context velocityContext; private boolean activeFlag; /** * Read any additional objects to export to the Velocity context * from the configuration. * * @param configuration the class configurations. * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration) */ public void configure(Configuration configuration) throws ConfigurationException { this.resolverContext = new DefaultContext(); this.tmplEngine = new VelocityEngine(); // Set up a JavaScript introspector for the Cocoon flow layer this.tmplEngine.setProperty(org.apache.velocity.runtime.RuntimeConstants.UBERSPECT_CLASSNAME, JSIntrospector.class.getName()); this.tmplEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, this); // First set up our default 'cocoon' resource loader this.tmplEngine.setProperty("cocoon.resource.loader.class", TemplateLoader.class.getName()); this.tmplEngine.setProperty("cocoon.resource.loader.cache", configuration.getAttribute("usecache", "false")); this.tmplEngine.setProperty("cocoon.resource.loader.modificationCheckInterval", configuration.getAttribute("checkInterval", "0")); this.tmplEngine.setProperty("cocoon.resource.loader.context", this.resolverContext); // Read in any additional properties to pass to the VelocityEngine during initialization Configuration[] properties = configuration.getChildren("property"); for (int i = 0; i < properties.length; ++i) { Configuration c = properties[i]; String name = c.getAttribute("name"); // Disallow setting of certain properties if (name.startsWith("runtime.log") || name.indexOf(".resource.loader.") != -1) { if (getLogger().isInfoEnabled()) { getLogger().info("ignoring disallowed property '" + name + "'."); } continue; } this.tmplEngine.setProperty(name, c.getAttribute("value")); } // Now read in any additional Velocity resource loaders List resourceLoaders = new ArrayList(); Configuration[] loaders = configuration.getChildren("resource-loader"); for (int i = 0; i < loaders.length; ++i) { Configuration loader = loaders[i]; String name = loader.getAttribute("name"); if (name.equals("cocoon")) { if (getLogger().isInfoEnabled()) { getLogger().info("'cocoon' resource loader already defined."); } continue; } resourceLoaders.add(name); String prefix = name + ".resource.loader."; String type = loader.getAttribute("class"); this.tmplEngine.setProperty(prefix + "class", type); Configuration[] loaderProperties = loader.getChildren("property"); for (int j = 0; j < loaderProperties.length; j++) { Configuration c = loaderProperties[j]; String propName = c.getAttribute("name"); this.tmplEngine.setProperty(prefix + propName, c.getAttribute("value")); } } // Velocity expects resource loaders as CSV list // StringBuffer buffer = new StringBuffer("cocoon"); for (Iterator it = resourceLoaders.iterator(); it.hasNext();) { buffer.append(','); buffer.append((String) it.next()); } tmplEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, buffer.toString()); } /** * @see org.apache.avalon.framework.activity.Initializable#initialize() */ public void initialize() throws Exception { //this.tmplEngine.init(); } /** * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(SourceResolver, Map, String, Parameters) */ public void setup(SourceResolver resolver, Map objectModel, String src, Parameters params) throws ProcessingException, SAXException, IOException { if (activeFlag) { throw new IllegalStateException("setup called on recyclable sitemap component before properly recycling previous state"); } super.setup(resolver, objectModel, src, params); // Pass along the SourceResolver to the Velocity resource loader this.resolverContext.put(CONTEXT_RESOLVER_KEY, resolver); this.resolverContext.put(CONTEXT_SOURCE_CACHE_KEY, new HashMap()); // FIXME: Initialize the Velocity context. Use objectModel to pass these final Object bean = FlowHelper.getContextObject(objectModel); if (bean != null) { final WebContinuation kont = FlowHelper.getWebContinuation(objectModel); // Hack? I use JXPath to determine the properties of the bean object final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass()); DynamicPropertyHandler h = null; final PropertyDescriptor[] props; if (bi.isDynamic()) { Class cl = bi.getDynamicPropertyHandlerClass(); try { h = (DynamicPropertyHandler) cl.newInstance(); } catch (Exception exc) { exc.printStackTrace(); h = null; } props = null; } else { h = null; props = bi.getPropertyDescriptors(); } final DynamicPropertyHandler handler = h; this.velocityContext = new Context() { public Object put(String key, Object value) { if (key.equals("flowContext") || key.equals("continuation")) { return value; } if (handler != null) { handler.setProperty(bean, key, value); return value; } else { for (int i = 0; i < props.length; i++) { if (props[i].getName().equals(key)) { try { return props[i].getWriteMethod().invoke(bean, new Object[]{value}); } catch (Exception ignored) { break; } } } return value; } } public boolean containsKey(Object key) { if (key.equals("flowContext") || key.equals("continuation")) { return true; } if (handler != null) { String[] result = handler.getPropertyNames(bean); for (int i = 0; i < result.length; i++) { if (key.equals(result[i])) { return true; } } } else { for (int i = 0; i < props.length; i++) { if (key.equals(props[i].getName())) { return true; } } } return false; } public Object[] getKeys() { Object[] result = null; if (handler != null) { result = handler.getPropertyNames(bean); } else { result = new Object[props.length]; for (int i = 0; i < props.length; i++) { result[i] = props[i].getName(); } } Set set = new HashSet(); for (int i = 0; i < result.length; i++) { set.add(result[i]); } set.add("flowContext"); set.add("continuation"); result = new Object[set.size()]; set.toArray(result); return result; } public Object get(String key) { if (key.equals("flowContext")) { return bean; } if (key.equals("continuation")) { return kont; } if (handler != null) { return handler.getProperty(bean, key); } else { for (int i = 0; i < props.length; i++) { if (props[i].getName().equals(key)) { try { return props[i].getReadMethod().invoke(bean, null); } catch (Exception ignored) { break; } } } return null; } } public Object remove(Object key) { // not implemented return key; } }; } this.velocityContext = new ChainedContext (this.velocityContext, ObjectModelHelper.getRequest(objectModel), ObjectModelHelper.getResponse(objectModel), ObjectModelHelper.getContext(objectModel), params); this.velocityContext.put("template", src); this.activeFlag = true; } /** * Free up the VelocityContext associated with the pipeline, and * release any Source objects resolved by the resource loader. * * @see org.apache.avalon.excalibur.pool.Recyclable#recycle() */ public void recycle() { this.activeFlag = false; // Recycle all the Source objects resolved/used by our resource loader try { Map sourceCache = (Map) this.resolverContext.get(CONTEXT_SOURCE_CACHE_KEY); for (Iterator it = sourceCache.values().iterator(); it.hasNext();) { this.resolver.release((Source) it.next()); } } catch (ContextException ignore) { } this.velocityContext = null; super.recycle(); } /** * Generate XML data using Velocity template. * * @see org.apache.cocoon.generation.Generator#generate() */ public void generate() throws IOException, SAXException, ProcessingException { // Guard against calling generate before setup. if (!activeFlag) { throw new IllegalStateException("generate called on sitemap component before setup."); } SAXParser parser = null; StringWriter w = new StringWriter(); try { parser = (SAXParser) this.manager.lookup(SAXParser.ROLE); if (getLogger().isDebugEnabled()) { getLogger().debug("Processing File: " + super.source); } if (!tmplEngineInitialized) { tmplEngine.init(); tmplEngineInitialized = true; } /* lets render a template */ this.tmplEngine.mergeTemplate(super.source, velocityContext, w); InputSource xmlInput = new InputSource(new StringReader(w.toString())); xmlInput.setSystemId(super.source); parser.parse(xmlInput, this.xmlConsumer); } catch (IOException e) { getLogger().warn("VelocityGenerator.generate()", e); throw new ResourceNotFoundException("Could not get Resource for VelocityGenerator", e); } catch (SAXParseException e) { int line = e.getLineNumber(); int column = e.getColumnNumber(); if (line <= 0) { line = Integer.MAX_VALUE; } BufferedReader reader = new BufferedReader(new StringReader(w.toString())); StringBuffer message = new StringBuffer(e.getMessage()); message.append(" In generated document:\n"); for (int i = 0; i < line; i++) { String lineStr = reader.readLine(); if (lineStr == null) { break; } message.append(lineStr); message.append("\n"); } if (column > 0) { message.append(StringUtils.leftPad("^\n", column + 1)); } SAXException pe = new SAXParseException(message.toString(), e.getPublicId(), "(Document generated from template "+e.getSystemId() + ")", e.getLineNumber(), e.getColumnNumber(), null); getLogger().error("VelocityGenerator.generate()", pe); throw pe; } catch (SAXException e) { getLogger().error("VelocityGenerator.generate()", e); throw e; } catch (ServiceException e) { getLogger().error("Could not get parser", e); throw new ProcessingException("Exception in VelocityGenerator.generate()", e); } catch (ProcessingException e) { throw e; } catch (Exception e) { getLogger().error("Could not get parser", e); throw new ProcessingException("Exception in VelocityGenerator.generate()", e); } finally { this.manager.release(parser); } } /** * This implementation does nothing. * * @see org.apache.velocity.runtime.log.LogSystem#init(RuntimeServices) */ public void init(RuntimeServices rs) throws Exception { } /** * Pass along Velocity log messages to our configured logger. * * @see org.apache.velocity.runtime.log.LogSystem#logVelocityMessage(int, String) */ public void logVelocityMessage(int level, String message) { switch (level) { case LogSystem.WARN_ID: getLogger().warn(message); break; case LogSystem.INFO_ID: getLogger().info(message); break; case LogSystem.DEBUG_ID: getLogger().debug(message); break; case LogSystem.ERROR_ID: getLogger().error(message); break; default : getLogger().info(message); break; } } }