jdbc treiber (h2) mit eigenem ClassLoader laden

Status
Nicht offen für weitere Antworten.

modestiny

Mitglied
Hi,

ich hab mal wieder eine Frage. Und zwar muß ich die JDBC-Treiber von h2 mittels eines eigenen ClassLoaders laden. Normalerweise wird das ja so gemacht:
Java:
import java.sql.*;
public class Test {
    public static void main(String[] a)
            throws Exception {
        Class.forName("org.h2.Driver");
        Connection conn = DriverManager.
            getConnection("jdbc:h2:~/test", "sa", "");
        // add application code here
        conn.close();
    }
}

Nun kann ich aber nicht den System-ClassLoader nehmen, da das Prog ein Installationtool ist, welches die h2-JAR in ein Verzeichnis installiert, in dem die spätere Anwendung (die ebenfalls installiert wird) läuft. Das Inst-Prog muß aber noch einige Tabellen in der DB erstellen und Einträge in diesen vornehmen. Somit ist zur Startzeit des Inst-Prog der spätere Installpath nicht bekannt, da dieser vom Benutzer gesetzt wird.

Nun habe ich mir gedacht, ich baue mir einen eigenen ClassLoader, der den Treiber lädt. Aber dies brachte neue Probleme mit sich. Erst wurden Klassen wie z.B. java.sql.Driver nicht mehr gefunden, die innerhalb des h2-jdbc-treibers referenziert werden. Dies konnte ich aber noch lösen, in dem ich bei nicht auffinden der Klasse innerhalb der h2-JAR den System-ClassLoader bemühe. Aber sobald ich den DriverManager bemühe eine Connection aufzubauen, sagt dieser das kein entsprechender Treiber gefunden werden konnte (no suitable driver...). Daraus schließe ich jetzt mal, das mein ClassLoader den Treiber nicht richtig initialisiert, so daß dieser sich beim DriverManager anmeldet.

Hier mal der Code meises ClassLoaders:
Java:
package WebSetup.Setup;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

public class JarClassLoader extends ClassLoader
{
	private JarFile jarFile;
	
	public JarClassLoader(File jarFile) throws IOException
	{
		this.jarFile = new JarFile(jarFile);
	}
	
	protected Class<?> findClass(String className)
	{
		Class<?> ret = null;
		
		try
		{
			String classString = className.replace(".", "/") + ".class";
			System.out.println("Try to find Class: " + classString);
			ZipEntry zipEntry = this.jarFile.getEntry(classString);
			if(zipEntry != null)
			{
				System.out.println("Class found...");
				InputStream in = this.jarFile.getInputStream(zipEntry);
				int b;
				ArrayList<Byte> array = new ArrayList<Byte>();
				while((b = in.read()) != -1)
				{
					array.add(new Byte((byte)b));
				}
				byte[] buffer = new byte[array.size()];
				for(int i = 0; i < buffer.length; i++)
				{
					buffer[i] = array.get(i).byteValue();
				}
				System.out.println("Class data length: " + buffer.length);
				ret = this.defineClass(className, buffer, 0, buffer.length);
				this.resolveClass(ret);
			}
			else
			{
				ret = Class.forName(className);
			}
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
		catch (ClassNotFoundException e)
		{
			e.printStackTrace();
		}
		return(ret);
	}
	
	public Class<?> loadClass(String className)
	{
		Class<?> ret = this.findClass(className);
		return(ret);
	}
}

Außerdem fand ich noch etwas in den Sourcen des h2-Treibers, was die Initialisierung angeht, von dem ich aber weder wußte, daß es sowas gibt, noch wie sowas von der Funktion her abläuft.
Java:
/*
 * Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
 * Version 1.0, and under the Eclipse Public License, Version 1.0
 * ([url=http://h2database.com/html/license.html]License[/url]).
 * Initial Developer: H2 Group
 */
package org.h2;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.util.Properties;

import org.h2.engine.Constants;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.Message;
import org.h2.message.TraceSystem;

/**
 * The database driver. An application should not use this class directly. The
 * only thing the application needs to do is load the driver. This can be done
 * using Class.forName. To load the driver and open a database connection, use
 * the following code:
 *
 * <pre>
 * Class.forName(&quot;org.h2.Driver&quot;);
 * Connection conn = DriverManager.getConnection(
 *      &quot;jdbc:h2:&tilde;/test&quot;, &quot;sa&quot;, &quot;sa&quot;);
 * </pre>
 */
public class Driver implements java.sql.Driver {

