Ok, the patch is done for OSGi and it works well.
I should write Unit test but I don't find *unit test* on OSGi activator.
I'm using IntelliJ, how do you create a valid project with H2 source ?
How it works :
The H2 Bundle Activator register into org.h2.Util.Utils an object that
handle a specific kind of binary class name. This specific binary class
name follow this structure
"BundleSymbolicName:BundleVersion:JavaBinaryName"
I don't find a standard way to describe such kind of structure. I could use
Bundle Id (int) but this identifier can change after framework restart, and
it is too opaque.
This extended name allow OSGi developers to still using the "CREATE ALIAS"
Sql query without doing any Java operation, neither deal with h2 packages.
I'm working now on H2 trunk, still trying to figure out what is the best
strategy for h2 spatial.
Thank for you support.
-Nicolas
Le jeudi 18 avril 2013 17:12:59 UTC+2, Nicolas Fortin (OrbisGIS) a écrit :
>
> Thank your for the answer. I will work on the patch, trying to not break
> anything. And I will send another patch for maven local build that do not
> read the good version of the library (got stuck on home made build system).
>
> I will release also H2 Spatial OSGi integration and unit testing projects
> also.
>
> For license I will see with my boss Erwan. Spatial feature would require
> at least one library dependency JTS topology suite, is this a problem for
> trunk development ? May be an optional build to enable spatial feature ?
>
> Best regards,
>
> -Nicolas
> Le jeudi 18 avril 2013 16:27:30 UTC+2, Noel Grandin a écrit :
>>
>>
>>
>> On Thursday, 18 April 2013, Nicolas Fortin (OrbisGIS) wrote:
>>
>>> We are using H2 in an OSGi environment.
>>>
>>> I search an alternative to CREATE ALIAS in order to provide to h2 the
>>> java Class.
>>>
>>> Maybe by calling something like org.h2.util.Utils.addUserClass("Class
>>> name",Class<?> userClass);
>>
>>
>>
>> Are yes, I see your problem. In an osgi context the db does not know
>> which class loader to use when loading the alias.
>>
>> For now I suggest you patch such a method in yourself. If you can't go
>> that, I will send you a patch next week. (I'm at a conference this week)
>>
>>
>>
>>
>>>
>>>
>>>
>>>
>>>
>>>
>>
--
You received this message because you are subscribed to the Google Groups "H2
Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/h2-database?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
Index: src/main/org/h2/util/DbDriverActivator.java
===================================================================
--- src/main/org/h2/util/DbDriverActivator.java (r?vision 4743)
+++ src/main/org/h2/util/DbDriverActivator.java (copie de travail)
@@ -5,10 +5,14 @@
*/
package org.h2.util;
+import java.lang.ClassNotFoundException;
+import java.lang.Override;
import java.util.Properties;
import org.h2.engine.Constants;
+import org.h2.util.Utils;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Bundle;
import org.osgi.service.jdbc.DataSourceFactory;
/**
@@ -16,6 +20,7 @@
* is unloaded when stopping the bundle.
*/
public class DbDriverActivator implements BundleActivator {
+ private OSGIClassFactory osgiClassFactory;
/**
* Start the bundle. This will load the database driver and register the
@@ -25,6 +30,8 @@
*/
public void start(BundleContext bundleContext) {
org.h2.Driver driver = org.h2.Driver.load();
+ osgiClassFactory = new OSGIClassFactory(bundleContext);
+ Utils.addClassFactory(osgiClassFactory);
Properties properties = new Properties();
properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, org.h2.Driver.class.getName());
properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_NAME, "H2");
@@ -40,7 +47,54 @@
* @param bundleContext the bundle context
*/
public void stop(BundleContext bundleContext) {
+ Utils.removeClassFactory(osgiClassFactory);
org.h2.Driver.unload();
+ osgiClassFactory = null;
}
+ /**
+ * Extend the H2 class loading in order to find class defined in other bundles.
+ *
+ * The class format for bundle class is the following :
+ * BundleSymbolicName:BundleVersion:BinaryClassName
+ */
+ private static class OSGIClassFactory implements Utils.ClassFactory {
+ /** Separator character to merge bundle name,version and class binary name */
+ public static final String SEPARATOR = ":";
+ private BundleContext bundleContext;
+ private static final int BUNDLE_SYMBOLIC_NAME_INDEX = 0;
+ private static final int BUNDLE_VERSION_INDEX = 1;
+ private static final int BINARY_CLASS_NAME_INDEX = 2;
+
+ /**
+ * Constructor
+ * @param bundleContext Valid bundleContext instance
+ */
+ private OSGIClassFactory(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ public boolean match(String name) {
+ // OSGi binary class name must contain two SEPARATOR character
+ int index = name.indexOf(SEPARATOR);
+ return !(index==-1 || name.indexOf(SEPARATOR,index+1)==-1);
+ }
+
+ @Override
+ public Class<?> loadClass(String name) throws ClassNotFoundException {
+ String[] parts = name.split(SEPARATOR);
+ if(parts.length!=3) {
+ throw new ClassNotFoundException("OSGi class binary name must contain only 3 '"+SEPARATOR+"' characters");
+ }
+ for(Bundle bundle : bundleContext.getBundles()) {
+ if(bundle.getSymbolicName().equals(parts[BUNDLE_SYMBOLIC_NAME_INDEX])
+ && bundle.getVersion().toString().equals(parts[BUNDLE_VERSION_INDEX])) {
+ // Found the right bundle
+ return bundle.loadClass(parts[BINARY_CLASS_NAME_INDEX]);
+ }
+ }
+ throw new ClassNotFoundException("OSGi Bundle not found "+parts[BUNDLE_SYMBOLIC_NAME_INDEX]+" "+parts[BUNDLE_VERSION_INDEX]);
+ }
+ }
}
Index: src/main/org/h2/util/Utils.java
===================================================================
--- src/main/org/h2/util/Utils.java (r?vision 4743)
+++ src/main/org/h2/util/Utils.java (copie de travail)
@@ -13,10 +13,16 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
+import java.lang.Class;
+import java.lang.ClassLoader;
+import java.lang.ClassNotFoundException;
+import java.lang.Exception;
+import java.lang.String;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.List;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
@@ -61,6 +67,10 @@
private static boolean allowAllClasses;
private static HashSet<String> allowedClassNames;
+ /**
+ * In order to manage more than one class loader
+ */
+ private static List<ClassFactory> userClassFactories = new ArrayList<ClassFactory>();
private static String[] allowedClassNamePrefixes;
static {
@@ -74,6 +84,21 @@
}
}
+ /**
+ * Add a class factory in order to manage more than one class loader.
+ * @param classFactory An object that implements ClassFactory
+ */
+ public static void addClassFactory(ClassFactory classFactory) {
+ userClassFactories.add(classFactory);
+ }
+
+ /**
+ * Remove a class factory
+ * @param classFactory Already inserted class factory instance
+ */
+ public static void removeClassFactory(ClassFactory classFactory) {
+ userClassFactories.remove(classFactory);
+ }
private Utils() {
// utility class
}
@@ -568,18 +593,20 @@
throw DbException.get(ErrorCode.ACCESS_DENIED_TO_CLASS_1, className);
}
}
- // the following code might be better for OSGi (need to verify):
- /*
- try {
- return Utils.class.getClassLoader().loadClass(className);
- } catch (Throwable e) {
- try {
- return Thread.currentThread().getContextClassLoader().loadClass(className);
- } catch (Throwable e2) {
- DbException.get(ErrorCode.CLASS_NOT_FOUND_1, e, className);
+ // Use provided class factory first.
+ for(ClassFactory classFactory : userClassFactories) {
+ if(classFactory.match(className)) {
+ try {
+ Class<?> userClass = classFactory.loadClass(className);
+ if(!(userClass == null)) {
+ return userClass;
+ }
+ } catch (Exception e) {
+ throw DbException.get(ErrorCode.CLASS_NOT_FOUND_1, e, className);
+ }
}
}
- */
+ // Use local ClassLoader
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
@@ -877,4 +904,22 @@
return defaultValue;
}
+ /**
+ * Utils will try to use provided class factories to convert binary name of class to Class object.
+ * Used by H2 Osgi Activator in order to provide class from another bundle ClassLoader.
+ */
+ public interface ClassFactory {
+ /**
+ * @param name The binary name of the class
+ * @return True if this factory can return a valid class for the provided class name.
+ */
+ public boolean match(String name);
+ /**
+ * @param name The binary name of the class
+ * @return The class object
+ * @throws ClassNotFoundException If the class is not handle by this factory
+ */
+ public Class<?> loadClass(String name)
+ throws ClassNotFoundException;
+ }
}