You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by Mike Williams <md...@bigfoot.com> on 2002/11/12 14:05:15 UTC

class-loading in Velocity

As part of my Velocity app (vdoclet.sf.net, plug plug), I provide an
Ant-task.  In my first attempt, I ran into class-loading issues, where
Velocity didn't have access to the user's classes.  I think I need a
picture to illustrate:

    +---------------------------
    | [A] System class-loader
    |   rt.jar, ant.jar, ...
    +---------------------------
                 ^
                 |
    +---------------------------
    | [B] AntClassLoader (for "vdoclet" taskdef)
    |   velocity-dep.jar
    |   vdoclet.jar
    +---------------------------
                 ^
                 |
    +---------------------------
    | [C] AntClassLoader (use of "vdoclet" task)
    |   user-classes.jar
    |   otherLibs.jar
    +---------------------------

The problem is, Velocity, in class-loader [B], can't see my user's classes,
in class-loader [C].  This is a problem, because I'm using the
ClasspathResourceLoader to load templates.  The problem gets worse if for
some reason Velocity ends up on the system classpath: then Velocity can't
even locate my custom LogSystem implementation.

My current workaround is to have my Ant task fork a new Java process,
basically putting everything into a single classpath.  It works, but I'm
slightly irked that such measures are necessary.

This has me thinking: what if I could somehow specify what class-loader
Velocity should use for dynamic class-loading.  How feasible would it be to
attach a "classLoader" property to the VelocityEngine itself, and have it
use the provided loader instead of Class.forName()?

-- 
cheers, Mike

"And so, may evil beware, and may good dress warmly and eat lots 
 of fresh vegetables."
        -- The Tick 

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: [PATCH] use context class-loader to load classpath resources

Posted by Mike Williams <md...@bigfoot.com>.
  >>> On Wed, 13 Nov 2002 16:08:25 -0500,
  >>> "Bill" == Bill Burton <bi...@progress.com> wrote:

  Bill> Attached is an untested patch to ClasspathResourceLoader.java that
  Bill> allows specifying a classloader via VelocityEngine.setProperty.

Nice.  Thanks Bill, I'll give it a try.  

-- 
cheers, Mike

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: [PATCH] use context class-loader to load classpath resources

Posted by Bill Burton <bi...@progress.com>.
Hi Mike,

Attached is an untested patch to ClasspathResourceLoader.java that 
allows specifying a classloader via VelocityEngine.setProperty.  If 
that's not specified, it now looks first for a Context ClassLoader.  If 
that's null, it uses it's own ClassLoader.  The only thing about that is 
is it's not backwards compatible.  But maybe using the Context 
ClassLoader if available would not cause any real problems so it might 
be okay?

The code to set up for the classloader attribute would be something like:
         // Get desired classloader in some way.
         Thread t = Thread.currentThread();
         ClassLoader cl = t.getContextClassLoader();

         // Instantiate and initialize Velocity.
         VelocityEngine ve = new VelocityEngine();
         ve.setProperty("resource.loader", "class");
         ve.setProperty("class.resource.loader.classloader", cl);
         ...

If the classloader property support works for you, maybe this could be 
accepted for the 1.3 release.  But the check for the Context ClassLoader 
should probably wait for 1.4.

Hope this helps,
-Bill

Mike Williams wrote:
>   >>> Earlier, I said:
> 
>   Mike> How about having Velocity use the context class-loader?  
> 
> Here's a patch that supports use of the context ClassLoader when loading
> classpath resources such as custom log-systems, or templates via the
> ClasspathResourceLoader.  The idea is that your app would do something
> like:
> 

[PATCH] use context class-loader to load classpath resources

Posted by Mike Williams <md...@bigfoot.com>.
  >>> Earlier, I said:

  Mike> How about having Velocity use the context class-loader?  

Here's a patch that supports use of the context ClassLoader when loading
classpath resources such as custom log-systems, or templates via the
ClasspathResourceLoader.  The idea is that your app would do something
like:

    Thread t = Thread.currentThread();
    ClassLoader origLoader = t.getContextClassLoader();
    t.setContextClassLoader( someOtherLoader );
    try {
        velocityEngine.mergeTemplate( "template.vm", context, writer );
    } finally {
        t.setContextClassLoader( origLoader );
    }

