Für die Spezialisten: Erweiterebare DB-Funktionaliät für Eclipse RCP

Vatar

Erfahrenes Mitglied
Hallo alle miteinander.

Heute habe ich, so denke ich zumindest, ein etwas schwierigere Frage. In meiner RCP nutze ich Hibernate für die Datenbankabstraktion. Im eclipse Magazin war dann noch ein interessanter Artikel wie man die Datenbank die von HIbernate genutzt wird austauschen kann ohne dabei am Code oder der Hibernatekonfiguration rumbasteln zu müssen. Lange Rede kurzes Sinn, das ganze funktioniert wunderbar über Extension Points (dadurch bringt das DBPlugin den Treiber, Dialekt, sowie das Protokol mit) und Preferences (für Verbindungsdaten wie Pfad, User, usw).

Jetzt zum eigentlich Problem: diese Architektur kann nur über eine seperate Startkonfiguration genutzt werden und es können nicht mehreren Datenbankplugins gleichzeitig verwendet werden. Ich stelle mir das so vor, dass ich mehrere Datenbankplugins hinzufügen kann und diese dann über die Optionen (Preferences) auswählen kann. Idealerweise sollte jedes weitere Datenbankplugin automatisch in die Optionen übernommen werden (extension points nutzen?).

Wurde eine Datenbank aus den Optionen ausgewählt, so wird diese automatisch beim nächsten Start genutzt (im Falle einer embedded DB auch gestartet)

Leider ist mein Wissen bezüglich extension points sehr begrenzt :). Wie kann ich beispielsweise mehrere Ausprägungen (extensions) auswerten? Oder ist so etwas überhaupt machbar?

Bin für jeden Vorschlag dankbar
greetz
 
Hallo,

wie man eine (Serverside) Eclipse Anwendung mit Hibernate über Extension Points dynamisch um Mapping Informationen erweitert hab ich schon mal hier gezeigt:
http://www.tutorials.de/forum/java/...ate-equinox-osgi-eclipse-extensionpoints.html

Ein Beispiel zu Extension Points findest du hier:
http://www.tutorials.de/forum/java/265296-extension-points-eclipse-fleh.html

Ansonsten würd ich mir einfach neben einem Extension Point für die Mapping Locations noch einen Extension Point für die Hibernate Configuration anlegen.

Über den MappingLocations Extension Point können andere Bundles dann MappingLocations bei der Hibernate Laufzeit registrieren. Über den Hibernate Configuration Extension POint wird dann die Hibernate Laufzeit konfiguriert.

Man könnte sich folgende bundles vorstellen:
de.tutorials.example.persistence.hibernate:
Enthält die Jars der Hibernate Laufzeit und Code der diese Konfiguriert. Dabei wird über die Extension Registry geschaut welche Hibernate Konfigurationen zur Verfügung stehen (natürlich sollte man hier irgendwie bestimmen welche nun gewählt werden soll -> entsprechende Preference Page). Hat man dann eine entsprechende Hibernate Configuration gefunden, geht man über den HibernateMappingLocations extension point drüber, sammelt alle Mapping Locations und übergibt diese an die Hibernate Konfiguration. Nun kann man mit der vollständigen Hibernate Konfiguration die Hibernate Laufzeit starten.
Dieses bundle ist mit der Eclipse-BuddyPolicy: registered im Manifest.mf deklariert.

de.tutorials.example.persistence.mysql:
Stellt den mysql-jdbc Treiber und die mysql-Spezifische Hibernate Konfiguration (hibernate.cfg.xml) zur Verfügung.
Dieses Bundle ist abhängig von de.tutorials.example.persistence.hibernate (oder einem allgemeinen persistence Bundle) und exportiert die mysql-jdbc-driver Klassen.
Hier ist im Manifest.mf Eclipse-RegisterBuddy: de.tutorials.example.persistence.hibernate
gesetzt.

Eine weitere Möglichkeit wäre die das: de.tutorials.example.persistence.mysql bundle als fragment zu definieren und als hostbundle de.tutorials.example.persistence.hibernate / de.tutorials.example.persistence zu wählen (de.tutorials.example.persistence.hibernate wäre dann abhängig von de.tutorials.example.persistence). Die hibernate spezifische Konfiguration für mysql wäre dann in einem anderen Bundle: de.tutorials.example.persistence.mysql.hibernate
abgelegt und dort entsprechend als extension zum HibernateConfiguration extension point definiert.