    private static final Driver INSTANCE = new Driver();
    private static volatile boolean registered;

    static {
        load();
    }

    /**
     * Open a database connection.
     * This method should not be called by an application.
     * Instead, the method DriverManager.getConnection should be used.
     *
     * @param url the database URL
     * @param info the connection properties
     * @return the new connection
     */
    public Connection connect(String url, Properties info) throws SQLException {
        try {
            if (info == null) {
                info = new Properties();
            }
            if (!acceptsURL(url)) {
                return null;
            }
            return new JdbcConnection(url, info);
        } catch (Exception e) {
            throw Message.convert(e);
        }
    }

    /**
     * Check if the driver understands this URL.
     * This method should not be called by an application.
     *
     * @param url the database URL
     * @return if the driver understands the URL
     */
    public boolean acceptsURL(String url) {
        return url != null && url.startsWith(Constants.START_URL);
    }

    /**
     * Get the major version number of the driver.
     * This method should not be called by an application.
     *
     * @return the major version number
     */
    public int getMajorVersion() {
        return Constants.VERSION_MAJOR;
    }

    /**
     * Get the minor version number of the driver.
     * This method should not be called by an application.
     *
     * @return the minor version number
     */
    public int getMinorVersion() {
        return Constants.VERSION_MINOR;
    }

    /**
     * Get the list of supported properties.
     * This method should not be called by an application.
     *
     * @param url the database URL
     * @param info the connection properties
     * @return a zero length array
     */
    public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
        return new DriverPropertyInfo[0];
    }

    /**
     * Check if this driver is compliant to the JDBC specification.
     * This method should not be called by an application.
     *
     * @return true
     */
    public boolean jdbcCompliant() {
        return true;
    }

    /**
     * INTERNAL
     */
    public static synchronized Driver load() {
        try {
            if (!registered) {
                registered = true;
                DriverManager.registerDriver(INSTANCE);
            }
        } catch (SQLException e) {
            TraceSystem.traceThrowable(e);
        }
        return INSTANCE;
    }

    /**
     * INTERNAL
     */
    public static synchronized void unload() {
        try {
            if (registered) {
                registered = false;
                DriverManager.deregisterDriver(INSTANCE);
            }
        } catch (SQLException e) {
            TraceSystem.traceThrowable(e);
        }
    }

}
Ich meine dieses einsame
Java:
static { load(); }
am Anfang der Klasse.

Wann wird sowas ausgeführt und wodurch?

Danke in Vorraus

Lars
 
Zuletzt bearbeitet von einem Moderator:

mvitz

Top Contributor
Ist jetzt keine direkte Antwort, sondern eher eine Idee.

h2 kann ja die DB auch als Datei speichern, wenn du dies eh machst, kannst du doch evtl. einfach die richtig initialisierte DB Datei mit installieren.

Das funktioniert natürlich nur, wenn das DB Schema nicht von irgendetwas abhängt, was erst während der Installation gesetzt wird.
 

modestiny

Mitglied
Tja und das ist leider der Fall.

Es werden informationen in die DB eingetragen, die erst bei der Installation gesammelt werden.

Gruß

Lars
 

Ebenius

Top Contributor
Nun habe ich mir gedacht, ich baue mir einen eigenen ClassLoader, der den Treiber lädt.
Wieso? Was gibt's denn gegen den URLClassLoader einzuwenden?

Daraus schließe ich jetzt mal, das mein ClassLoader den Treiber nicht richtig initialisiert, so daß dieser sich beim DriverManager anmeldet.
Dieser Schluss ist falsch. Der DriverManager erlaubt ganz explizit nur Treiber die über den selben ClassLoader erreichbar sind, den auch der Caller benutzt. Zwei Lösungsvorschläge...

Vorschlag 1: (der einfache Weg) Mach Deine Connection einfach ohne DriverManager. Hier am Beispiel Oracle, weil ich kein MySQL habe:
Java:
static Connection getOracleConnection(
      String jdbcURLString,
      Properties props) throws SQLException {
  final Driver driver = getOracleDriver();
  return driver.connect(jdbcURLString, props);
}

