org.apache.flex.forks.velocity.runtime.resource.ResourceManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.flex.forks.velocity.runtime.resource.ResourceManagerImpl.java

Source

package org.apache.flex.forks.velocity.runtime.resource;

/*
 * Copyright 2000-2001,2004 The Apache Software Foundation.
 * 
 * Licensed 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.ArrayList;
import java.util.Hashtable;
import java.util.Vector;

import java.io.InputStream;
import java.io.IOException;

import org.apache.flex.forks.velocity.runtime.RuntimeServices;
import org.apache.flex.forks.velocity.runtime.RuntimeConstants;

import org.apache.flex.forks.velocity.runtime.resource.ResourceFactory;
import org.apache.flex.forks.velocity.runtime.resource.loader.ResourceLoader;
import org.apache.flex.forks.velocity.runtime.resource.loader.ResourceLoaderFactory;

import org.apache.flex.forks.velocity.exception.ResourceNotFoundException;
import org.apache.flex.forks.velocity.exception.ParseErrorException;

import org.apache.commons.collections.ExtendedProperties;

/**
 * Class to manage the text resource for the Velocity
 * Runtime.
 *
 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 * @author <a href="mailto:paulo.gaspar@krankikom.de">Paulo Gaspar</a>
 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
 * @version $Id: ResourceManagerImpl.java,v 1.7.4.1 2004/03/03 23:23:01 geirm Exp $
 */
public class ResourceManagerImpl implements ResourceManager {
    /**
     * A template resources.
     */
    public static final int RESOURCE_TEMPLATE = 1;

    /**
     * A static content resource.
     */
    public static final int RESOURCE_CONTENT = 2;

    /**
     * token used to identify the loader internally
     */
    private static final String RESOURCE_LOADER_IDENTIFIER = "_RESOURCE_LOADER_IDENTIFIER_";

    /**
     *  Object implementing ResourceCache to 
     *  be our resource manager's Resource cache.
     */
    protected ResourceCache globalCache = null;

    /**
     * The List of templateLoaders that the Runtime will
     * use to locate the InputStream source of a template.
     */
    protected ArrayList resourceLoaders = new ArrayList();

    /**
     * This is a list of the template input stream source
     * initializers, basically properties for a particular
     * template stream source. The order in this list
     * reflects numbering of the properties i.e.
     *
     * <loader-id>.resource.loader.<property> = <value>
     */
    private ArrayList sourceInitializerList = new ArrayList();

    /**
     * This is a map of public name of the template
     * stream source to it's initializer. This is so
     * that clients of velocity can set properties of
     * a template source stream with its public name.
     * So for example, a client could set the 
     * File.resource.path property and this would
     * change the resource.path property for the
     * file template stream source.
     */
    private Hashtable sourceInitializerMap = new Hashtable();

    /**
     * Each loader needs a configuration object for
     * its initialization, this flags keeps track of whether
     * or not the configuration objects have been created
     * for the resource loaders.
     */
    private boolean resourceLoaderInitializersActive = false;

    /**
     *  switch to turn off log notice when a resource is found for
     *  the first time.
     */
    private boolean logWhenFound = true;

    protected RuntimeServices rsvc = null;