diff -rNu -x 'CVS*' jakarta-velocity/src/java/org/apache/velocity/app/FieldMethodizer.java jakarta-velocity-hacked/src/java/org/apache/velocity/app/FieldMethodizer.java
--- jakarta-velocity/src/java/org/apache/velocity/app/FieldMethodizer.java	2001-03-05 22:44:48.000000000 +1100
+++ jakarta-velocity-hacked/src/java/org/apache/velocity/app/FieldMethodizer.java	2002-11-13 22:53:38.000000000 +1100
@@ -59,6 +59,8 @@
 import java.lang.reflect.Modifier;
 import java.util.HashMap;
 
+import org.apache.velocity.util.ClassUtils;
+
 /**
  *  <p>
  *  This is a small utility class allow easy access to static fields in a class,
@@ -154,7 +156,7 @@
     public void addObject ( String s )
         throws Exception
     {
-        inspect(Class.forName(s));
+        inspect(ClassUtils.loadClass(s));
     }
     
     /**
diff -rNu -x 'CVS*' jakarta-velocity/src/java/org/apache/velocity/runtime/RuntimeInstance.java jakarta-velocity-hacked/src/java/org/apache/velocity/runtime/RuntimeInstance.java
--- jakarta-velocity/src/java/org/apache/velocity/runtime/RuntimeInstance.java	2002-08-09 10:54:53.000000000 +1000
+++ jakarta-velocity-hacked/src/java/org/apache/velocity/runtime/RuntimeInstance.java	2002-11-13 22:50:02.000000000 +1100
@@ -89,6 +89,7 @@
 import org.apache.velocity.runtime.resource.ContentResource;
 import org.apache.velocity.runtime.resource.ResourceManager;
 
+import org.apache.velocity.util.ClassUtils;
 import org.apache.velocity.util.SimplePool;
 import org.apache.velocity.util.StringUtils;
 
@@ -303,7 +304,7 @@
 
             try
             {
-               o = Class.forName(rm).newInstance();
+               o = ClassUtils.newInstance(rm);
             }
             catch (ClassNotFoundException cnfe)
             {
@@ -543,7 +544,7 @@
             
             try
             {
-               o = Class.forName( rm ).newInstance();
+               o = ClassUtils.newInstance(rm);
             }
             catch (ClassNotFoundException cnfe )
             {
@@ -692,7 +693,7 @@
     {    
         try
         {
-            Object o = Class.forName( directiveClass ).newInstance();
+            Object o = ClassUtils.newInstance(directiveClass);
             
             if ( o instanceof Directive )
             {
diff -rNu -x 'CVS*' jakarta-velocity/src/java/org/apache/velocity/runtime/log/LogManager.java jakarta-velocity-hacked/src/java/org/apache/velocity/runtime/log/LogManager.java
--- jakarta-velocity/src/java/org/apache/velocity/runtime/log/LogManager.java	2001-11-17 23:32:34.000000000 +1100
+++ jakarta-velocity-hacked/src/java/org/apache/velocity/runtime/log/LogManager.java	2002-11-13 23:33:39.000000000 +1100
@@ -60,6 +60,7 @@
 
 import org.apache.velocity.runtime.RuntimeServices;
 import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.util.ClassUtils;
 
 /**
  * <p>
@@ -158,7 +159,7 @@
           
                 try
                 {
-                    o = Class.forName( claz ).newInstance();
+                    o = ClassUtils.newInstance(claz);
 
                     if ( o instanceof LogSystem )
                     {
diff -rNu -x 'CVS*' jakarta-velocity/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java jakarta-velocity-hacked/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java
--- jakarta-velocity/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java	2002-03-25 11:44:00.000000000 +1100
+++ jakarta-velocity-hacked/src/java/org/apache/velocity/runtime/resource/ResourceManagerImpl.java	2002-11-13 22:50:17.000000000 +1100
@@ -205,7 +205,7 @@
         {
             try
             {
-               o = Class.forName( claz ).newInstance();
+               o = ClassUtils.newInstance(claz);
             }
             catch (ClassNotFoundException cnfe )
             {
diff -rNu -x 'CVS*' jakarta-velocity/src/java/org/apache/velocity/runtime/resource/loader/ResourceLoaderFactory.java jakarta-velocity-hacked/src/java/org/apache/velocity/runtime/resource/loader/ResourceLoaderFactory.java
--- jakarta-velocity/src/java/org/apache/velocity/runtime/resource/loader/ResourceLoaderFactory.java	2001-10-22 13:53:26.000000000 +1000
+++ jakarta-velocity-hacked/src/java/org/apache/velocity/runtime/resource/loader/ResourceLoaderFactory.java	2002-11-13 22:51:20.000000000 +1100
@@ -55,6 +55,7 @@
  */
 
 import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.ClassUtils;
 import org.apache.velocity.util.StringUtils;
 
 /**
@@ -76,9 +77,7 @@
         
         try
         {
-            loader = ((ResourceLoader)Class.forName(loaderClassName)
-                .newInstance());
-            
+            loader = (ResourceLoader)ClassUtils.newInstance(loaderClassName);
             rs.info("Resource Loader Instantiated: " + 
                 loader.getClass().getName());
             
diff -rNu -x 'CVS*' jakarta-velocity/src/java/org/apache/velocity/texen/Generator.java jakarta-velocity-hacked/src/java/org/apache/velocity/texen/Generator.java
--- jakarta-velocity/src/java/org/apache/velocity/texen/Generator.java	2002-07-25 08:42:51.000000000 +1000
+++ jakarta-velocity-hacked/src/java/org/apache/velocity/texen/Generator.java	2002-11-13 22:52:41.000000000 +1100
@@ -531,8 +531,7 @@
                 
                 try
                 {
-                    Class cls = Class.forName (contextObj);
-                    Object o = cls.newInstance();
+                    Object o = ClassUtils.newInstance(contextObj);
                     context.put (contextName,o);
                 }
                 catch (Exception e)
diff -rNu -x 'CVS*' jakarta-velocity/src/java/org/apache/velocity/util/ClassUtils.java jakarta-velocity-hacked/src/java/org/apache/velocity/util/ClassUtils.java
--- jakarta-velocity/src/java/org/apache/velocity/util/ClassUtils.java	1970-01-01 10:00:00.000000000 +1000
+++ jakarta-velocity-hacked/src/java/org/apache/velocity/util/ClassUtils.java	2002-11-13 23:31:25.000000000 +1100
@@ -0,0 +1,47 @@
+package org.apache.velocity.util;
+
+/**
+ * This class provides methods for dynamic loading of classes.
+ *
+ * @author <a href="mailto:mdub@bigfoot.com">Mike Williams</a>
+ * @version $Id$
+ */
+public class ClassUtils
+{
+
+    /**
+     * Load the named class.  Look in the context ClassLoader first, then
+     * the ClassLoader used to load Velocity.
+     *
+     * @param className name of desired class
+     * @throws ClassNotFoundException if the class cannot be found
+     */
+    public static Class loadClass(String className)
+        throws ClassNotFoundException
+    {
+        ClassLoader contextLoader = 
+            Thread.currentThread().getContextClassLoader();
+        try {
+            return Class.forName(className, true, contextLoader);
+        } catch (ClassNotFoundException e) {
+            return Class.forName(className);
+        }
+    }
+
+    /**
+     * Instantiate the named class.
+     *
+     * @param className name of desired class
+     * @return a new instance of the named class
+     * @throws ClassNotFoundException if the class cannot be found
+     * @throws IllegalAccessException if the class or initializer is not accessible
+     * @throws InstantiationException if instantiation fails for some other reason
+     */
+    public static Object newInstance(String className)
+        throws ClassNotFoundException,
+               IllegalAccessException, InstantiationException
+    {
+        return loadClass(className).newInstance();
+    }
+
+}

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: class-loading in Velocity