static Driver getOracleDriver() throws SQLException {
  File driverJARFile =
        new File("/opt/oracle/product/10gR2/jdbc/lib/ojdbc14.jar");
  URL driverJarUrl;
  try {
    driverJarUrl = driverJARFile.toURI().toURL();
  } catch (MalformedURLException ex) {
    throw new SQLException("Could not load JDBC Driver: " + driverJARFile,
          "IM003", ex);
  }

  String driverClassName = "oracle.jdbc.driver.OracleDriver";
  return loadDriver(driverJarUrl, driverClassName);
}

static Driver loadDriver(URL driverJARURL, String driverClassName)
      throws SQLException {
  try {
    final URLClassLoader cl =
          new URLClassLoader(new URL[] { driverJARURL });

    final Class<?> driverClass = Class.forName(driverClassName, true, cl);
    final Driver driver = (Driver) driverClass.newInstance();
    return driver;
  } catch (ClassNotFoundException ex) {
    throw new SQLException(
          "Could not load JDBC Driver: " + driverClassName, "IM003", ex);
  } catch (InstantiationException ex) {
    throw new SQLException(
          "Could not load JDBC Driver: " + driverClassName, "IM003", ex);
  } catch (IllegalAccessException ex) {
    throw new SQLException(
          "Could not load JDBC Driver: " + driverClassName, "IM003", ex);
  }
}

Vorschlag 2: (der Weg gefällt mir besser): Diese Klasse ist im Installationsprogramm enthalten:
Java:
/* (@)JDBCDriverManager.java */

/* Copyright 2009 Sebastian Haufe

 * Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       [url]http://www.apache.org/licenses/LICENSE-2.0[/url]

 * Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. */

package com.ebenius;

import java.net.URL;
import java.net.URLClassLoader;
import java.sql.*;
import java.util.*;
import java.util.logging.Logger;

/**
 * A stub to load a JDBC Driver, working around the problem that the
 * {@link DriverManager DriverManager class} cannot load Drivers within an
 * alternative ClassLoader context.
 * 
 * @version 1.0
 * @author Sebastian Haufe
 */
public abstract class JDBCDriverManager {

  // -------------------------------------------------------------------------
  // Class members
  // -------------------------------------------------------------------------

  private static URL implURL;
  private static final String implClassName =
        JDBCDriverManager.class.getPackage().getName()
              + ".JDBCDriverStubImpl";

  private static boolean initialized = false;
  private static Collection<Stub> stubs =
        Collections.synchronizedCollection(new ArrayList<Stub>());

  static {
    try {
      Class.forName(implClassName);
      Logger.getLogger("JDBCDriverManager.class.getName()").severe(
            implClassName
                  + " is available per our class loader. "
                  + "Enhanced driver support will not work!");
    } catch (ClassNotFoundException ex) {
      // okay
    }
  }

  /**
   * Get the JDBC driver to the given URL.
   * 
   * @param url the URL
   * @return the driver
   * @throws SQLException if no such driver exists
   * @see DriverManager#getDriver(String)
   */
  public static Driver getDriver(String url) throws SQLException {
    /* try to get the driver from our context */
    SQLException storedException = null;
    try {
      return DriverManager.getDriver(url);
    } catch (SQLException ex) {
      storedException = ex;
    }

    /* find the driver by stubs */
    synchronized (stubs) {
      for (Stub stub : stubs) {
        try {
          return stub.getDriver(url);
        } catch (SQLException ex) {
          // ignore, we throw the stored exception if all attempts fail
        }
      }
    }

    throw storedException;
  }

  /**
   * Get a connection to the given JDBC URL with the given properties.
   * 
   * @param url the JDBC URL
   * @param info the properties
   * @return the connection
   * @throws SQLException if the connection cannot be established for any
   *           reason
   * @see DriverManager#getConnection(String, Properties)
   */
  public static Connection getConnection(String url, Properties info)
        throws SQLException {
    return getDriver(url).connect(url, info);
  }

