Revision: 4630 http://sourceforge.net/p/jump-pilot/code/4630 Author: edso Date: 2015-12-17 18:58:28 +0000 (Thu, 17 Dec 2015) Log Message: ----------- allow loading of jdbc drivers from lib/ext/<subfolder/>
Modified Paths: -------------- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleValueConverterFactory.java core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDataStoreDriver.java core/trunk/src/com/vividsolutions/jump/datastore/spatialite/SpatialiteDataStoreDriver.java core/trunk/src/com/vividsolutions/jump/workbench/plugin/PlugInManager.java Added Paths: ----------- core/trunk/src/com/vividsolutions/jump/datastore/jdbc/DelegatingDriver.java Added: core/trunk/src/com/vividsolutions/jump/datastore/jdbc/DelegatingDriver.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/jdbc/DelegatingDriver.java (rev 0) +++ core/trunk/src/com/vividsolutions/jump/datastore/jdbc/DelegatingDriver.java 2015-12-17 18:58:28 UTC (rev 4630) @@ -0,0 +1,59 @@ +package com.vividsolutions.jump.datastore.jdbc; + +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Properties; +import java.util.logging.Logger; + +/** + * a jdbc driver wrapper to allow loading the driver with custon classloader + * from an arbitrary location during runtime. + * DatabaseManager.registerDriver() only registers drivers loaded with the + * system classloader so we trick it into accepting our driver by wrapping it + * into this one. + * + * @see + * https://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location + */ +public class DelegatingDriver implements Driver { + private final Driver driver; + + public DelegatingDriver(Driver driver) { + if (driver == null) { + throw new IllegalArgumentException("Driver must not be null."); + } + this.driver = driver; + } + + public Connection connect(String url, Properties info) throws SQLException { + return driver.connect(url, info); + } + + public boolean acceptsURL(String url) throws SQLException { + return driver.acceptsURL(url); + } + + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) + throws SQLException { + return driver.getPropertyInfo(url, info); + } + + public int getMajorVersion() { + return driver.getMajorVersion(); + } + + public int getMinorVersion() { + return driver.getMinorVersion(); + } + + public boolean jdbcCompliant() { + return driver.jdbcCompliant(); + } + + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return driver.getParentLogger(); + } +} \ No newline at end of file Property changes on: core/trunk/src/com/vividsolutions/jump/datastore/jdbc/DelegatingDriver.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleValueConverterFactory.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleValueConverterFactory.java 2015-12-16 12:15:55 UTC (rev 4629) +++ core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleValueConverterFactory.java 2015-12-17 18:58:28 UTC (rev 4630) @@ -5,6 +5,8 @@ import com.vividsolutions.jump.datastore.jdbc.ValueConverterFactory; import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesValueConverterFactory; import com.vividsolutions.jump.feature.AttributeType; +import com.vividsolutions.jump.workbench.JUMPWorkbench; + import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -33,20 +35,29 @@ IllegalAccessException, IllegalArgumentException, InvocationTargetException { Object geometryObject = rs.getObject(columnIndex); - Class converterClazz = Class - .forName("org.geotools.data.oracle.sdo.GeometryConverter"); - Class connectionClazz = Class.forName("oracle.jdbc.OracleConnection"); - Class structClazz = Class.forName("oracle.sql.STRUCT"); + + // we need to use the plugin classloader to find the dependenciy + // jars under lib/ext/<subfolder>/ , additionally we apply some + // reflection to allow the dependencies to be only available + // during runtime + ClassLoader cl = JUMPWorkbench.getInstance().getPlugInManager() + .getClassLoader(); + Class converterClazz = Class.forName( + "org.geotools.data.oracle.sdo.GeometryConverter", true, cl); + Class connectionClazz = Class.forName("oracle.jdbc.OracleConnection", + true, cl); + Class structClazz = Class.forName("oracle.sql.STRUCT", true, cl); Method converterMethod = converterClazz.getMethod("asGeometry", - new Class[]{structClazz}); + new Class[] { structClazz }); Constructor constructor = converterClazz .getDeclaredConstructor(connectionClazz); Object converter = constructor.newInstance(connectionClazz.cast(rs .getStatement().getConnection())); - return converterMethod.invoke(converter, structClazz.cast(geometryObject)); - + return converterMethod + .invoke(converter, structClazz.cast(geometryObject)); + /** * below is the original implementation w/o reflection * KEEP FOR REFERENCE!!! Modified: core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDataStoreDriver.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDataStoreDriver.java 2015-12-16 12:15:55 UTC (rev 4629) +++ core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDataStoreDriver.java 2015-12-17 18:58:28 UTC (rev 4630) @@ -2,12 +2,15 @@ import com.vividsolutions.jump.datastore.DataStoreConnection; import com.vividsolutions.jump.datastore.DataStoreDriver; +import com.vividsolutions.jump.datastore.jdbc.DelegatingDriver; import com.vividsolutions.jump.datastore.mariadb.MariadbDSConnection; import com.vividsolutions.jump.datastore.oracle.OracleDSConnection; import com.vividsolutions.jump.datastore.postgis.PostgisDSConnection; import com.vividsolutions.jump.datastore.spatialite.SpatialiteDSConnection; import com.vividsolutions.jump.parameter.ParameterList; import com.vividsolutions.jump.parameter.ParameterListSchema; +import com.vividsolutions.jump.workbench.JUMPWorkbench; + import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; @@ -34,6 +37,8 @@ protected String[] paramNames = null; protected Class[] paramClasses = null; protected ParameterListSchema schema = null; + + protected boolean registered = false; public SpatialDatabasesDataStoreDriver() { // Nicolas Ribot: @@ -103,8 +108,21 @@ String url = String.valueOf(new StringBuffer(urlPrefix).append(host).append(":").append(port).append("/").append(database)); - Driver driver = (Driver) Class.forName(this.getJdbcClass()).newInstance(); - DriverManager.registerDriver(driver); + // only register once per driver + if (!this.registered) { + // we always use the plugin classloader to find jdbc jars + // under lib/ext/<subfolder>/ + ClassLoader cl = JUMPWorkbench.getInstance().getPlugInManager() + .getClassLoader(); + Driver driver = (Driver) Class.forName(this.getJdbcClass(), true, cl) + .newInstance(); + // DriverManager insists on jdbc drivers loaded with the default + // classloader, so we wrap our foreign one into a simple wrapper + // see + // https://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location + DriverManager.registerDriver(new DelegatingDriver(driver)); + this.registered = true; + } // mmichaud 2013-08-27 workaround for ticket #330 String savePreferIPv4Stack = System.getProperty("java.net.preferIPv4Stack"); Modified: core/trunk/src/com/vividsolutions/jump/datastore/spatialite/SpatialiteDataStoreDriver.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/spatialite/SpatialiteDataStoreDriver.java 2015-12-16 12:15:55 UTC (rev 4629) +++ core/trunk/src/com/vividsolutions/jump/datastore/spatialite/SpatialiteDataStoreDriver.java 2015-12-17 18:58:28 UTC (rev 4630) @@ -8,79 +8,98 @@ import java.util.Properties; import com.vividsolutions.jump.datastore.DataStoreConnection; +import com.vividsolutions.jump.datastore.jdbc.DelegatingDriver; import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDataStoreDriver; import com.vividsolutions.jump.parameter.ParameterList; import com.vividsolutions.jump.parameter.ParameterListSchema; +import com.vividsolutions.jump.workbench.JUMPWorkbench; //import org.sqlite.SQLiteConfig; /** * A driver for supplying {@link SpatialDatabaseDSConnection}s */ -public class SpatialiteDataStoreDriver - extends SpatialDatabasesDataStoreDriver { - - public final static String JDBC_CLASS = "org.sqlite.JDBC"; - - public SpatialiteDataStoreDriver() { - this.driverName = "Spatialite"; - this.jdbcClass = "org.sqlite.JDBC"; - //TODO: prompt for filename - this.urlPrefix = "jdbc:sqlite:"; - - // Panel parameters: adds a file chooser for db name - paramNames = new String[]{PARAM_DB_File}; - paramClasses = new Class[]{File.class}; - schema = new ParameterListSchema(paramNames, paramClasses); - } - - /** - * returns a spatialite JDBC connexion with spatialite extension loaded if possible - * if not, a property will tell so - * - * @param params - * @return - * @throws Exception - */ +public class SpatialiteDataStoreDriver extends SpatialDatabasesDataStoreDriver { + + public final static String JDBC_CLASS = "org.sqlite.JDBC"; + private static boolean initialized = false; + + public SpatialiteDataStoreDriver() { + this.driverName = "Spatialite"; + this.jdbcClass = "org.sqlite.JDBC"; + // TODO: prompt for filename + this.urlPrefix = "jdbc:sqlite:"; + + // Panel parameters: adds a file chooser for db name + paramNames = new String[] { PARAM_DB_File }; + paramClasses = new Class[] { File.class }; + schema = new ParameterListSchema(paramNames, paramClasses); + } + + /** + * returns a spatialite JDBC connection with spatialite extension loaded if + * possible if not, a property will tell so + * + * @param params + * @return + * @throws Exception + */ @Override public DataStoreConnection createConnection(ParameterList params) throws Exception { // PARAM_DB_File is a fileChooser: the default mechanism used will store a - // DefaultAwtShell on OSX. Do not cast to this internal type but gets its toString() method + // DefaultAwtShell on OSX. Do not cast to this internal type but gets its + // toString() method // returning the choosen filename. String database = params.getParameter(PARAM_DB_File).toString(); - - // First checks if the file exists: cannot manage newly created Spatialite files yet: + + // First checks if the file exists: cannot manage newly created Spatialite + // files yet: // File must exists File sqliteFile = new File(database); if (!sqliteFile.exists() || !sqliteFile.canRead()) { // TODO: I18N - throw new Exception("Spatialite file: " + database + " does not exist. cannot create connection"); + throw new Exception("Spatialite file: " + database + + " does not exist. cannot create connection"); } - - // mandatory to load spatialite extension - Class configClazz = Class.forName("org.sqlite.SQLiteConfig"); + + String url = String.valueOf(new StringBuffer(urlPrefix).append(database)); + + ClassLoader cl = JUMPWorkbench.getInstance().getPlugInManager() + .getClassLoader(); + + // we register only once + if (!initialized) { + Driver driver = (Driver) cl.loadClass(this.getJdbcClass()).newInstance(); + // DriverManager insists on jdbc drivers loaded with the default + // classloader, so we wrap our foreign one into a simple wrapper + // see https://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location + DriverManager.registerDriver(new DelegatingDriver(driver)); + initialized = true; +// Enumeration<Driver> ds = DriverManager.getDrivers(); +// while (ds.hasMoreElements()) { +// System.out.println(ds.nextElement()); +// } + } + + // mandatory to enable loading extensions + Class configClazz = cl.loadClass("org.sqlite.SQLiteConfig"); Method enableMethod = configClazz.getMethod("enableLoadExtension", - new Class[]{boolean.class}); - + new Class[] { boolean.class }); + Object config = configClazz.newInstance(); enableMethod.invoke(config, true); // this is the code above w/o reflection, KEEP FOR REFERENCE!!! // mandatory to load spatialite extension - //SQLiteConfig config = new SQLiteConfig(); - //config.enableLoadExtension(true); + // SQLiteConfig config = new SQLiteConfig(); + // config.enableLoadExtension(true); - String url - = String.valueOf(new StringBuffer(urlPrefix).append(database)); - - Driver driver = (Driver) Class.forName(this.getJdbcClass()).newInstance(); - DriverManager.registerDriver(driver); - Method getPropsMethod = configClazz.getMethod("toProperties"); - Properties props = (Properties)getPropsMethod.invoke(config); + Properties props = (Properties) getPropsMethod.invoke(config); Connection conn = DriverManager.getConnection(url, props); return new SpatialiteDSConnection(conn); } + } Modified: core/trunk/src/com/vividsolutions/jump/workbench/plugin/PlugInManager.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/workbench/plugin/PlugInManager.java 2015-12-16 12:15:55 UTC (rev 4629) +++ core/trunk/src/com/vividsolutions/jump/workbench/plugin/PlugInManager.java 2015-12-17 18:58:28 UTC (rev 4630) @@ -84,15 +84,48 @@ if ( plugInDirectory instanceof File ) { ArrayList<File> files = new ArrayList(); files.add( plugInDirectory ); - files.addAll( findFilesRecursively(plugInDirectory,true) ); - classLoader = new URLClassLoader(toURLs(files)); + files.addAll( findFilesRecursively( plugInDirectory,true) ); + //System.out.println(Arrays.toString(files.toArray())); + class ExtendedURLClassLoader extends URLClassLoader{ + + public ExtendedURLClassLoader(URL[] urls) { + super(urls); + } + + /** + * not really necessary now, but we keep it for reference for a future + * classloader per extension for allowing extensions to use differently + * versioned dependency jars in separate subfolders under + * lib/ext/<extension_subfolder>/ + */ + @Override + public Class loadClass(String name) throws ClassNotFoundException { + Class c = findLoadedClass(name); + if (c == null) { + try { + c = getParent().loadClass(name); + } catch (ClassNotFoundException e) { + } + if (c == null) + c = findClass(name); + } + return c; + } + + public void addUrls( URL[] urls ){ + for (URL url : urls) { + addURL(url); + } + } + }; + + ExtendedURLClassLoader mycl = new ExtendedURLClassLoader(new URL[]{}); + mycl.addUrls(toURLs(files)); + classLoader = mycl; } else { classLoader = getClass().getClassLoader(); } -// classLoader = plugInDirectory != null ? new URLClassLoader( -// toURLs(findFilesRecursively(plugInDirectory,true))) : getClass().getClassLoader(); - I18N.setClassLoader(classLoader); this.context = context; this.plugInDirectory = plugInDirectory; ------------------------------------------------------------------------------ _______________________________________________ Jump-pilot-devel mailing list Jump-pilot-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel