Reflection - wie anwenden/benutzen bei folgendem Problem

y0dA

Erfahrenes Mitglied
Hi!
Also ich habe eine Klasse OfflineProcessor, von jener sind folgende 2 Klassen eine Spezialisierung: FileSystemProcessor und DatabaseProccesor. Die beiden Spezialisierungen wiederum besitzen eine Klassenvariable welche ein Interface (ITrackParser) implementieren sollte.

Also grafisch ungefähr so:

OfflineProcessor
^ ^
| |
FileSystemProcessor DatabaseProcessor
| |
use use
| |
ITrackParser ITrackParser <-------NMEAParser (Beispiel Implementation)


Nun liegt mein Problem darin dass es möglich sein sollte dass man die Klasse welche ITrackParser implementiert auch von dritten implementiert werden können. Nun brauche ich irgendeine Möglichkeit dass, wenn ich der Klasse OfflineProcessor den Pfad zum JAR File der Implementation zu ITrackParser angebe, dass ich dann jenen Parser in FileSystemProcessor benutze.

Kann mir hier jemand weiterhelfen bzw. ist es überhaupt möglich zur Laufzeit externe JARS zu benutzen?

OfflineProcessor:
Code:
public class OfflineProcessor {
	/**
	 * implementing class name
	 * must implement interface
	 * @see {@link org.pcd.wam.gegenstandsbereich.streetDetection.locating.parser.ITrackParser}
	 */
	private String className;
	
	/**
	 * path to jar libary path which contains <code>{@link #className}</code>
	 * if <code>{@link #className}</code> is already implemented, value is null
	 */
	private String libPath;
	
	/** */
	private String name;
	
	/**
	 * empty constructor
	 */
	public OfflineProcessor() {
		// nothing to do
	}

	/**
	 * getter method
	 * dynamically instantiate a certain processor class
	 * @return processor class
	 */
	protected ITrackParser getTrackInterface() {
		//FIXME wie implementieren?
		return null;
	}
	
	/**
	 * process <code>track</code>
	 * @param track track
	 */
	protected void processTrack(final Track track) {
	}
}

FileSystemProcessor:
Code:
public class FileSystemProcessor extends OfflineProcessor {
	
	/** file path */
	private String filePath;

	/**
	 * 
	 * get closed files with a length greater than 0
	 * @return list of file names
	 */
	public List<String> getFiles() {
		return null;
	}
}

ITrackParser:
Code:
public interface ITrackParser {
	/**
	 * returns all tracks depending on parameter <code>fileName</code>
	 * @param s file path name or a jdbc connection string
	 * @return tracks
	 */
	public List<Track> getTracks(final String s);
	/**
	 * returns a device id which corresponds with already definded devices
	 * if not available return null
	 * @return receiver id
	 */
	public String getReceiverId();
}
mfg
 

Oliver Gierke

Erfahrenes Mitglied
Hm... das ganze klingt nach nem Anwendungsfall für DependencyInjection. DI ist ein Pattern was im groben aussagt, dass Komponenten ihre Dependencies nicht selbst besorgen, sondern injiziert bekommen. WIll eine Komponente Ausdrücken, dass sie eine Instanz einer Fremdkomponente benötigt, tut sie das durch das Bereitstellen eines Setters oder eines Konstruktors mit entsprechenden Parametern.

Welche konkrete Instanz dann zur Laufzeit injiziert wird, wird in einer Konfigurationsdatei beschrieben. Diese wertet ein Container aus und instantiiert und "verdrahtet" die entsprechenden Komponenten. Bekanntestes Beispiel davon ist wohl Spring, dessen Kern ein DI Container bildet.

Deine Klasse OfflineProcessor könnte also zum Beispiel eine Methode setITrackParser(ITrackParser parser) anbeiten und dann wie folgt verdrahtet werden:

XML:
<beans>
  <bean class="foo.bar.FileSystemProcessor">
    <!-- referenziert die Bean iTrackParser und wird durch den entsprechenden Setter injiziert-->
    <property name="iTrackParser" ref="iTrackParser" />
  </bean>

  <bean id="iTrackParser" class="your.class.from.different.jar.FooBar" />
</beans>

So kannst du durch Ändern des class Attributs in der Bean iTrackParser einfach die konkrete Instanz ändern.

Ich vermute du wirst da npch mehr Fragen zu haben, aber ist das grundsätzlich das was du benötigst?

Gruß
Ollie
 
Zuletzt bearbeitet von einem Moderator:

Oliver Gierke

Erfahrenes Mitglied
Du brauchst halt das JAR mit der KLasse your.class.from.different.jar.FooBar im Classpath. Wie das Teil heißt ist ja völlig egal.

Dyniamisches Laden von JARs ist nicht ohne. Das ClassLoading gerät dadurch schnell ausser Kontrolle (Welche KLasse FooBar aus welchem Jar ist denn jetzt gerade geladen?). Wenn es wirklich sein muss, solltest du dir mal OSGi anschauen. Das ist genau dafür gedacht zur Laufzeit JARs zu laden, zu aktivieren usw.

Gruß
Ollie
 

Thomas Darimont

Erfahrenes Mitglied
Hallo,

(Welche KLasse FooBar aus welchem Jar ist denn jetzt gerade geladen?).

Na dass kann man ja leicht zur Laufzeit rausfinden:
Java:
/**
 * 
 */
package de.tutorials;

import org.objectweb.asm.ClassReader;

/**
 * @author Thomas.Darimont
 *
 */
public class ClassOriginExample {

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(ClassReader.class.getProtectionDomain().getCodeSource());
    }

}

Ausgabe (bei mir):
Code:
(file:/D:/stuff/asm/3.1/asm-3.1/lib/asm-3.1.jar <no signer certificates>)

Gruß Tom
 

Oliver Gierke

Erfahrenes Mitglied
Schon klar, aber wer fängt denn an, so einen Mechanismus selber zu coden, gerade, wenn das im großen laufen soll?

Gruß
Ollie
 

y0dA

Erfahrenes Mitglied
Ja aber es geht hier ja darum JARS zur Laufzeit zu benutzen, welche nicht im Classpath sind - da sie zur Compilezeit noch nicht existiert haben.

Also kann man nun die JARs so nachladen wie in dem angegebenen Link oder führt dies zu Problemen?

mfg
 

Thomas Darimont

Erfahrenes Mitglied
Hallo,

unter Java 6 könntest du den java.util.ServiceLoader verwenden:
http://java.sun.com/javase/6/docs/api//java/util/ServiceLoader.html
http://www.javaspecialists.eu/archive/Issue139.html

Bei älteren Versionen kann man sich diesen Mechanismus ganz einfach selbst nachbauen. Einfach das jar das die neuen klassen enthält in den ClassPath legen (entweder per addURL(...) hack oder über einen neuen URLClassLoader der im Konstruktor die neuen Jars übergeben bekommen hat) anschließend scannt man eine Konfigurationsdatei im jar in der die Interface-Implementierungsmappings angegeben sind oder iteriert einfach über alle Klassen und schaut ob sie das gewünschte Interface implementieren.

Gruß Tom