Package org.pentaho.reporting.libraries.resourceloader

Source Code of org.pentaho.reporting.libraries.resourceloader.ResourceManager

/**
* ================================================
* LibLoader : a free Java resource loading library
* ================================================
*
* Project Info:  http://reporting.pentaho.org/libloader/
*
* (C) Copyright 2006-2008, by Pentaho Corporation and Contributors.
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
*
* ------------
* ResourceManager.java
* ------------
*/
package org.pentaho.reporting.libraries.resourceloader;

import java.net.URL;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.resourceloader.cache.NullResourceBundleDataCache;
import org.pentaho.reporting.libraries.resourceloader.cache.NullResourceDataCache;
import org.pentaho.reporting.libraries.resourceloader.cache.NullResourceFactoryCache;
import org.pentaho.reporting.libraries.resourceloader.cache.ResourceBundleDataCache;
import org.pentaho.reporting.libraries.resourceloader.cache.ResourceDataCache;
import org.pentaho.reporting.libraries.resourceloader.cache.ResourceDataCacheProvider;
import org.pentaho.reporting.libraries.resourceloader.cache.ResourceFactoryCache;
import org.pentaho.reporting.libraries.resourceloader.cache.ResourceFactoryCacheProvider;
import org.pentaho.reporting.libraries.resourceloader.cache.ResourceBundleDataCacheEntry;
import org.pentaho.reporting.libraries.resourceloader.cache.ResourceDataCacheEntry;
import org.pentaho.reporting.libraries.resourceloader.cache.ResourceBundleDataCacheProvider;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;

/**
* The resource manager takes care about the loaded resources, performs caching, if needed and is the central instance
* when dealing with resources. Resource loading is a two-step process. In the first step, the {@link ResourceLoader}
* accesses the physical storage or network connection to read in the binary data. The loaded {@link ResourceData}
* carries versioning information with it an can be cached indendently from the produced result. Once the loading is
* complete, a {@link ResourceFactory} interprets the binary data and produces a Java-Object from it.
* <p/>
* Resources are identified by an Resource-Key and some optional loader parameters (which can be used to parametrize the
* resource-factories).
*
* @author Thomas Morgner
* @see ResourceData
* @see ResourceLoader
* @see ResourceFactory
*/
public final class ResourceManager
{
  private static Log logger = LogFactory.getLog(ResourceManager.class);
  private ResourceManagerBackend backend;

  public static final String BUNDLE_LOADER_PREFIX = "org.pentaho.reporting.libraries.resourceloader.bundle.loader.";
  public static final String LOADER_PREFIX = "org.pentaho.reporting.libraries.resourceloader.loader.";
  public static final String FACTORY_TYPE_PREFIX = "org.pentaho.reporting.libraries.resourceloader.factory.type.";
  public static final String BUNDLE_CACHE_PROVIDER_KEY = "org.pentaho.reporting.libraries.resourceloader.cache.BundleDataCacheProvider";
  public static final String DATA_CACHE_PROVIDER_KEY = "org.pentaho.reporting.libraries.resourceloader.cache.DataCacheProvider";
  public static final String FACTORY_CACHE_PROVIDER_KEY = "org.pentaho.reporting.libraries.resourceloader.cache.FactoryCacheProvider";

  private ResourceDataCache dataCache;
  private ResourceBundleDataCache bundleCache;
  private ResourceFactoryCache factoryCache;

  /**
   * A set that contains the class-names of all cache-modules, which could not be instantiated correctly.
   * This set is used to limit the number of warnings in the log to exactly one per class.
   */
  private static final Set failedModules = new HashSet();

  /**
   * Default Constructor.
   */
  public ResourceManager()
  {
    this(new DefaultResourceManagerBackend());
  }

  public ResourceManager(final ResourceManagerBackend resourceManagerBackend)
  {
    if (resourceManagerBackend == null)
    {
      throw new NullPointerException();
    }
    this.backend = resourceManagerBackend;
    this.bundleCache = new NullResourceBundleDataCache();
    this.dataCache = new NullResourceDataCache();
    this.factoryCache = new NullResourceFactoryCache();
  }

  public ResourceManager(final ResourceManager parent, final ResourceManagerBackend backend)
  {
    if (backend == null)
    {
      throw new NullPointerException();
    }
    if (parent == null)
    {
      throw new NullPointerException();
    }
   
    this.backend = backend;
    this.bundleCache = parent.getBundleCache();
    this.dataCache = parent.getDataCache();
    this.factoryCache = parent.getFactoryCache();
  }

