edu.internet2.middleware.shibboleth.common.util.StringResourceLoader.java Source code

Java tutorial

Introduction

Here is the source code for edu.internet2.middleware.shibboleth.common.util.StringResourceLoader.java

Source

/*
 * Licensed to the University Corporation for Advanced Internet Development, 
 * Inc. (UCAID) under one or more contributor license agreements.  See the 
 * NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The UCAID 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 edu.internet2.middleware.shibboleth.common.util;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

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.loader.ResourceLoader;
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;

/*
 * This class is a copy of revison 535935 from the Apache Velocity Engine project.  This is included here because 
 * at this time there is no released version of Velocity that contains the fix for bug:
 * https://issues.apache.org/jira/browse/VELOCITY-541
 * 
 * TODO remove this class when the next version of Velocity is available
 */

/**
 * 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 = &quot;/some/imaginary/path/hello.vm&quot;;
 * String myTemplate = &quot;Hi, ${username}... this is some template!&quot;;
 * 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(&quot;foo&quot;);
 * </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(&quot;foo&quot;);
 * </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(&quot;foo&quot;, repo);
 * </code></pre>
 * 
 * Or for a non-static repository:
 * 
 * <pre><code>
 * StringResourceRepository repo = new MyStringResourceRepository();
 * repo.magicallyAddSomeStringResources();
 * velocityEngine.setApplicationAttribute(&quot;foo&quot;, 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 535935
 */
public class StringResourceLoader extends ResourceLoader {
    /** Key to determine whether the repository should be set as the static one or not. */
    public static final String REPOSITORY_STATIC = "repository.static";

    /** By default, repositories are stored statically (shared across the VM). */
    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. */
    public static final String REPOSITORY_NAME = "repository.name";

    /**
     * The default name for string resource repositories
     * ('org.apache.velocity.runtime.resource.util.StringResourceRepository').
     */
    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.
     */
    public static StringResourceRepository getRepository(String name) {
        return (StringResourceRepository) STATIC_REPOSITORIES.get(name);
    }

    /**
     * Sets the specified {@link StringResourceRepository} in static storage under the specified name.
     */
    public static void setRepository(String name, StringResourceRepository repo) {
        STATIC_REPOSITORIES.put(name, repo);
    }

    /**
     * Removes the {@link StringResourceRepository} stored under the specified name.
     */
    public static StringResourceRepository removeRepository(String name) {
        return (StringResourceRepository) STATIC_REPOSITORIES.remove(name);
    }

    /**
     * Removes all statically stored {@link StringResourceRepository}s.
     */
    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.warn("Cannot change class of string repository '" + repoName + "' from "
                        + this.repository.getClass().getName() + " to " + repoClass);
            }

            // allow them to change the default encoding of the repo
            if (encoding != null && !this.repository.getEncoding().equals(encoding)) {
                if (log.isInfoEnabled()) {
                    log.info("Changing the default encoding of string repository '" + repoName + "' from "
                            + this.repository.getEncoding() + " to " + encoding);
                }
                this.repository.setEncoding(encoding);
            }
        }

        log.trace("StringResourceLoader : initialization complete.");
    }

    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;
    }

    /**
     * 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;
    }

}