Posted by Juozas Baliuka <ba...@mail.lt>.
----- Original Message -----
From: "Mike Williams" <md...@bigfoot.com>
To: <ve...@jakarta.apache.org>
Sent: Wednesday, November 13, 2002 9:56 PM
Subject: Re: class-loading in Velocity


>   >>> On Wed, 13 Nov 2002 18:03:08 +0100,
>   >>> "Juozas" == "Juozas Baliuka" <ba...@mail.lt> wrote:
>
>   >> Would this break backward compatibility?
>
>   Juozas> It will be problem on some app serversm, some of them do not set
>   Juozas> "ContextClassLoader" and app classes are not visible for this
>   Juozas> classloader.
>
> True.
>
> The patch I submitted first attempts to load classes using the context
> ClassLoader (which defaults to the System ClassLoader, I believe).  If
this
> fails, it falls back to using Class.forName() ... so, it won't prevent
> access to any class that was previously visible.

It is better to call "Class.forName()" fist for backward compatibility, some
classes can be visible for
both classLoaders and it can produce "ClassCastExeption" if old code casts
Class defined in "threadClassLoader" to
class defined in "Class.forName()".

>
> Out of interest, I stole the idea from James Strachan's blog [1].
>
>     [1] http://radio.weblogs.com/0112098/2002/09/24.html
>
> --
> cheers, Mike
>
> "Don't pet the sweaty things."
>
> --
> To unsubscribe, e-mail:
<ma...@jakarta.apache.org>
> For additional commands, e-mail:
<ma...@jakarta.apache.org>
>


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: class-loading in Velocity