  public ResourceManagerBackend getBackend()
  {
    return backend;
  }

  /**
   * Creates a ResourceKey that carries no Loader-Parameters from the given object.
   *
   * @param data the key-data
   * @return the generated resource-key, never null.
   * @throws ResourceKeyCreationException if the key-creation failed.
   */
  public ResourceKey createKey(final Object data)
      throws ResourceKeyCreationException
  {
    return createKey(data, null);
  }

  /**
   * Creates a ResourceKey that carries the given Loader-Parameters contained in the optional map.
   *
   * @param data       the key-data
   * @param parameters an optional map of parameters.
   * @return the generated resource-key, never null.
   * @throws ResourceKeyCreationException if the key-creation failed.
   */
  public ResourceKey createKey(final Object data, final Map parameters)
      throws ResourceKeyCreationException
  {
    return backend.createKey(data, parameters);
  }

  /**
   * Derives a new key from the given resource-key. Only keys for a hierarchical storage system (like file-systems or
   * URLs) can have derived keys. Since LibLoader 0.3.0 only hierarchical keys can be derived. For that, the deriving
   * path must be given as String.
   * <p/>
   * Before trying to derive the key, the system tries to interpret the path as absolute key-value.
   *
   * @param parent the parent key, must never be null
   * @param path   the relative path, that is used to derive the key.
   * @return the derived key.
   * @throws ResourceKeyCreationException if deriving the key failed.
   */
  public ResourceKey deriveKey(final ResourceKey parent, final String path)
      throws ResourceKeyCreationException
  {
    return deriveKey(parent, path, null);
  }

  /**
   * Derives a new key from the given resource-key. Only keys for a hierarchical storage system (like file-systems or
   * URLs) can have derived keys. Since LibLoader 0.3.0 only hierarchical keys can be derived. For that, the deriving
   * path must be given as String.
   * <p/>
   * The optional parameter-map will be applied to the derived key after the parent's parameters have been copied to
   * the new key.
   * <p/>
   * Before trying to derive the key, the system tries to interpret the path as absolute key-value.
   *
   * @param parent     the parent key, or null to interpret the path as absolute key.
   * @param path       the relative path, that is used to derive the key.
   * @param parameters a optional map containing resource-key parameters.
   * @return the derived key.
   * @throws ResourceKeyCreationException if deriving the key failed.
   */
  public ResourceKey deriveKey(final ResourceKey parent, final String path, final Map parameters)
      throws ResourceKeyCreationException
  {
    return backend.deriveKey(parent, path, parameters);
  }
  /**
   * Tries to convert the resource-key into an URL. Not all resource-keys have an URL representation. This method
   * exists to make it easier to connect LibLoader to other resource-loading frameworks.
   *
   * @param key the resource-key
   * @return the URL for the key, or null if there is no such key.
   */
  public URL toURL(final ResourceKey key)
  {
    return backend.toURL(key);
  }

  public Resource createDirectly(final Object keyValue, final Class target)
      throws ResourceLoadingException,
      ResourceCreationException,
      ResourceKeyCreationException
  {
    final ResourceKey key = createKey(keyValue);
    return create(key, null, target);
  }


  /**
   * Tries to find the first resource-bundle-loader that would be able to process the key.
   *
   * @param key the resource-key.
   * @return the resourceloader for that key, or null, if no resource-loader is able to process the key.
   * @throws ResourceLoadingException if an error occured.
   */
  public synchronized ResourceBundleData loadResourceBundle(final ResourceKey key) throws ResourceLoadingException
  {
    final ResourceBundleDataCache bundleCache = getBundleCache();
    final ResourceBundleDataCacheEntry cached = bundleCache.get(key);
    if (cached != null)
    {
      final ResourceBundleData data = cached.getData();
      // check, whether it is valid.

      final long version = data.getVersion(this);
      if ((cached.getStoredVersion() < 0) ||
          (version >= 0 && cached.getStoredVersion() == version))
      {
        // now also make sure that the underlying data has not changed.
        // This may look a bit superfluous, but the repository may not provide
        // sensible cacheable information.
        final ResourceDataCacheEntry bundleRawDataCacheEntry = getDataCache().get(data.getBundleKey());
        if (bundleRawDataCacheEntry != null)
        {
          final ResourceData bundleRawData = bundleRawDataCacheEntry.getData();
          if (bundleRawData != null)
          {
            if (isValidData(bundleRawDataCacheEntry, bundleRawData))
            {
              return data;
            }
            getDataCache().remove(bundleRawData);
          }
        }
      }
      bundleCache.remove(data);
    }

    final ResourceBundleData data = backend.loadResourceBundle(this, key);
    if (data != null)
    {
      bundleCache.put(this, data);
    }
    return data;
  }

