| package org.apache.velocity.runtime.resource.loader; |
| |
| /* |
| * 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. |
| */ |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.io.ByteArrayInputStream; |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| import org.apache.commons.collections.ExtendedProperties; |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.velocity.exception.ResourceNotFoundException; |
| import org.apache.velocity.exception.VelocityException; |
| import org.apache.velocity.runtime.resource.Resource; |
| import org.apache.velocity.runtime.resource.util.StringResource; |
| import org.apache.velocity.runtime.resource.util.StringResourceRepository; |
| import org.apache.velocity.runtime.resource.util.StringResourceRepositoryImpl; |
| import org.apache.velocity.util.ClassUtils; |
| |
| /** |
| * Resource loader that works with Strings. Users should manually add |
| * resources to the repository that is used by the resource loader instance. |
| * |
| * Below is an example configuration for this loader. |
| * Note that 'repository.class' is not necessary; |
| * if not provided, the factory will fall back on using |
| * {@link StringResourceRepositoryImpl} as the default. |
| * <pre> |
| * resource.loader = string |
| * string.resource.loader.description = Velocity StringResource loader |
| * string.resource.loader.class = org.apache.velocity.runtime.resource.loader.StringResourceLoader |
| * string.resource.loader.repository.class = org.apache.velocity.runtime.resource.loader.StringResourceRepositoryImpl |
| * </pre> |
| * Resources can be added to the repository like this: |
| * <pre><code> |
| * StringResourceRepository repo = StringResourceLoader.getRepository(); |
| * |
| * String myTemplateName = "/some/imaginary/path/hello.vm"; |
| * String myTemplate = "Hi, ${username}... this is some template!"; |
| * repo.putStringResource(myTemplateName, myTemplate); |
| * </code></pre> |
| * |
| * After this, the templates can be retrieved as usual. |
| * <br> |
| * <p>If there will be multiple StringResourceLoaders used in an application, |
| * you should consider specifying a 'string.resource.loader.repository.name = foo' |
| * property in order to keep you string resources in a non-default repository. |
| * This can help to avoid conflicts between different frameworks or components |
| * that are using StringResourceLoader. |
| * You can then retrieve your named repository like this: |
| * <pre><code> |
| * StringResourceRepository repo = StringResourceLoader.getRepository("foo"); |
| * </code></pre> |
| * and add string resources to the repo just as in the previous example. |
| * </p> |
| * <p>If you have concerns about memory leaks or for whatever reason do not wish |
| * to have your string repository stored statically as a class member, then you |
| * should set 'string.resource.loader.repository.static = false' in your properties. |
| * This will tell the resource loader that the string repository should be stored |
| * in the Velocity application attributes. To retrieve the repository, do: |
| * <pre><code> |
| * StringResourceRepository repo = velocityEngine.getApplicationAttribute("foo"); |
| * </code></pre> |
| * If you did not specify a name for the repository, then it will be stored under the |
| * class name of the repository implementation class (for which the default is |
| * 'org.apache.velocity.runtime.resource.util.StringResourceRepositoryImpl'). |
| * Incidentally, this is also true for the default statically stored repository. |
| * </p> |
| * <p>Whether your repository is stored statically or in Velocity's application |
| * attributes, you can also manually create and set it prior to Velocity |
| * initialization. For a static repository, you can do something like this: |
| * <pre><code> |
| * StringResourceRepository repo = new MyStringResourceRepository(); |
| * repo.magicallyAddSomeStringResources(); |
| * StringResourceLoader.setRepository("foo", repo); |
| * </code></pre> |
| * Or for a non-static repository: |
| * <pre><code> |
| * StringResourceRepository repo = new MyStringResourceRepository(); |
| * repo.magicallyAddSomeStringResources(); |
| * velocityEngine.setApplicationAttribute("foo", repo); |
| * </code></pre> |
| * Then, assuming the 'string.resource.loader.repository.name' property is |
| * set to 'some.name', the StringResourceLoader will use that already created |
| * repository, rather than creating a new one. |
| * </p> |
| * |
| * @author <a href="mailto:eelco.hillenius@openedge.nl">Eelco Hillenius</a> |
| * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a> |
| * @author Nathan Bubna |
| * @version $Id$ |
| * @since 1.5 |
| */ |
| public class StringResourceLoader extends ResourceLoader |
| { |
| /** |
| * Key to determine whether the repository should be set as the static one or not. |
| * @since 1.6 |
| */ |
| public static final String REPOSITORY_STATIC = "repository.static"; |
| |
| /** |
| * By default, repositories are stored statically (shared across the VM). |
| * @since 1.6 |
| */ |
| public static final boolean REPOSITORY_STATIC_DEFAULT = true; |
| |
| /** Key to look up the repository implementation class. */ |
| public static final String REPOSITORY_CLASS = "repository.class"; |
| |
| /** The default implementation class. */ |
| public static final String REPOSITORY_CLASS_DEFAULT = |
| StringResourceRepositoryImpl.class.getName(); |
| |
| /** |
| * Key to look up the name for the repository to be used. |
| * @since 1.6 |
| */ |
| public static final String REPOSITORY_NAME = "repository.name"; |
| |
| /** The default name for string resource repositories |
| * ('org.apache.velocity.runtime.resource.util.StringResourceRepository'). |
| * @since 1.6 |
| */ |
| public static final String REPOSITORY_NAME_DEFAULT = |
| StringResourceRepository.class.getName(); |
| |
| /** Key to look up the repository char encoding. */ |
| public static final String REPOSITORY_ENCODING = "repository.encoding"; |
| |
| /** The default repository encoding. */ |
| public static final String REPOSITORY_ENCODING_DEFAULT = "UTF-8"; |
| |
| |
| protected static final Map STATIC_REPOSITORIES = |
| Collections.synchronizedMap(new HashMap()); |
| |
| /** |
| * Returns a reference to the default static repository. |
| */ |
| public static StringResourceRepository getRepository() |
| { |
| return getRepository(REPOSITORY_NAME_DEFAULT); |
| } |
| |
| /** |
| * Returns a reference to the repository stored statically under the |
| * specified name. |
| * @since 1.6 |
| */ |
| public static StringResourceRepository getRepository(String name) |
| { |
| return (StringResourceRepository)STATIC_REPOSITORIES.get(name); |
| } |
| |
| /** |
| * Sets the specified {@link StringResourceRepository} in static storage |
| * under the specified name. |
| * @since 1.6 |
| */ |
| public static void setRepository(String name, StringResourceRepository repo) |
| { |
| STATIC_REPOSITORIES.put(name, repo); |
| } |
| |
| /** |
| * Removes the {@link StringResourceRepository} stored under the specified |
| * name. |
| * @since 1.6 |
| */ |
| public static StringResourceRepository removeRepository(String name) |
| { |
| return (StringResourceRepository)STATIC_REPOSITORIES.remove(name); |
| } |
| |
| /** |
| * Removes all statically stored {@link StringResourceRepository}s. |
| * @since 1.6 |
| */ |
| public static void clearRepositories() |
| { |
| STATIC_REPOSITORIES.clear(); |
| } |
| |
| |
| // the repository used internally by this resource loader |
| protected StringResourceRepository repository; |
| |
| |
| /** |
| * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(org.apache.commons.collections.ExtendedProperties) |
| */ |
| public void init(final ExtendedProperties configuration) |
| { |
| log.trace("StringResourceLoader : initialization starting."); |
| |
| // get the repository configuration info |
| String repoClass = configuration.getString(REPOSITORY_CLASS, REPOSITORY_CLASS_DEFAULT); |
| String repoName = configuration.getString(REPOSITORY_NAME, REPOSITORY_NAME_DEFAULT); |
| boolean isStatic = configuration.getBoolean(REPOSITORY_STATIC, REPOSITORY_STATIC_DEFAULT); |
| String encoding = configuration.getString(REPOSITORY_ENCODING); |
| |
| // look for an existing repository of that name and isStatic setting |
| if (isStatic) |
| { |
| this.repository = getRepository(repoName); |
| if (repository != null && log.isDebugEnabled()) |
| { |
| log.debug("Loaded repository '"+repoName+"' from static repo store"); |
| } |
| } |
| else |
| { |
| this.repository = (StringResourceRepository)rsvc.getApplicationAttribute(repoName); |
| if (repository != null && log.isDebugEnabled()) |
| { |
| log.debug("Loaded repository '"+repoName+"' from application attributes"); |
| } |
| } |
| |
| if (this.repository == null) |
| { |
| // since there's no repository under the repo name, create a new one |
| this.repository = createRepository(repoClass, encoding); |
| |
| // and store it according to the isStatic setting |
| if (isStatic) |
| { |
| setRepository(repoName, this.repository); |
| } |
| else |
| { |
| rsvc.setApplicationAttribute(repoName, this.repository); |
| } |
| } |
| else |
| { |
| // ok, we already have a repo |
| // warn them if they are trying to change the class of the repository |
| if (!this.repository.getClass().getName().equals(repoClass)) |
| { |
| log.debug("Cannot change class of string repository '"+repoName+ |
| "' from "+this.repository.getClass().getName()+" to "+repoClass+ |
| ". The change will be ignored."); |
| } |
| |
| // allow them to change the default encoding of the repo |
| if (encoding != null && |
| !this.repository.getEncoding().equals(encoding)) |
| { |
| if (log.isDebugEnabled()) |
| { |
| log.debug("Changing the default encoding of string repository '"+repoName+ |
| "' from "+this.repository.getEncoding()+" to "+encoding); |
| } |
| this.repository.setEncoding(encoding); |
| } |
| } |
| |
| log.trace("StringResourceLoader : initialization complete."); |
| } |
| |
| /** |
| * @since 1.6 |
| */ |
| public StringResourceRepository createRepository(final String className, |
| final String encoding) |
| { |
| if (log.isDebugEnabled()) |
| { |
| log.debug("Creating string repository using class "+className+"..."); |
| } |
| |
| StringResourceRepository repo; |
| try |
| { |
| repo = (StringResourceRepository) ClassUtils.getNewInstance(className); |
| } |
| catch (ClassNotFoundException cnfe) |
| { |
| throw new VelocityException("Could not find '" + className + "'", cnfe); |
| } |
| catch (IllegalAccessException iae) |
| { |
| throw new VelocityException("Could not access '" + className + "'", iae); |
| } |
| catch (InstantiationException ie) |
| { |
| throw new VelocityException("Could not instantiate '" + className + "'", ie); |
| } |
| |
| if (encoding != null) |
| { |
| repo.setEncoding(encoding); |
| } |
| else |
| { |
| repo.setEncoding(REPOSITORY_ENCODING_DEFAULT); |
| } |
| |
| if (log.isDebugEnabled()) |
| { |
| log.debug("Default repository encoding is " + repo.getEncoding()); |
| } |
| return repo; |
| } |
| |
| /** |
| * Overrides superclass for better performance. |
| * @since 1.6 |
| */ |
| public boolean resourceExists(final String name) |
| { |
| if (name == null) |
| { |
| return false; |
| } |
| return (this.repository.getStringResource(name) != null); |
| } |
| |
| /** |
| * Get an InputStream so that the Runtime can build a |
| * template with it. |
| * |
| * @param name name of template to get. |
| * @return InputStream containing the template. |
| * @throws ResourceNotFoundException Ff template not found |
| * in the RepositoryFactory. |
| */ |
| public InputStream getResourceStream(final String name) |
| throws ResourceNotFoundException |
| { |
| if (StringUtils.isEmpty(name)) |
| { |
| throw new ResourceNotFoundException("No template name provided"); |
| } |
| |
| StringResource resource = this.repository.getStringResource(name); |
| |
| if(resource == null) |
| { |
| throw new ResourceNotFoundException("Could not locate resource '" + name + "'"); |
| } |
| |
| byte [] byteArray = null; |
| |
| try |
| { |
| byteArray = resource.getBody().getBytes(resource.getEncoding()); |
| return new ByteArrayInputStream(byteArray); |
| } |
| catch(UnsupportedEncodingException ue) |
| { |
| throw new VelocityException("Could not convert String using encoding " + resource.getEncoding(), ue); |
| } |
| } |
| |
| /** |
| * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#isSourceModified(org.apache.velocity.runtime.resource.Resource) |
| */ |
| public boolean isSourceModified(final Resource resource) |
| { |
| StringResource original = null; |
| boolean result = true; |
| |
| original = this.repository.getStringResource(resource.getName()); |
| |
| if (original != null) |
| { |
| result = original.getLastModified() != resource.getLastModified(); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getLastModified(org.apache.velocity.runtime.resource.Resource) |
| */ |
| public long getLastModified(final Resource resource) |
| { |
| StringResource original = null; |
| |
| original = this.repository.getStringResource(resource.getName()); |
| |
| return (original != null) |
| ? original.getLastModified() |
| : 0; |
| } |
| |
| } |
| |