  /**
   * Register a JDBC driver. The URL is used by an {@link URLClassLoader} to
   * load the driver.
   * 
   * @param driverResourceURL the URL to the driver (JAR file, directory, ...)
   * @param driverClassName the class name of the driver implementation to
   *          initialized
   * @throws SQLException if registering the driver fails for any reason
   * @throws IllegalStateException if this class is not initialized
   * @see #initialize(URL)
   */
  public static void registerDriver(
        URL driverResourceURL,
        String driverClassName) throws SQLException {
    if (!initialized) {
      throw new IllegalStateException("Stub not initialized!");
    }

    final URLClassLoader cl =
          new URLClassLoader(new URL[] { driverResourceURL, implURL });
    try {
      final Class<?> stubImplClass = Class.forName(implClassName, true, cl);
      stubs.add((Stub) stubImplClass.newInstance());
    } catch (Exception ex) {
      throw new SQLException("Could not load Driver Stub Impl: " + implURL,
            "IM003", ex);
    }

    /* let the driver register itself */
    try {
      Class.forName(driverClassName, true, cl);
    } catch (ClassNotFoundException ex) {
      throw new SQLException(
            "Could not load JDBC Driver: " + driverClassName, "IM003", ex);
    }
  }

  /**
   * Returns the URL to the stub implementation (JAR file or directory).
   * 
   * @return the URL &ndash; possibly {@code null} (if not yet initialized)
   * @see #initialize(URL)
   */
  public static URL getStubImplURL() {
    return implURL;
  }

  /**
   * Sets the URL to the stub implementation (JAR file or directory).
   * 
   * @param stubImplURL the URL to set
   */
  public static void initialize(URL stubImplURL) {
    synchronized (stubs) {
      if (stubImplURL == null) {
        throw new IllegalArgumentException( //
              "stubImplURL not allowed null"); //$NON-NLS-1$
      }

      JDBCDriverManager.implURL = stubImplURL;
      initialized = true;
    }
  }

  // -------------------------------------------------------------------------
  // Inner classes
  // -------------------------------------------------------------------------

  public interface Stub {

    /**
     * Loads the JDBC Driver to the given URL.
     * 
     * @param url the URL to load the JDBC driver for
     * @return the JDBC Driver
     * @throws SQLException if loading the driver fails for any reason
     */
    public abstract Driver getDriver(String url) throws SQLException;
  }
}

Diese Klasse liegt in einer extra JAR-Datei die nicht über den normalen ClassLoader verfügbar ist, aber im selben package wie die andere Klasse:
Java:
package com.ebenius;

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;

public final class JDBCDriverStubImpl implements JDBCDriverManager.Stub {

  /** Creates a new {@code JDBCDriverStubImpl}. */
  public JDBCDriverStubImpl() {}

  public Driver getDriver(String url) throws SQLException {
    return DriverManager.getDriver(url);
  }
}

Verwendet wird dann Vorschlag 2 so:
Java:
String jdbcURLString = "jdbc:oracle:thin:@//workhorse:1521/MYDB";
final Properties p = new Properties();
p.setProperty("user", "XXXX");
p.setProperty("password", "YYYY");

JDBCDriverManager.initialize(new File(
      "/home/ebenius/workspace/Playground/jdbcdriverstubimpl.jar").toURI().toURL());
JDBCDriverManager.registerDriver(new File(
      "/opt/oracle/product/10gR2/jdbc/lib/ojdbc14.jar").toURI().toURL(),
      "oracle.jdbc.driver.OracleDriver");

final Connection conn = JDBCDriverManager.getConnection(jdbcURLString, p);

Java:
static { load(); }
am Anfang der Klasse.
Das nennt sich static initialization. Diese Blöcke werden beim Initialisieren der Klasse ausgeführt; also frühestens direkt nach dem Laden der Klasse und spätestens direkt vor der ersten Verwendung der Klasse. Mehr dazu im Sun Java™ Tutorial: Initializing Fields

Ebenius
 