  public ResourceData load(final ResourceKey key) throws ResourceLoadingException
  {
    final ResourceBundleData bundle = loadResourceBundle(key);
    if (bundle != null)
    {
      logger.debug ("Loaded bundle for key " + key);
      return bundle;
    }
    final ResourceKey parent = key.getParent();
    if (parent != null)
    {
      // try to load the bundle data of the parent
      final ResourceBundleData parentData = loadResourceBundle(parent);
      if (parentData != null)
      {
        logger.debug ("Loaded bundle for key (derivate) " + key);
        return parentData.deriveData(key);
      }
    }
    return loadRawData(key);
  }

  private boolean isValidData (final ResourceDataCacheEntry cached,
                               final ResourceData data) throws ResourceLoadingException
  {
    // check, whether it is valid.
    if (cached.getStoredVersion() < 0)
    {
      // a non versioned entry is always valid. (Maybe this is from a Jar-URL?)
      return true;
    }

    final long version = data.getVersion(this);
    if (version < 0)
    {
      // the system is no longer able to retrieve the version information?
      // (but versioning information must have been available in the past)
      // oh, that's bad. Assume the worst and re-read the data.
      return false;
    }
    if (cached.getStoredVersion() == version)
    {
      return true;
    }
    else
    {
      return false;
    }
  }

  public synchronized ResourceData loadRawData(final ResourceKey key)
      throws UnrecognizedLoaderException, ResourceLoadingException
  {
    final ResourceDataCache dataCache = getDataCache();
    // Alternative 3: This is a plain resource and not contained in a bundle. Load as binary data
    final ResourceDataCacheEntry cached = dataCache.get(key);
    if (cached != null)
    {
      final ResourceData data = cached.getData();
      if (data != null)
      {
        if (isValidData(cached, data))
        {
          return data;
        }
        dataCache.remove(data);
      }
    }

    final ResourceData data = backend.loadRawData(this, key);
    dataCache.put(this, data);
    return data;
  }

  public Resource create(final ResourceKey key, final ResourceKey context, final Class target)
      throws ResourceLoadingException, ResourceCreationException
  {
    if (target == null)
    {
      throw new NullPointerException("Target must not be null");
    }
    if (key == null)
    {
      throw new NullPointerException("Key must not be null.");
    }
    return create(key, context, new Class[]{target});
  }

  public Resource create(final ResourceKey key, final ResourceKey context)
      throws ResourceLoadingException, ResourceCreationException
  {
    return create(key, context, (Class[]) null);
  }

  public Resource create(final ResourceKey key, final ResourceKey context, final Class[] target)
      throws ResourceLoadingException, ResourceCreationException
  {
    if (key == null)
    {
      throw new NullPointerException();
    }

    final ResourceFactoryCache factoryCache = getFactoryCache();
    // ok, we have a handle to the data, and the data is current.
    // Lets check whether we also have a cached result.
    final Resource resource = factoryCache.get(key);
    if (resource != null)
    {
      if (backend.isResourceUnchanged(this, resource))
      {
        // mama, look i am a good cache manager ...
        return resource;
      }
      else
      {
        // someone evil changed one of the dependent resources ...
        factoryCache.remove(resource);
      }
    }

    final ResourceData loadedData = load(key);
    final Resource newResource = backend.create(this, loadedData, context, target);
    factoryCache.put(newResource);
    return newResource;
  }


  public ResourceDataCache getDataCache()
  {
    return dataCache;
  }

  public void setDataCache(final ResourceDataCache dataCache)
  {
    if (dataCache == null)
    {
      throw new NullPointerException();
    }
    this.dataCache = dataCache;
  }

  public ResourceFactoryCache getFactoryCache()
  {
    return factoryCache;
  }

  public void setFactoryCache(final ResourceFactoryCache factoryCache)
  {
    if (factoryCache == null)
    {
      throw new NullPointerException();
    }
    this.factoryCache = factoryCache;
  }

  public ResourceBundleDataCache getBundleCache()
  {
    return bundleCache;
  }

  public void setBundleCache(final ResourceBundleDataCache bundleCache)
  {
    if (bundleCache == null)
    {
      throw new NullPointerException();
    }
    this.bundleCache = bundleCache;
  }

  public void registerDefaults()
  {
    // Create all known resource loaders ...
    registerDefaultLoaders();

    // Register all known factories ...
    registerDefaultFactories();
    // add the caches ..
    registerDataCache();
    registerBundleDataCache();
    registerFactoryCache();
  }

