lbehnke / h2database

Automatically exported from code.google.com/p/h2database
0 stars 0 forks source link

Feature Request - Support for non-default ClassLoaders #112

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
(simple SQL scripts or simple standalone applications are preferred)
1. Use the h2 jdbc driver in software that has a custom class loader (e.g.
TIBCO BusinessWorks)
2. Create a jar with public class with a public static method that will be
used as a function or stored procedure in h2
3. Add the jar to the application per it's instructions (e.g. in TIBCO
BusinessWorks create a AliasLibrary containing the File Alias to the jar)
4. Within the application execute SQL to create the ALIAS and execute it
(e.g. CREATE ALIAS MY_STORED_PROC FOR "org.windyroad.Procs.MY_STORED_PROC";
CALL MY_STORED_PROC(24);)

What is the expected output?
The static method executes and returns a resultset

What do you see instead?
Error reporting the class cannot be found.

What version of the product are you using? On what operating system, file
system, and virtual machine?
h2 1.0.79
Virtual Windows XP
VMWare 2.0.5 on OS X 10.5

Do you know a workaround?
Add jar containing static method to the class path

How important/urgent is the problem for you?
It's an significant inconvenience.

In your view, is this a defect or a feature request?
Feature request.

Please provide any additional information below.
I believe the issue is in org/h2/util/ClassUtils.java on line 84, where it
calls:
    Class.forName(className);
To support custom class loaders, it would need to call
    Class.forName(className, true, SomeClassLoader);

There are a number of ways I can think of to add support for custom class
loaders, which are:

1. System Properties

In the same way that the h2.allowedClasses system property is used to limit
what classes can be loaded, a h2.classLoader property could be used to
specify the name of the class loader that will be used to load classes.

Pros:

Very easy to extend the code to support this design.

No redundancy or conflicts in the class loader specification.  No
redundancy as each loader only needs to be specified once. No conflict as
the class will be loaded from the first class loader that is able to load it.

Cons:

Inefficient.  Loading the class for a function or stored proc requires
searching through all of the specified class loaders, whereas in reality
the developer will know which class loader needs to be used for each
function or stored proc.

2. Extend CREATE ALIAS

Extend CREATE ALIAS as follows:

    CREATE ALIAS [IF NOT EXISTS] newFunctionAliasName FOR
classAndMethodName [USING classLoaderName [USING classLoaderName [USING
classLoaderName [...]]]]

The subsequent class loaders may be specified to use a non-default class
loader to load the class loader. e.g. 

    CREATE ALIAS MY_STORED_PROC FOR "org.windyroad.Procs.MY_STORED_PROC"
USING "org.windyroad.MyClassLoader" USING "com.tibco.bw.ClassLoader"

would load "org.windyroad.Procs.MY_STORED_PROC" using
"org.windyroad.MyClassLoader", which is in turn loaded by
"com.tibco.bw.ClassLoader", which is loaded by the default class loader.

Pros:

Efficient. Only one class loader is used for any given class.

Cons:

Requires more extensive code changes.

Allows redundant and conflicting class loader specifications. e.g.
the followind is redundant:
    CREATE ALIAS MY_STORED_PROC FOR
"org.windyroad.Procs.MY_FIRST_STORED_PROC" USING "org.windyroad.MyClassLoader"
    CREATE ALIAS MY_STORED_PROC FOR
"org.windyroad.Procs.MY_SECOND_STORED_PROC" USING "org.windyroad.MyClassLoader"
the following conflicts:
    CREATE ALIAS MY_STORED_PROC FOR
"org.windyroad.Procs.MY_FIRST_STORED_PROC" USING
"org.windyroad.MyFirstClassLoader"
    CREATE ALIAS MY_STORED_PROC FOR
"org.windyroad.Procs.MY_SECOND_STORED_PROC" USING
"org.windyroad.MySecondClassLoader"

3. Create a SET CLASSLOADER Command