Posted by Mike Williams <md...@bigfoot.com>.
  >>> On Wed, 13 Nov 2002 18:03:08 +0100,
  >>> "Juozas" == "Juozas Baliuka" <ba...@mail.lt> wrote:

  >> Would this break backward compatibility?

  Juozas> It will be problem on some app serversm, some of them do not set
  Juozas> "ContextClassLoader" and app classes are not visible for this
  Juozas> classloader.

True.

The patch I submitted first attempts to load classes using the context
ClassLoader (which defaults to the System ClassLoader, I believe).  If this
fails, it falls back to using Class.forName() ... so, it won't prevent
access to any class that was previously visible.

Out of interest, I stole the idea from James Strachan's blog [1]. 

    [1] http://radio.weblogs.com/0112098/2002/09/24.html

-- 
cheers, Mike

"Don't pet the sweaty things."

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: class-loading in Velocity

Posted by Juozas Baliuka <ba...@mail.lt>.
----- Original Message -----
From: "Mike Williams" <md...@bigfoot.com>
To: <ve...@jakarta.apache.org>
Sent: Wednesday, November 13, 2002 12:30 PM
Subject: Re: class-loading in Velocity


>   >>> On Tue, 12 Nov 2002 22:20:12 -0500,
>   >>> "Bill" == Bill Burton <bi...@progress.com> wrote:
>
>   Bill> As a work around, could you put the velocity-dep.jar in the Ant
nested
>   Bill> <classpath> element in the <vdoclet> element ([C] in your
diagram)?
>
> Unfortunately not, because:
>
>   Bill> But then maybe vdoclet.jar will fail because it can't find
>   Bill> Velocity's classes?
>
> Exactly.
>
>   Bill> The only other way I can think of as a work around is to include
>   Bill> your user classpath in the <taskdef> for vdoclet and not use the
>   Bill> nested <classpath> element.  That way, everything is in the same
>   Bill> classloader.
>
> Hmm.  But then I can't use the task multiple times with different
> classpaths.
>
>   Bill> For a better solution, maybe you could extend
>   Bill> ClasspathResourceLoader to accept a classloader.  Maybe you could
>   Bill> instantiate this version setting the desired classloader and then
>   Bill> set it into the Velocity properties before instantiating
>   Bill> VelocityEngine.
>
> That's a very interesting idea.  But to configure Velocity you use a
> Properties object, and I can't see no way to massage a class-loader (or a
> handle to one) into a property-value.  Can you?
>
> How about having Velocity use the context class-loader?  ie. instead of
>
>     Class.forName(className)
>
> use
>
>     Thread.currentThread().getContextClassLoader().loadClass(className)
>
> Then, at least, I could setContextClassLoader() before passing control to
> Velocity.  Would this break backward compatibility?

It will be problem on some app serversm, some of them do not set
"ContextClassLoader" and app classes are
not visible for this classloader.