Mit den mappings könnte es hier jedoch zu Problemen kommen, da die Mappings teilweise Datenbank abhängig sein können (Schema anders, Datentypen anders, etc.) u.U. müsste man die auch noch doppelt pflegen...

Gruß Tom
 
Also erst einmal danke für die Hilfe, die Auswahl läuft jetzt über die Optionen und neue Plugins werden der Auswahl automatisch hinzugefügt.

Jetzt habe ich nur noch das Problem mit dem Starten von embedded Versionen. Ich habe mir überlegt ob ich dies nicht einfach über ein Interface abwickeln könnte. Jedes Datenbankplugin muss eine Klasse mit diesem Interface implementieren (beinhaltet start und stop). Im Falle von MySQL würde da nichts drin stehen, aber im Fall von Derby würde eben die Datenbank hochgefahren/gestoppt.

Meine Frage ist jetzt, wie ich an diese Klasse herankomme: Muss ich das über Reflection machen oder reicht das BuddyClass-Loading aus? Im Fall von Reflection muss ich ja auch erst mal an den richtigen ClassLoader rankommen, da jedes Plugin ja seinen eigenen hat.
 
Hallo,

also wenn du eine Klasse aus einem anderen Bundle mit dessen BundleClassLoader laden willst kannst du das über Platform.getBundle("de.tutorials.bubu").loadClass("de.tutorials.Baba"); machen.
Wenn du ein gemeinsames Interface hier verwendest muss dieses für alle Bundles sichtbar sein (Beispielsweise über entsprechende Exports und Buddy-ClassLoading) sonst gibts ClassCastExceptions.

Gruß Tom
 
Wieder einmal Danke Tom :)

Ich habe während des Testens deiner Lösung noch festgestellt dass ich ja das Bundle auch einfach starten könnte und den Datenbankcode in dieser Methode unterbringen kann. Aber dann muss ich mein Plugin wieder von org.eclipse.core.runtime abhängig machen weshalb ich mich jetzt doch für Interface-Lösung entschieden habe.

Es funktioniert auch alles wunderbar nur im Moment muss ich noch den konkreten Klassennamen angeben um die Klasse zu laden, ich würde aber lieber nur das Interface angeben (siehe Zeile 13). Ist das Möglich?

Java:
private void startDatabaseEnvironment() {
	// get the selected database environment from preferences
	String database = HibernatePlugin.getDefault().getPreferences().getString(IHibernatePreferenceConstants.PREFERENCE_DATABASE_NAME);
	// get the installed database extensions first
	List<IConfigurationElement> installedDatabaseExtensions = HibernatePlugin.getExtensionPointElements("Configuration");
	for(IConfigurationElement element : installedDatabaseExtensions){
		// select the configuration according to the selected database
		if(element.getAttribute("DatabaseName").equals(database)){
			IContributor contributor = element.getContributor();
			// Get bundle ClassLoader
			databaseBundle = Platform.getBundle(contributor.getName());
			try{
				String className = contributor.getName() + ".DatabaseManager";   // <------ Hier hatte ich gerne das Interface stehen
				Class <IDatabaseManager> c = databaseBundle.loadClass(className);
				IDatabaseManager o = (IDatabaseManager)c.newInstance();
				o.start(HibernatePlugin.getDefault().getPreferences().getString(IHibernatePreferenceConstants.PREFERENCE_DATABASE_CONNECTION_PATH));
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
 
Zuletzt bearbeitet:
Hallo,

also wenn du schon Java 6 benutzt könntest du das über den Service Provider Mechanismus machen...
Ansonsten würde ich einfach den Extension Point so erweitern dass du da auch deine "DatabaseManager" Klasse mit angibst. Dann ziehst du einfach nur noch das entsprechende ConfigurationElement aus der ExtensionRegistry und liest dann den Wert des databaseManagerClassName Attributes aus.

Gruß Tom
 
Ok, da ich gerade keine einfache Erklärung für diesen Service Provider finde mache ich das über den extension point.
 
Zurück