OSGi und MySQL ClassNotFoundException

janiwani

Grünschnabel
Hallo,

ich schreibe gerade an einem OSGi Bundle welches unter anderem auf einer MySQL Datenbank lesen/schreiben soll.
Habe standartmäßig die folgende Implementierung gewählt um eine Verbindung herzustellen:
Code:
try {
      Class.forName("com.mysql.jdbc.Driver");
      connection = DriverManager.getConnection(......);
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

Leider bekomme ich an dieser stelle nur eine ClassNotFoundException.
Folgendes habe ich schon probiert:
1. und Project->Properties->Java Build Path den mysql-connector hinzugefügt.
2. In der Manifest.MF unter Runtime den Classpath geändert/angepasst.

Hat bei mir bis jetzt nichts funktionert.
Vielleicht kann mir jemand weiterhelfen?
Vielen Dank schonmal im voraus!
Grüße jan
 
Für die Sichtbarkeit einer Klasse in einem OSGi Bundle müssen 2 Kriterien erfüllt sein:

1) Das referenzierte Bundle muss ein OSGi Bundle sein, dass öffentliche Packages im MANIFEST.MF per Export-Package deklariert
2) Das referenzierende Bundle muss Packages aus referenzierten Bundles in seinem MANIFEST.MF per Import-Package deklarieren.

Angewandt auf dein Problem gehe ich davon aus, dass du mit deinem 2. mein 2. erfüllt hast. Stellt sich nur noch die Frage ob das mysql-connector.jar überhaupt ein korrektes (OSGi fähiges) MANIFEST.MF enthält.

Desweiteren solltest du in einer OSGi Umgebung nicht unbedingt den DriverManager benutzen, sondern eher eine Implementierung von DataSource (z.B. aus Commons-DBCP), da der DriverManager gern Probleme in Verbindung mit dynamischen Classloadern (und damit besonders im OSGi Umfeld) sorgt.

Gruß
Ollie
 
Hallo und Dake für die Antwort,

leider hat das nichts gebracht. Ich poste einfach mal meine Manifest ohne die Referenzen auf den Connector. Wo müsste man diesen denn manuell eintragen?

Code:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: TestDB Plug-in
Bundle-SymbolicName: TestDB
Bundle-Version: 1.0.0
Bundle-Activator: testdb.Activator
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework;version="1.3.0"

Grüße Jan
 
Hallo,

1. und Project->Properties->Java Build Path den mysql-connector hinzugefügt.
Das bringt hier nichts. Dass musst du schon im Manifest.MF unter Runtime eintragen.

2) Das referenzierende Bundle muss Packages aus referenzierten Bundles in seinem MANIFEST.MF per Import-Package deklarieren.
AFAIK ist das nicht unbedingt notwendig. Das ist nur eine Möglichkeit neben BuddyClassLoading und / oder entsprechend gesetzten Dependencies.

Weiterhin ist der DriverManager veraltet. Heute sollte man die entsprechenden DataSource-Implementierungen verwenden.

Außerdem ist Class.forName in einer OSGi-Umgebung nicht sehr zuverlässig:
http://radio.weblogs.com/0112098/2002/09/24.html#a114
http://lists.jboss.org/pipermail/jboss-dev-forums/2008-March/019173.html
etc. siehe google...
Hier besser den Bundle-ClassLoader oder den aktuellen contextClassLoader des Threads verwenden.

Hier mal ein sehr einfaches Beispiel:

Im bestehenden OSGi-Bundle (Plug-in Projekt):
1) lib Folder anlegen.
2) entsprechendes mysql.jar dort rein kopieren
3) Manifest.MF -> Runtime -> Classpath -> Add
-> lib/mysqlxxxx.jar
4) Dann einfach im Activator
Java:
package de.tutorials.osgi.mysql;

import java.sql.Connection;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

public class Activator implements BundleActivator {

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext
	 * )
	 */
	public void start(BundleContext context) throws Exception {
		System.out.println("Hello World!!");

		MysqlDataSource mysqlDataSource = new MysqlDataSource();

		mysqlDataSource.setUser("root");
		mysqlDataSource.setPassword("tutorials");
		mysqlDataSource.setServerName("localhost");
		mysqlDataSource.setDatabaseName("test");

		Connection connection = mysqlDataSource.getConnection();

		System.out.println(connection);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
	 */
	public void stop(BundleContext context) throws Exception {
		System.out.println("Goodbye World!!");
	}

}

Fertig.

Wenn man mehrere unterschiedliche Datenbanktreiber unterstützen will kann man dass natürlich noch viel eleganter realisieren. Beispielsweise könnte man einen neuen JDBC Treiber mit einer entsprechenden Konfigurationsdatei in ein eigens Fragment-Bundle wrappen dass dann in den Classpath eines anderen Bundles legt (Host-Bundle) welches dann die Konfigurationsdatei im Classpath sucht und so den passenden JDBC Treiber findet mit dem dann eine Datenbankverbindung aufgebaut werden kann.

Eine andere Möglichkeit wären hier auch OSGi Services (sowas wie ein ConnectionProviderService).

Gruß Tom
 
Wie oben beschrieben bei Import-Package. Allerdings solltest du - ebenfalls wie beschrieben - als erstes nachschauen, ob das Treiber JAR wirklich ein OSGi kompatibles MANIFEST.MF enthält. Dann sollte da auch eine Version drinstehen, die du in deinem MANIFEST.MF in Import-Package benutzen musst.

Gruß
Ollie
 
Hallo,

also es funktioniert jetzt. Ich weiss zwar nicht wieso, denn ich hatte das Beispiel von Thomas bereits schon ausprobiert und da funktionerte es nicht, aber eine Nacht später und es geht :confused:

Aber trotzdem vielen Dank für die Hilfe.
Grüße
Jan
 
Eclipse als Buildsystem ist nicht gerade fehlerunanfällig. Und OSGi mit Maven oder Ant steckt noch in den Kinderschuhen. Das ist überhaupt DER Grund warum ich z.Zt. noch keinen Mehrwert darin sehe, mich intensiver mit den an sich sehr spannenden Themen in dem Umfeld zu beschäftigen.

Aber gut, wenn es für dich tut. ;)

Gruß
Ollie
 
Zurück