>
> --
> cheers, Mike
>
> "The large print giveth and the small print taketh away."
>     -- Tom Waits
>
> --
> To unsubscribe, e-mail:
<ma...@jakarta.apache.org>
> For additional commands, e-mail:
<ma...@jakarta.apache.org>
>


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: class-loading in Velocity

Posted by Mike Williams <md...@bigfoot.com>.
  >>> On Tue, 12 Nov 2002 22:20:12 -0500,
  >>> "Bill" == Bill Burton <bi...@progress.com> wrote:

  Bill> As a work around, could you put the velocity-dep.jar in the Ant nested
  Bill> <classpath> element in the <vdoclet> element ([C] in your diagram)? 

Unfortunately not, because:

  Bill> But then maybe vdoclet.jar will fail because it can't find
  Bill> Velocity's classes?

Exactly.

  Bill> The only other way I can think of as a work around is to include
  Bill> your user classpath in the <taskdef> for vdoclet and not use the
  Bill> nested <classpath> element.  That way, everything is in the same
  Bill> classloader.

Hmm.  But then I can't use the task multiple times with different
classpaths.

  Bill> For a better solution, maybe you could extend
  Bill> ClasspathResourceLoader to accept a classloader.  Maybe you could
  Bill> instantiate this version setting the desired classloader and then
  Bill> set it into the Velocity properties before instantiating
  Bill> VelocityEngine.

That's a very interesting idea.  But to configure Velocity you use a
Properties object, and I can't see no way to massage a class-loader (or a
handle to one) into a property-value.  Can you?

How about having Velocity use the context class-loader?  ie. instead of

    Class.forName(className)

use

    Thread.currentThread().getContextClassLoader().loadClass(className)

Then, at least, I could setContextClassLoader() before passing control to
Velocity.  Would this break backward compatibility?

-- 
cheers, Mike

"The large print giveth and the small print taketh away."
    -- Tom Waits

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: class-loading in Velocity

Posted by Bill Burton <bi...@progress.com>.
Hello,

As a work around, could you put the velocity-dep.jar in the Ant nested 
<classpath> element in the <vdoclet> element ([C] in your diagram)? 
That way, it will use the same classloader as your user classes and you 
shouldn't have to fork.  But then maybe vdoclet.jar will fail because it 
can't find Velocity's classes?

The only other way I can think of as a work around is to include your 
user classpath in the <taskdef> for vdoclet and not use the nested 
<classpath> element.  That way, everything is in the same classloader.

For a better solution, maybe you could extend ClasspathResourceLoader to 
accept a classloader.  Maybe you could instantiate this version setting 
the desired classloader and then set it into the Velocity properties 
before instantiating VelocityEngine.

-Bill

Mike Williams wrote:
> As part of my Velocity app (vdoclet.sf.net, plug plug), I provide an
> Ant-task.  In my first attempt, I ran into class-loading issues, where
> Velocity didn't have access to the user's classes.  I think I need a
> picture to illustrate:
> 
>     +---------------------------
>     | [A] System class-loader
>     |   rt.jar, ant.jar, ...
>     +---------------------------
>                  ^
>                  |
>     +---------------------------
>     | [B] AntClassLoader (for "vdoclet" taskdef)
>     |   velocity-dep.jar
>     |   vdoclet.jar
>     +---------------------------
>                  ^
>                  |
>     +---------------------------
>     | [C] AntClassLoader (use of "vdoclet" task)
>     |   user-classes.jar
>     |   otherLibs.jar
>     +---------------------------
> 
> The problem is, Velocity, in class-loader [B], can't see my user's classes,
> in class-loader [C].  This is a problem, because I'm using the
> ClasspathResourceLoader to load templates.  The problem gets worse if for
> some reason Velocity ends up on the system classpath: then Velocity can't
> even locate my custom LogSystem implementation.
> 
> My current workaround is to have my Ant task fork a new Java process,
> basically putting everything into a single classpath.  It works, but I'm
> slightly irked that such measures are necessary.
> 
> This has me thinking: what if I could somehow specify what class-loader
> Velocity should use for dynamic class-loading.  How feasible would it be to
> attach a "classLoader" property to the VelocityEngine itself, and have it
> use the provided loader instead of Class.forName()?
> 



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>