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 org.apache.commons.lang3.StringUtils; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.exception.VelocityException; import org.apache.velocity.runtime.RuntimeConstants; 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; import org.apache.velocity.util.ExtProperties; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * 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. *
* 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 ** Resources can be added to the repository like this: *
* StringResourceRepository repo = StringResourceLoader.getRepository();
*
* String myTemplateName = "/some/imaginary/path/hello.vm";
* String myTemplate = "Hi, ${username}... this is some template!";
* repo.putStringResource(myTemplateName, myTemplate);
*
*
* After this, the templates can be retrieved as usual.
* 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: *
* StringResourceRepository repo = StringResourceLoader.getRepository("foo");
*
* and add string resources to the repo just as in the previous example.
*
* 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: *
* StringResourceRepository repo = velocityEngine.getApplicationAttribute("foo");
*
* 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.
*
* 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: *
* StringResourceRepository repo = new MyStringResourceRepository();
* repo.magicallyAddSomeStringResources();
* StringResourceLoader.setRepository("foo", repo);
*
* Or for a non-static repository:
*
* StringResourceRepository repo = new MyStringResourceRepository();
* repo.magicallyAddSomeStringResources();
* velocityEngine.setApplicationAttribute("foo", repo);
*
* 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.
*
*
* @author Eelco Hillenius
* @author Henning P. Schmiedehausen
* @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";
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 ResourceLoader#init(org.apache.velocity.util.ExtProperties)
*/
public void init(final ExtProperties 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.debug("Loaded repository '{}' from static repo store", repoName);
}
}
else
{
this.repository = (StringResourceRepository)rsvc.getApplicationAttribute(repoName);
if (repository != null)
{
log.debug("Loaded repository '{}' from application attributes", repoName);
}
}
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 '{}' from {} to {}." +
" The change will be ignored.",
repoName, this.repository.getClass().getName(), repoClass);
}
// allow them to change the default encoding of the repo
if (encoding != null &&
!this.repository.getEncoding().equals(encoding))
{
log.debug("Changing the default encoding of string repository '{}' from {} to {}",
repoName, this.repository.getEncoding(), encoding);
this.repository.setEncoding(encoding);
}
}
log.trace("StringResourceLoader : initialization complete.");
}
/**
* @since 1.6
*/
public StringResourceRepository createRepository(final String className,
final String encoding)
{
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(RuntimeConstants.ENCODING_DEFAULT);
}
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 a reader so that the Runtime can build a
* template with it.
*
* @param name name of template to get.
* @param encoding asked encoding
* @return Reader containing the template.
* @throws ResourceNotFoundException Ff template not found
* in the RepositoryFactory.
* @since 2.0
*/
public Reader getResourceReader(String name, String encoding)
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;
InputStream rawStream = null;
try
{
byteArray = resource.getBody().getBytes(resource.getEncoding());
rawStream = new ByteArrayInputStream(byteArray);
return new InputStreamReader(rawStream, resource.getEncoding());
}
catch(UnsupportedEncodingException ue)
{
if (rawStream != null)
{
try
{
rawStream.close();
}
catch (IOException ioe) {}
}
throw new VelocityException("Could not convert String using encoding " + resource.getEncoding(), ue);
}
}
/**
* @see 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 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;
}
}