Author: henning Date: Sat Nov 4 11:08:45 2006 New Revision: 471247 URL: http://svn.apache.org/viewvc?view=rev&rev=471247 Log: Major rewrite of the ResourceManager implementation to use Iterators and clear up the init logic. Mostly happened while hunting for the template loading race condition found here.
Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java?view=diff&rev=471247&r1=471246&r2=471247 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java (original) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java Sat Nov 4 11:08:45 2006 @@ -16,12 +16,14 @@ * "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. + * under the License. */ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.Vector; import org.apache.commons.collections.ExtendedProperties; @@ -35,171 +37,163 @@ import org.apache.velocity.util.ClassUtils; import org.apache.velocity.util.StringUtils; + /** - * Class to manage the text resource for the Velocity - * Runtime. + * Class to manage the text resource for the Velocity Runtime. * - * @author <a href="mailto:[EMAIL PROTECTED]">Will Glass-Husain</a> - * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a> - * @author <a href="mailto:[EMAIL PROTECTED]">Paulo Gaspar</a> - * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a> - * @version $Id$ + * @author <a href="mailto:[EMAIL PROTECTED]">Will Glass-Husain</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Paulo Gaspar</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Henning P. Schmiedehausen</a> + * @version $Id$ */ -public class ResourceManagerImpl implements ResourceManager +public class ResourceManagerImpl + implements ResourceManager { - /** - * A template resources. - */ + + /** A template resources. */ public static final int RESOURCE_TEMPLATE = 1; - /** - * A static content resource. - */ + /** A static content resource. */ public static final int RESOURCE_CONTENT = 2; - /** - * token used to identify the loader internally - */ + /** 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. - */ + /** 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(); + /** The List of templateLoaders that the Runtime will use to locate the InputStream source of a template. */ + protected final List 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. + * 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> + * <p><loader-id>.resource.loader.<property> = <value></p> */ - private ArrayList sourceInitializerList = new ArrayList(); + private final List sourceInitializerList = new ArrayList(); /** - * 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. + * Has this Manager been initialized? */ - private boolean resourceLoaderInitializersActive = false; + private boolean isInit = false; - /** - * switch to turn off log notice when a resource is found for - * the first time. - */ - private boolean logWhenFound = true; + /** switch to turn off log notice when a resource is found for the first time. */ + private boolean logWhenFound = true; + /** The internal RuntimeServices object. */ protected RuntimeServices rsvc = null; + + /** Logging. */ protected Log log = null; /** * Initialize the ResourceManager. - * @param rs - * @throws Exception + * + * @param rs The Runtime Services object which is associated with this Resource Manager. + * + * @throws Exception */ - public void initialize( RuntimeServices rs ) + public synchronized void initialize(final RuntimeServices rsvc) throws Exception { - rsvc = rs; + if (isInit) + { + log.warn("Re-initialization of ResourceLoader attempted!"); + return; + } + + ResourceLoader resourceLoader = null; + + this.rsvc = rsvc; log = rsvc.getLog(); log.debug("Default ResourceManager initializing. (" + this.getClass() + ")"); - ResourceLoader resourceLoader; - assembleResourceLoaderInitializers(); - for (int i = 0; i < sourceInitializerList.size(); i++) + for (Iterator it = sourceInitializerList.iterator(); it.hasNext();) { /** * Resource loader can be loaded either via class name or be passed * in as an instance. */ - ExtendedProperties configuration = - (ExtendedProperties) sourceInitializerList.get(i); + ExtendedProperties configuration = (ExtendedProperties) it.next(); + String loaderClass = StringUtils.nullTrim(configuration.getString("class")); - ResourceLoader loaderInstance = - (ResourceLoader) configuration.get("instance"); + ResourceLoader loaderInstance = (ResourceLoader) configuration.get("instance"); - if ( (loaderClass == null) && (loaderInstance == null) ) + if (loaderInstance != null) { - log.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 = loaderInstance; } - else if ( loaderInstance != null ) + else if (loaderClass != null) { - resourceLoader = loaderInstance; - + resourceLoader = ResourceLoaderFactory.getLoader(rsvc, loaderClass); } else { - resourceLoader = ResourceLoaderFactory.getLoader( rsvc, loaderClass); + log.error("Unable to find '" + + configuration.getString(RESOURCE_LOADER_IDENTIFIER) + + ".resource.loader.class' specification in configuation." + + " This is a critical value. Please adjust configuration."); + + continue; // for(... } - resourceLoader.commonInit( rsvc, configuration); + 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 ); + logWhenFound = rsvc.getBoolean(RuntimeConstants.RESOURCE_MANAGER_LOGWHENFOUND, true); /* * now, is a global cache specified? */ - String claz = rsvc.getString( RuntimeConstants.RESOURCE_MANAGER_CACHE_CLASS ); + String cacheClassName = rsvc.getString(RuntimeConstants.RESOURCE_MANAGER_CACHE_CLASS); - Object o = null; + Object cacheObject = null; - if ( claz != null && claz.length() > 0 ) + if (org.apache.commons.lang.StringUtils.isNotEmpty(cacheClassName)) { + try { - o = ClassUtils.getNewInstance( claz ); + cacheObject = ClassUtils.getNewInstance(cacheClassName); } - catch (ClassNotFoundException cnfe ) + catch (ClassNotFoundException cnfe) { - log.error("The specified class for ResourceCache (" + claz + + log.error("The specified class for ResourceCache (" + cacheClassName + ") does not exist or is not accessible to the current classloader."); - o = null; + cacheObject = null; } - if (!(o instanceof ResourceCache) ) + if (!(cacheObject instanceof ResourceCache)) { - log.error("The specified class for ResourceCache (" + claz + + log.error("The specified class for ResourceCache (" + cacheClassName + ") does not implement " + ResourceCache.class.getName() + " ResourceManager. Using default ResourceCache implementation."); - o = null; + cacheObject = null; } } /* * if we didn't get through that, just use the default. */ - if (o == null) + if (cacheObject == null) { - o = new ResourceCacheImpl(); + cacheObject = new ResourceCacheImpl(); } - globalCache = (ResourceCache)o; + globalCache = (ResourceCache) cacheObject; globalCache.initialize(rsvc); @@ -207,25 +201,17 @@ } /** - * 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. + * This will produce a List of Hashtables, each hashtable contains the intialization info for a particular resource loader. This + * Hashtable will be passed in when initializing the the template loader. */ private void assembleResourceLoaderInitializers() { - if (resourceLoaderInitializersActive) - { - return; - } - - Vector resourceLoaderNames = - rsvc.getConfiguration().getVector(RuntimeConstants.RESOURCE_LOADER); + Vector resourceLoaderNames = rsvc.getConfiguration().getVector(RuntimeConstants.RESOURCE_LOADER); StringUtils.trimStrings(resourceLoaderNames); - for (int i = 0; i < resourceLoaderNames.size(); i++) + for (Iterator it = resourceLoaderNames.iterator(); it.hasNext(); ) { + /* * The loader id might look something like the following: * @@ -234,20 +220,22 @@ * The loader id is the prefix used for all properties * pertaining to a particular loader. */ - String loaderID = - resourceLoaderNames.get(i) + "." + RuntimeConstants.RESOURCE_LOADER; + String loaderName = (String) it.next(); + StringBuffer loaderID = new StringBuffer(loaderName); + loaderID.append(".").append(RuntimeConstants.RESOURCE_LOADER); - ExtendedProperties loaderConfiguration = - rsvc.getConfiguration().subset(loaderID); + ExtendedProperties loaderConfiguration = + rsvc.getConfiguration().subset(loaderID.toString()); /* * we can't really count on ExtendedProperties to give us an empty set */ - if ( loaderConfiguration == null) + if (loaderConfiguration == null) { - log.warn("ResourceManager : No configuration information for resource loader named '" - + resourceLoaderNames.get(i) + "'. Skipping."); + log.warn("ResourceManager : No configuration information for resource loader named '" + + loaderName + "'. Skipping."); + continue; } @@ -257,7 +245,7 @@ * in the 'name' field */ - loaderConfiguration.setProperty( RESOURCE_LOADER_IDENTIFIER, resourceLoaderNames.get(i)); + loaderConfiguration.setProperty(RESOURCE_LOADER_IDENTIFIER, loaderName); /* * Add resources to the list of resource loader @@ -265,27 +253,26 @@ */ 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>). + * 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 synchronized Resource getResource(String resourceName, int resourceType, String encoding ) - throws ResourceNotFoundException, ParseErrorException, Exception + * @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 synchronized Resource getResource(final String resourceName, final int resourceType, final String encoding) + throws ResourceNotFoundException, + ParseErrorException, + Exception { /* * Check to see if the resource was placed in the cache. @@ -300,7 +287,7 @@ String resourceKey = resourceType + resourceName; Resource resource = globalCache.get(resourceKey); - if( resource != null) + if (resource != null) { /* * refresh the resource @@ -308,9 +295,9 @@ try { - refreshResource( resource, encoding ); + refreshResource(resource, encoding); } - catch( ResourceNotFoundException rnfe ) + catch (ResourceNotFoundException rnfe) { /* * something exceptional happened to that resource @@ -318,19 +305,23 @@ * so clear the cache and try again */ - globalCache.remove( resourceKey ); + globalCache.remove(resourceKey); - return getResource( resourceName, resourceType, encoding ); + return getResource(resourceName, resourceType, encoding); } - catch( ParseErrorException pee ) + catch (ParseErrorException pee) { log.error("ResourceManager.getResource() exception", pee); throw pee; } - catch( Exception eee ) + catch (RuntimeException re) { - log.error("ResourceManager.getResource() exception", eee); - throw eee; + throw re; + } + catch (Exception e) + { + log.error("ResourceManager.getResource() exception", e); + throw e; } } else @@ -341,28 +332,32 @@ * it's not in the cache, so load it. */ - resource = loadResource( resourceName, resourceType, encoding ); + resource = loadResource(resourceName, resourceType, encoding); if (resource.getResourceLoader().isCachingOn()) { globalCache.put(resourceKey, resource); } } - catch( ResourceNotFoundException rnfe2 ) + catch (ResourceNotFoundException rnfe) { log.error("ResourceManager : unable to find resource '" + resourceName + "' in any resource loader."); - throw rnfe2; + throw rnfe; } - catch( ParseErrorException pee ) + catch (ParseErrorException pee) { log.error("ResourceManager.getResource() parse exception", pee); throw pee; } - catch( Exception ee ) + catch (RuntimeException re) { - log.error("ResourceManager.getResource() exception new", ee); - throw ee; + throw re; + } + catch (Exception e) + { + log.error("ResourceManager.getResource() exception new", e); + throw e; } } @@ -370,28 +365,29 @@ } /** - * Loads a resource from the current set of resource loaders + * 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 + * @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.setRuntimeServices(rsvc); - resource.setName( resourceName ); - resource.setEncoding( encoding ); + resource.setName(resourceName); + resource.setEncoding(encoding); /* * Now we have to try to find the appropriate @@ -401,13 +397,11 @@ * make a resource with. */ - long howOldItWas = 0; // Initialize to avoid warnings + long howOldItWas = 0; - ResourceLoader resourceLoader = null; - - for (int i = 0; i < resourceLoaders.size(); i++) + for (Iterator it = resourceLoaders.iterator(); it.hasNext();) { - resourceLoader = (ResourceLoader) resourceLoaders.get(i); + ResourceLoader resourceLoader = (ResourceLoader) it.next(); resource.setResourceLoader(resourceLoader); /* @@ -417,29 +411,31 @@ 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 && log.isDebugEnabled()) - { - log.debug("ResourceManager : found " + resourceName + + /* + * 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 && log.isDebugEnabled()) + { + log.debug("ResourceManager : found " + resourceName + " with loader " + resourceLoader.getClassName()); - } + } + + howOldItWas = resourceLoader.getLastModified(resource); - howOldItWas = resourceLoader.getLastModified( resource ); - break; - } + break; + } } - catch( ResourceNotFoundException rnfe ) + catch (ResourceNotFoundException rnfe) { /* * that's ok - it's possible to fail in @@ -453,18 +449,15 @@ */ if (resource.getData() == null) { - throw new ResourceNotFoundException( - "Unable to find resource '" + resourceName + "'"); + throw new ResourceNotFoundException("Unable to find resource '" + resourceName + "'"); } /* * some final cleanup */ - resource.setLastModified( howOldItWas ); - - resource.setModificationCheckInterval( - resourceLoader.getModificationCheckInterval()); + resource.setLastModified(howOldItWas); + resource.setModificationCheckInterval(resource.getResourceLoader().getModificationCheckInterval()); resource.touch(); @@ -472,23 +465,22 @@ } /** - * 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 + * 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 + * @param encoding character encoding of the 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(final Resource resource, final 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 @@ -497,7 +489,7 @@ * the input stream and parse it to make a new * AST for the resource. */ - if ( resource.requiresChecking() ) + if (resource.requiresChecking()) { /* * touch() the resource to reset the counters @@ -505,7 +497,7 @@ resource.touch(); - if( resource.isSourceModified() ) + if (resource.isSourceModified()) { /* * now check encoding info. It's possible that the newly declared @@ -513,21 +505,21 @@ * this strikes me as bad... */ - if (!resource.getEncoding().equals( encoding ) ) + if (!org.apache.commons.lang.StringUtils.equals(resource.getEncoding(), encoding)) { log.warn("Declared encoding for template '" + - resource.getName() + - "' is different on reload. Old = '" + - resource.getEncoding() + "' New = '" + encoding); + resource.getName() + + "' is different on reload. Old = '" + + resource.getEncoding() + "' New = '" + encoding); - resource.setEncoding( encoding ); + resource.setEncoding(encoding); } /* * read how old the resource is _before_ * processing (=>reading) it */ - long howOldItWas = resource.getResourceLoader().getLastModified( resource ); + long howOldItWas = resource.getResourceLoader().getLastModified(resource); /* * read in the fresh stream and parse @@ -540,54 +532,51 @@ * the modification check counters */ - resource.setLastModified( howOldItWas ); + 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 - * [EMAIL PROTECTED] #getResource(String resourceName, int resourceType, - * String encoding )} + /** + * 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 [EMAIL PROTECTED] #getResource(String resourceName, int resourceType, String encoding )} */ - public Resource getResource(String resourceName, int resourceType ) - throws ResourceNotFoundException, ParseErrorException, Exception + public Resource getResource(String resourceName, int resourceType) + throws ResourceNotFoundException, + ParseErrorException, + Exception { - return getResource( resourceName, resourceType, RuntimeConstants.ENCODING_DEFAULT); + 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. + * Determines if 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 + * @param resourceName Name of template or content resource + * + * @return class name of loader than can provide it */ - public String getLoaderNameForResource(String resourceName ) + public String getLoaderNameForResource(String resourceName) { - ResourceLoader resourceLoader = null; - /* * loop through our loaders... */ - for (int i = 0; i < resourceLoaders.size(); i++) + for (Iterator it = resourceLoaders.iterator(); it.hasNext(); ) { - resourceLoader = (ResourceLoader) resourceLoaders.get(i); + ResourceLoader resourceLoader = (ResourceLoader) it.next(); InputStream is = null; @@ -597,14 +586,14 @@ */ try { - is=resourceLoader.getResourceStream( resourceName ); + is = resourceLoader.getResourceStream(resourceName); - if( is != null) + if (is != null) { return resourceLoader.getClass().toString(); } } - catch( ResourceNotFoundException e) + catch (ResourceNotFoundException rnfe) { /* * this isn't a problem. keep going @@ -612,23 +601,24 @@ } finally { + /* * if we did find one, clean up because we were * returned an open stream */ if (is != null) { + try { is.close(); } catch (IOException supressed) - { - } + { } } } } return null; } -} +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]