Zuletzt bearbeitet:
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
V Maven Maven ClassNotFoundException MySQL JDBC Treiber Allgemeine Java-Themen 1
Kirby.exe JDBC Connector von Maven Repo importieren Allgemeine Java-Themen 1
O Zugriff auf mySQL ohne JDBC Allgemeine Java-Themen 3
M JDBC DATE 0000-00-00 kann nicht ausgegeben werden Allgemeine Java-Themen 3
I Performance - JDBC UPC PoolDataSoure Allgemeine Java-Themen 0
K Classpath JDBC Driver auf Server Allgemeine Java-Themen 4
F Alternative sun.jdbc.odbc.JdbcOdbcDriver (Access Datenbank) Allgemeine Java-Themen 2
A Classpath programm findet jdbc nicht Allgemeine Java-Themen 4
D JDBC Allgemeine Java-Themen 8
M Junit und Mocks bei JDBC Daos Allgemeine Java-Themen 8
G Große Datenmengen per JDBC Allgemeine Java-Themen 5
G JDBC Programm und niedrige Java-Versionen Allgemeine Java-Themen 14
G Text aus Zwischenablage in JTextarea -> JDBC Error Allgemeine Java-Themen 3
H Sekunden messen wie lange h.ibernate/jdbc abfragen brauchen Allgemeine Java-Themen 7
T JDBC: Unterschiede in Interfaces zwischen 2 Java-Versionen. Allgemeine Java-Themen 6
L JDBC Erklärung zu einer Java Anwendung Allgemeine Java-Themen 2
märliprinz com.sap.dbtech.jdbc.exceptions.JDBCDriverException Allgemeine Java-Themen 2
C ClassLoader - dymisches Laden von JDBC Drivers Allgemeine Java-Themen 3
V gezielte Datenbankeinträge über JDBC schnittstelle Allgemeine Java-Themen 10
M JDBC Class not found Allgemeine Java-Themen 2
Fredy JDBC: character Werte aus Tabellenspalten holen?? Allgemeine Java-Themen 2
S Java Treiber (JavaPOS) Allgemeine Java-Themen 7
S ANT mysql treiber einbinden Allgemeine Java-Themen 4
C RXTX Treiber einbinden für Linux Allgemeine Java-Themen 6
G PostScript-Treiber Allgemeine Java-Themen 2
Zrebna Zuverlässiges Automatisiertes Testen im eigenem Software-Unternehmen aufsetzen - How to? Allgemeine Java-Themen 12
C Frage zu eigenem TableCellRenderer Allgemeine Java-Themen 11
MiMa Log4j in Dateien mit eigenem Namen schreiben Allgemeine Java-Themen 3
D JAVA Basiertes Spiel aus dem Internet in eigenem Client laden Allgemeine Java-Themen 3
N ArrayList in eigenem Object nicht richtig serialisierbar Allgemeine Java-Themen 14
T AES-Verschlüsselung mit eigenem 256 Bit Schlüssel Allgemeine Java-Themen 12
M Über Liste verschiendene JComponents mit eigenem implementierten Interface ansprechen Allgemeine Java-Themen 7
K Programm startet nur auf eigenem Rechner??? Allgemeine Java-Themen 6
Dragonfire Datentypen Map effizient mit eigenem Key Allgemeine Java-Themen 71
DStrohma Jede node in JTree mit eigenem Icon Allgemeine Java-Themen 7
M Eigenem Dateiformat Icon zuweisen Allgemeine Java-Themen 6
S Problem mit eigenem DatenTyp. HILFE!!! Allgemeine Java-Themen 4
D Abstruse Probleme mit eigenem replace Algorithmus Allgemeine Java-Themen 11
R HashSet mit eigenem Objekt als Key Allgemeine Java-Themen 10
A jpeg Files mit eigenem Programm öffnen Allgemeine Java-Themen 4
F Probleme mit eigenem Plugin-System Allgemeine Java-Themen 3
H externe JARs in eigenem Programm-Jar einbinden. Allgemeine Java-Themen 5
F "source not found" in eigenem Projekt mit eigenen Allgemeine Java-Themen 2
W Externes Programm beenden mit eigenem Programm beenden Allgemeine Java-Themen 7
D Programm mit eigenem ClassLoader funktioniert nicht Allgemeine Java-Themen 12
K KeyEvent in eigenem Component geht nicht Allgemeine Java-Themen 3
R Problem beim speichern von eigenem objekt Allgemeine Java-Themen 6

Ähnliche Java Themen

Neue Themen


Oben