  public void registerDefaultFactories()
  {
    backend.registerDefaultFactories();
  }

  public void registerBundleDataCache()
  {
    final Configuration config = LibLoaderBoot.getInstance().getGlobalConfig();
    final String dataCacheProviderClass =
        config.getConfigProperty(BUNDLE_CACHE_PROVIDER_KEY);
    if (dataCacheProviderClass == null)
    {
      return;
    }
    final Object maybeDataCacheProvider =
        ObjectUtilities.loadAndInstantiate
            (dataCacheProviderClass, ResourceManager.class, ResourceBundleDataCacheProvider.class);
    if (maybeDataCacheProvider == null)
    {
      return;
    }
   
    final ResourceBundleDataCacheProvider provider = (ResourceBundleDataCacheProvider) maybeDataCacheProvider;
    try
      {
        final ResourceBundleDataCache cache = provider.createBundleDataCache();
      if (cache != null)
      {
        setBundleCache(cache);
      }
    }
    catch (Throwable e)
    {
      // ok, did not work ...
      synchronized (failedModules)
      {
        if (failedModules.contains(dataCacheProviderClass) == false)
        {
          logger.warn("Failed to create data cache: " + e.getLocalizedMessage());
          failedModules.add(dataCacheProviderClass);
        }
      }
    }
  }

  public void registerDataCache()
  {
    final Configuration config = LibLoaderBoot.getInstance().getGlobalConfig();
    final String dataCacheProviderClass =
        config.getConfigProperty(DATA_CACHE_PROVIDER_KEY);
    if (dataCacheProviderClass == null)
    {
      return;
    }
    final Object maybeDataCacheProvider =
        ObjectUtilities.loadAndInstantiate
            (dataCacheProviderClass, ResourceManager.class, ResourceDataCacheProvider.class);
    if (maybeDataCacheProvider instanceof ResourceDataCacheProvider)
    {
      final ResourceDataCacheProvider provider = (ResourceDataCacheProvider) maybeDataCacheProvider;
      try
      {
        final ResourceDataCache cache = provider.createDataCache();
        if (cache != null)
        {
          setDataCache(cache);
        }
      }
      catch (Throwable e)
      {
        // ok, did not work ...
        synchronized (failedModules)
        {
          if (failedModules.contains(dataCacheProviderClass) == false)
          {
            logger.warn("Failed to create data cache: " + e.getLocalizedMessage());
            failedModules.add(dataCacheProviderClass);
          }
        }
      }
    }
  }

  public void registerFactoryCache()
  {
    final Configuration config = LibLoaderBoot.getInstance().getGlobalConfig();
    final String cacheProviderClass = config.getConfigProperty
        (FACTORY_CACHE_PROVIDER_KEY);
    if (cacheProviderClass == null)
    {
      return;
    }
    final Object maybeCacheProvider = ObjectUtilities.loadAndInstantiate
        (cacheProviderClass, ResourceManager.class, ResourceFactoryCacheProvider.class);

    if (maybeCacheProvider != null)
    {
      final ResourceFactoryCacheProvider provider = (ResourceFactoryCacheProvider) maybeCacheProvider;
      try
      {
        final ResourceFactoryCache cache = provider.createFactoryCache();
        if (cache != null)
        {
          setFactoryCache(cache);
        }
      }
      catch (Throwable e)
      {
        synchronized (failedModules)
        {
          if (failedModules.contains(cacheProviderClass) == false)
          {
            logger.warn("Failed to create factory cache: " + e.getLocalizedMessage());
            failedModules.add(cacheProviderClass);
          }
        }
      }
    }
  }

  public void registerDefaultLoaders()
  {
    backend.registerDefaultLoaders();
  }

  public void registerBundleLoader(final ResourceBundleLoader loader)
  {
    if (loader == null)
    {
      throw new NullPointerException();
    }
    backend.registerBundleLoader(loader);
  }

  public void registerLoader(final ResourceLoader loader)
  {
    if (loader == null)
    {
      throw new NullPointerException();
    }
    backend.registerLoader(loader);
  }

  public void registerFactory(final ResourceFactory factory)
  {
    if (factory == null)
    {
      throw new NullPointerException();
    }
    backend.registerFactory(factory);
  }

  public void shutDown()
  {
    factoryCache.shutdown();
    dataCache.shutdown();
  }
}
TOP

Related Classes of org.pentaho.reporting.libraries.resourceloader.ResourceManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.