Create a SET CLASSLOADER command as follows:

    SET CLASSLOADER classLoaderName FOR className

this would be used to set a map between className and classLoaderName,
which would be used by org.h2.util.ClassUtils.  e.g.

    CREATE ALIAS MY_STORED_PROC FOR "org.windyroad.Procs.MY_STORED_PROC";
    SET CLASSLOADER "org.windyroad.MyClassLoader" FOR "org.windyroad.Procs";
    SET CLASSLOADER "com.tibco.bw.classloader" FOR
"org.windyroad.MyClassLoader";

Pros:

No redundancy or conflicts in the class loader specification.  No
redundancy as each loader only needs to be specified once. No conflict as
the class will be loaded from the first class loader that is able to load it.

Efficient. Only one class loader is used for any given class.

Cons:

Requires more extensive code changes.

Recommendation:
From the (very) little I know about h2, option 3 seems to be the best of
those that I can think of.  You or someone else may very easily have a
better solution.

Thoughts?

Original issue reported on code.google.com by t...@windyroad.org on 20 Aug 2009 at 12:48

GoogleCodeExporter commented 9 years ago
Hi,

Thanks for your help! And sorry for the delay. 

In all your solutions you have used the name of the class-loader class. Is 
there no
need to set a specific class-loader instance, or at least provide a parameter /
parameters to the class-loader?

Regards,
Thomas

Original comment by thomas.t...@gmail.com on 26 Aug 2009 at 5:07

GoogleCodeExporter commented 9 years ago
Fair call. It probably makes more sense to specify a method that will give us 
the class loader instance.  e.g.

CREATE ALIAS MY_STORED_PROC FOR "org.windyroad.Procs.MY_STORED_PROC";
SET CLASSLOADER "org.windyroad.MyClassLoader.getInstance" FOR 
"org.windyroad.Procs";
SET CLASSLOADER "com.tibco.pe.plugin.SharedClassLoader.getInstance" FOR 
"org.windyroad.MyClassLoader";

I was admittedly working from memory on this as I had to solve a very similar 
issue for a completely unrelated 
library. Having a look back at that code, I'm using 
com.tibco.pe.plugin.SharedClassLoader, which has a public 
static getInstance() method that takes no args.  

Let's now say we have a class loader that either doesn't have a getInstance or 
similar method, or the method 
expects some arguments.  In that case, I can create one as follows

public class MyClassLoaderFactory  {
    private static MyClassLoader mcl = null;

    public static ClassLoader getInstance() {
        if( mcl == null ) {
            // throw exception
        }
        return mcl;
    }

    public static ClassLoader getInstance(/*some args*/) {
        if( mcl == null ) {
            // do what I need here to create a MyClassLoader instance and assign it to mlc
        }
        return mcl;
    }
};

Then, all I would need to do is make sure I call the second 
MyClassLoaderFactory.getInstance() method, 
before I called any h2 code.

thoughts?

Original comment by t...@windyroad.org on 30 Aug 2009 at 10:05

GoogleCodeExporter commented 9 years ago
Hi,

Have been playing with h2 inside NetKernel and encountered a similar 
ClassLoader issue (see 
http://www.1060.org/forum/topic/569/1). However the fix for this one is a lot 
simpler than the discussed 
options. If h2 used the context class loader to load trigger classes then the 
problem goes away. I've attached an 
h2 patch which achieves what I need, it is effectively a one-liner.

Cheers,

Chris

Original comment by christop...@deltaxml.com on 11 Sep 2009 at 6:08

Attachments:

GoogleCodeExporter commented 9 years ago
I like the getContextClassLoader solution and will implement
it in the next release.

Original comment by thomas.t...@gmail.com on 11 Sep 2009 at 6:51

GoogleCodeExporter commented 9 years ago
The getContextClassLoader solution is implemented in version 1.1.119

Original comment by thomas.t...@gmail.com on 26 Sep 2009 at 11:32