    /**
     * Initialize the ResourceManager.
     */
    public void initialize(RuntimeServices rs) throws Exception {
        rsvc = rs;

        rsvc.info("Default ResourceManager initializing. (" + this.getClass() + ")");

        ResourceLoader resourceLoader;

        assembleResourceLoaderInitializers();

        for (int i = 0; i < sourceInitializerList.size(); i++) {
            ExtendedProperties configuration = (ExtendedProperties) sourceInitializerList.get(i);
            String loaderClass = configuration.getString("class");

            if (loaderClass == null) {
                rsvc.error("Unable to find '" + configuration.getString(RESOURCE_LOADER_IDENTIFIER)
                        + ".resource.loader.class' specification in configuation."
                        + " This is a critical value.  Please adjust configuration.");
                continue;
            }

            resourceLoader = ResourceLoaderFactory.getLoader(rsvc, loaderClass);
            resourceLoader.commonInit(rsvc, configuration);
            resourceLoader.init(configuration);
            resourceLoaders.add(resourceLoader);

        }

        /*
         * now see if this is overridden by configuration
         */

        logWhenFound = rsvc.getBoolean(RuntimeConstants.RESOURCE_MANAGER_LOGWHENFOUND, true);

        /*
         *  now, is a global cache specified?
         */

        String claz = rsvc.getString(RuntimeConstants.RESOURCE_MANAGER_CACHE_CLASS);

        Object o = null;

        if (claz != null && claz.length() > 0) {
            try {
                o = Class.forName(claz).newInstance();
            } catch (ClassNotFoundException cnfe) {
                String err = "The specified class for ResourceCache (" + claz
                        + ") does not exist (or is not accessible to the current classlaoder).";
                rsvc.error(err);

                o = null;
            }

            if (!(o instanceof ResourceCache)) {
                String err = "The specified class for ResourceCache (" + claz
                        + ") does not implement org.apache.runtime.resource.ResourceCache."
                        + " ResourceManager. Using default ResourceCache implementation.";

                rsvc.error(err);

                o = null;
            }
        }

        /*
         *  if we didn't get through that, just use the default.
         */

        if (o == null)
            o = new ResourceCacheImpl();

        globalCache = (ResourceCache) o;

        globalCache.initialize(rsvc);

        rsvc.info("Default ResourceManager initialization complete.");

    }

    /**
     * This will produce a List of Hashtables, each
     * hashtable contains the intialization info for
     * a particular resource loader. This Hastable
     * will be passed in when initializing the
     * the template loader.
     */
    private void assembleResourceLoaderInitializers() {
        if (resourceLoaderInitializersActive) {
            return;
        }

        Vector resourceLoaderNames = rsvc.getConfiguration().getVector(RuntimeConstants.RESOURCE_LOADER);

        for (int i = 0; i < resourceLoaderNames.size(); i++) {
            /*
             * The loader id might look something like the following:
             *
             * file.resource.loader
             *
             * The loader id is the prefix used for all properties
             * pertaining to a particular loader.
             */
            String loaderID = resourceLoaderNames.get(i) + "." + RuntimeConstants.RESOURCE_LOADER;

            ExtendedProperties loaderConfiguration = rsvc.getConfiguration().subset(loaderID);

            /*
             *  we can't really count on ExtendedProperties to give us an empty set
             */

            if (loaderConfiguration == null) {
                rsvc.warn("ResourceManager : No configuration information for resource loader named '"
                        + resourceLoaderNames.get(i) + "'. Skipping.");
                continue;
            }

            /*
             *  add the loader name token to the initializer if we need it
             *  for reference later. We can't count on the user to fill
             *  in the 'name' field
             */

            loaderConfiguration.setProperty(RESOURCE_LOADER_IDENTIFIER, resourceLoaderNames.get(i));

            /*
             * Add resources to the list of resource loader
             * initializers.
             */
            sourceInitializerList.add(loaderConfiguration);
        }

        resourceLoaderInitializersActive = true;
    }

