Velocity
  1. Velocity
  2. VELOCITY-98

#parse / #include causing ClassCastException

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 1.3-rc1
    • Fix Version/s: 1.5
    • Component/s: Engine
    • Labels:
      None
    • Environment:
      Operating System: Linux
      Platform: All

      Description

      If caching is on, and a template has been previously cached due to a #parse or
      #include directive, subsequently using the other directive will cause a
      ClassCastException and fail.

      Example: if I have two templates, the first one loaded includes the line:
      #include( "include/header.vtl" )
      and the second one the line:
      #parse( "include/header.vtl" )

      When the second page is viewed, a ClassCastException is thrown with an error in
      the log like:

      [error] #parse() : arg = include/header.vtl. Exception :
      java.lang.ClassCastException:
      org.apache.velocity.runtime.resource.ContentResource

      A solution might be to include the resourceType as part of the key when
      caching/retrieving the resource, within the getResource() method of the
      org.apache.velocity.runtime.resource.ResourceManagerImpl class. Alternatively
      seperate caches could be used for the different types of Resource.

        Activity

        Hide
        Geir Magnusson Jr added a comment -

        ack

        Show
        Geir Magnusson Jr added a comment - ack
        Hide
        Michal Chmielewski added a comment -

        Just stumbled on this one too. Seems the fix is pretty trivial.

        Here is code that fixes this problem for me at least ...

        public class ResourceManagerImpl2 extends ResourceManagerImpl {

        public void initialize (RuntimeServices runtimeServices) throws Exception

        { runtimeServices.info("Initializing " + this.getClass().getName() ); super.initialize( runtimeServices ); }

        /**

        • 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.
          */
          String resourceKey = resourceType + ":" + resourceName;
          Resource resource = globalCache.get(resourceKey);

        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(resourceKey); 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(resourceKey, 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;
        }
        }

        Show
        Michal Chmielewski added a comment - Just stumbled on this one too. Seems the fix is pretty trivial. Here is code that fixes this problem for me at least ... public class ResourceManagerImpl2 extends ResourceManagerImpl { public void initialize (RuntimeServices runtimeServices) throws Exception { runtimeServices.info("Initializing " + this.getClass().getName() ); super.initialize( runtimeServices ); } /** 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. */ String resourceKey = resourceType + ":" + resourceName; Resource resource = globalCache.get(resourceKey); 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(resourceKey); 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(resourceKey, 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; } }
        Hide
        Will Glass-Husain added a comment -

        If someone can make a patch against the current source, it passes ant test, and it solves this problem - I'll commit it.

        Show
        Will Glass-Husain added a comment - If someone can make a patch against the current source, it passes ant test, and it solves this problem - I'll commit it.
        Hide
        Will Glass-Husain added a comment -

        Hi,

        Tested this against the unreleased Velocity 1.5. I couldn't duplicate the problem. I know it has been a long time, but has anyone hit this recently?

        Here's the code I used to test it:

        /**

        • Tests for fix of bug VELOCITY-98 where a #include followed by #parse
        • of the same file throws ClassCastException when caching is on.
        • @throws Exception
          */
          public void testIncludeParseCaching ()
          throws Exception { VelocityEngine ve = new VelocityEngine(); ve.setProperty("file.resource.loader.cache", "true"); ve.setProperty("file.resource.loader.path", FILE_RESOURCE_LOADER_PATH); ve.init(); Template template = ve.getTemplate("testincludeparse.vm"); Writer writer = new StringWriter(); VelocityContext context = new VelocityContext(); // will produce a ClassCastException if Velocity-98 is not solved template.merge(context, writer); writer.flush(); writer.close(); }

        And two files:

        testincludeparse.vm
        #include("include1.vm")
        #parse("include1.vm")

        include1.vm
        test

        Show
        Will Glass-Husain added a comment - Hi, Tested this against the unreleased Velocity 1.5. I couldn't duplicate the problem. I know it has been a long time, but has anyone hit this recently? Here's the code I used to test it: /** Tests for fix of bug VELOCITY-98 where a #include followed by #parse of the same file throws ClassCastException when caching is on. @throws Exception */ public void testIncludeParseCaching () throws Exception { VelocityEngine ve = new VelocityEngine(); ve.setProperty("file.resource.loader.cache", "true"); ve.setProperty("file.resource.loader.path", FILE_RESOURCE_LOADER_PATH); ve.init(); Template template = ve.getTemplate("testincludeparse.vm"); Writer writer = new StringWriter(); VelocityContext context = new VelocityContext(); // will produce a ClassCastException if Velocity-98 is not solved template.merge(context, writer); writer.flush(); writer.close(); } And two files: testincludeparse.vm #include("include1.vm") #parse("include1.vm") include1.vm test
        Hide
        Will Glass-Husain added a comment -

        Since test case passes, I'm closing this issue. Please reopen if you find it still exists.

        Show
        Will Glass-Husain added a comment - Since test case passes, I'm closing this issue. Please reopen if you find it still exists.
        Hide
        Stepan Koltsov added a comment -

        I confirm, error present in 1.4, fixed in today stapshot.

        Show
        Stepan Koltsov added a comment - I confirm, error present in 1.4, fixed in today stapshot.
        Hide
        Will Glass-Husain added a comment -

        The problem reappeared once we changed the Exception handling code to pass through RuntimeExceptions. (hooray for the regression test case which suddenly started failing.)

        I've applied Michal's patch. Thanks for contributing!

        Show
        Will Glass-Husain added a comment - The problem reappeared once we changed the Exception handling code to pass through RuntimeExceptions. (hooray for the regression test case which suddenly started failing.) I've applied Michal's patch. Thanks for contributing!
        Hide
        Henning Schmiedehausen added a comment -

        Close all resolved issues for Engine 1.5 release.

        Show
        Henning Schmiedehausen added a comment - Close all resolved issues for Engine 1.5 release.

          People

          • Assignee:
            Unassigned
            Reporter:
            Simon Christian
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development