    /**
     * Gets the named resource.  Returned class type corresponds to specified type
     * (i.e. <code>Template</code> to <code>RESOURCE_TEMPLATE</code>).
     *
     * @param resourceName The name of the resource to retrieve.
     * @param resourceType The type of resource (<code>RESOURCE_TEMPLATE</code>,
     *                     <code>RESOURCE_CONTENT</code>, etc.).
     * @param encoding  The character encoding to use.
     * @return Resource with the template parsed and ready.
     * @throws ResourceNotFoundException if template not found
     *          from any available source.
     * @throws ParseErrorException if template cannot be parsed due
     *          to syntax (or other) error.
     * @throws Exception if a problem in parse
     */
    public Resource getResource(String resourceName, int resourceType, String encoding)
            throws ResourceNotFoundException, ParseErrorException, Exception {
        /* 
         * Check to see if the resource was placed in the cache.
         * If it was placed in the cache then we will use
         * the cached version of the resource. If not we
         * will load it.
         */

        Resource resource = globalCache.get(resourceName);

        if (resource != null) {
            /*
             *  refresh the resource
             */

            try {
                refreshResource(resource, encoding);
            } catch (ResourceNotFoundException rnfe) {
                /*
                 *  something exceptional happened to that resource
                 *  this could be on purpose, 
                 *  so clear the cache and try again
                 */

                globalCache.remove(resourceName);

                return getResource(resourceName, resourceType, encoding);
            } catch (ParseErrorException pee) {
                rsvc.error("ResourceManager.getResource() exception: " + pee);

                throw pee;
            } catch (Exception eee) {
                rsvc.error("ResourceManager.getResource() exception: " + eee);

                throw eee;
            }
        } else {
            try {
                /*
                 *  it's not in the cache, so load it.
                 */

                resource = loadResource(resourceName, resourceType, encoding);

                if (resource.getResourceLoader().isCachingOn()) {
                    globalCache.put(resourceName, resource);
                }
            } catch (ResourceNotFoundException rnfe2) {
                rsvc.error(
                        "ResourceManager : unable to find resource '" + resourceName + "' in any resource loader.");

                throw rnfe2;
            } catch (ParseErrorException pee) {
                rsvc.error("ResourceManager.getResource() parse exception: " + pee);

                throw pee;
            } catch (Exception ee) {
                rsvc.error("ResourceManager.getResource() exception new: " + ee);

                throw ee;
            }
        }

        return resource;
    }

    /**
     *  Loads a resource from the current set of resource loaders
     *
     * @param resourceName The name of the resource to retrieve.
     * @param resourceType The type of resource (<code>RESOURCE_TEMPLATE</code>,
     *                     <code>RESOURCE_CONTENT</code>, etc.).
     * @param encoding  The character encoding to use.
     * @return Resource with the template parsed and ready.
     * @throws ResourceNotFoundException if template not found
     *          from any available source.
     * @throws ParseErrorException if template cannot be parsed due
     *          to syntax (or other) error.
     * @throws Exception if a problem in parse
     */
    protected Resource loadResource(String resourceName, int resourceType, String encoding)
            throws ResourceNotFoundException, ParseErrorException, Exception {
        Resource resource = ResourceFactory.getResource(resourceName, resourceType);

        resource.setRuntimeServices(rsvc);

        resource.setName(resourceName);
        resource.setEncoding(encoding);

        /* 
         * Now we have to try to find the appropriate
         * loader for this resource. We have to cycle through
         * the list of available resource loaders and see
         * which one gives us a stream that we can use to
         * make a resource with.
         */

        long howOldItWas = 0; // Initialize to avoid warnings

        ResourceLoader resourceLoader = null;

        for (int i = 0; i < resourceLoaders.size(); i++) {
            resourceLoader = (ResourceLoader) resourceLoaders.get(i);
            resource.setResourceLoader(resourceLoader);

            /*
             *  catch the ResourceNotFound exception
             *  as that is ok in our new multi-loader environment
             */

            try {
                if (resource.process()) {
                    /*
                     *  FIXME  (gmj)
                     *  moved in here - technically still 
                     *  a problem - but the resource needs to be 
                     *  processed before the loader can figure 
                     *  it out due to to the new 
                     *  multi-path support - will revisit and fix
                     */

                    if (logWhenFound) {
                        rsvc.info("ResourceManager : found " + resourceName + " with loader "
                                + resourceLoader.getClassName());
                    }

                    howOldItWas = resourceLoader.getLastModified(resource);
                    break;
                }
            } catch (ResourceNotFoundException rnfe) {
                /*
                 *  that's ok - it's possible to fail in
                 *  multi-loader environment
                 */
            }
        }

        /*
         * Return null if we can't find a resource.
         */
        if (resource.getData() == null) {
            throw new ResourceNotFoundException("Unable to find resource '" + resourceName + "'");
        }

        /*
         *  some final cleanup
         */

        resource.setLastModified(howOldItWas);

        resource.setModificationCheckInterval(resourceLoader.getModificationCheckInterval());

        resource.touch();

        return resource;
    }

    /**
     *  Takes an existing resource, and 'refreshes' it.  This
     *  generally means that the source of the resource is checked
     *  for changes according to some cache/check algorithm
     *  and if the resource changed, then the resource data is
     *  reloaded and re-parsed.
     *
     *  @param resource resource to refresh
     *
     * @throws ResourceNotFoundException if template not found
     *          from current source for this Resource
     * @throws ParseErrorException if template cannot be parsed due
     *          to syntax (or other) error.
     * @throws Exception if a problem in parse
     */
    protected void refreshResource(Resource resource, String encoding)
            throws ResourceNotFoundException, ParseErrorException, Exception {
        /* 
         * The resource knows whether it needs to be checked
         * or not, and the resource's loader can check to
         * see if the source has been modified. If both
         * these conditions are true then we must reload
         * the input stream and parse it to make a new
         * AST for the resource.
         */
        if (resource.requiresChecking()) {
            /*
             *  touch() the resource to reset the counters
             */

            resource.touch();

            if (resource.isSourceModified()) {
                /*
                 *  now check encoding info.  It's possible that the newly declared
                 *  encoding is different than the encoding already in the resource
                 *  this strikes me as bad...
                 */

                if (!resource.getEncoding().equals(encoding)) {
                    rsvc.error("Declared encoding for template '" + resource.getName()
                            + "' is different on reload.  Old = '" + resource.getEncoding() + "'  New = '"
                            + encoding);

                    resource.setEncoding(encoding);
                }

                /*
                 *  read how old the resource is _before_
                 *  processing (=>reading) it
                 */
                long howOldItWas = resource.getResourceLoader().getLastModified(resource);

                /*
                 *  read in the fresh stream and parse
                 */

                resource.process();

                /*
                 *  now set the modification info and reset
                 *  the modification check counters
                 */

                resource.setLastModified(howOldItWas);
            }
        }
    }

    /**
    * Gets the named resource.  Returned class type corresponds to specified type
    * (i.e. <code>Template</code> to <code>RESOURCE_TEMPLATE</code>).
    *
    * @param resourceName The name of the resource to retrieve.
    * @param resourceType The type of resource (<code>RESOURCE_TEMPLATE</code>,
    *                     <code>RESOURCE_CONTENT</code>, etc.).
    * @return Resource with the template parsed and ready.
    * @throws ResourceNotFoundException if template not found
    *          from any available source.
    * @throws ParseErrorException if template cannot be parsed due
    *          to syntax (or other) error.
    * @throws Exception if a problem in parse
    *
    *  @deprecated Use
    *  {@link #getResource(String resourceName, int resourceType, 
    *          String encoding )}
    */
    public Resource getResource(String resourceName, int resourceType)
            throws ResourceNotFoundException, ParseErrorException, Exception {
        return getResource(resourceName, resourceType, RuntimeConstants.ENCODING_DEFAULT);
    }

    /**
     *  Determines is a template exists, and returns name of the loader that 
     *  provides it.  This is a slightly less hokey way to support
     *  the Velocity.templateExists() utility method, which was broken
     *  when per-template encoding was introduced.  We can revisit this.
     *
     *  @param resourceName Name of template or content resource
     *  @return class name of loader than can provide it
     */
    public String getLoaderNameForResource(String resourceName) {
        ResourceLoader resourceLoader = null;

        /*
         *  loop through our loaders...
         */
        for (int i = 0; i < resourceLoaders.size(); i++) {
            resourceLoader = (ResourceLoader) resourceLoaders.get(i);

            InputStream is = null;

            /*
             *  if we find one that can provide the resource,
             *  return the name of the loaders's Class
             */
            try {
                is = resourceLoader.getResourceStream(resourceName);

                if (is != null) {
                    return resourceLoader.getClass().toString();
                }
            } catch (ResourceNotFoundException e) {
                /*
                 * this isn't a problem.  keep going
                 */
            } finally {
                /*
                 *  if we did find one, clean up because we were 
                 *  returned an open stream
                 */
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException ioe) {
                    }
                }
            }
        }

        return null;
    }
}