Hallo,
mal wieder das leidige Thema und Probleme mit dem URLClassloader.
Könnte bitte jemand auf den code schauen und den/die Fehler finden? danke
Problemstellung:
Für einen FH-Kurs soll jede Gruppe ein Komponentenmodel ala OSGI und Konsorten entwickelt und programmiert werden. Natürlich nicht der komplette Funktionsumfang nur ein proof of concept.
Dabei soll es eine RuntimeEnviroment geben, die Komponenten auf wunsch des Users laden, entladen, starten, stoppen, ... etc. kann.
Unser Entwurf sieht so aus:
2 Java Bibliotheken: libComponent, libRuntime
Die libComponent beinhaltet nur ein Interface ComponentInterface.
Der User muss nur um eine Komponente zu erstellen die libComponent einbinden und seine Komponentenklasse muss das Interface implementieren.
Die Runtime baut auf der libCompnent auf und beinhaltet 2 Klassen zum verwalten der Komponenten (Runtime und ComponentContainer). Die Runtime-Klasse besitzt ein public Interface damit eine Anwendung, die die libRuntime einbindet diese steuern kann.
ComponentInterface:
nun die Klasse Runtime:
Die CopmponentContainer-Klasse (wo die eigentliche Arbeit verrichtet wird -- Und wo es die Probleme hagelt)
Den Code zum laden der Klassen aus einer Jar stammt nicht von mir, google sei dank ... Dachte ihn genug verstanden zu haben damit er funktioniert.
Tja so irrt man sich ... Die vielen anderen Beispiele hier aus dem Forum und von Google sahen jetzt nicht viel anders aus, als dass ich Denken würde,
dass konzeptionell etwas nicht stimmt.
Jedenfalls mit einer kleinen Testanwendung (RTManager - Code im folgenden Post, da sonst zu lang):
kommt es immer wieder zu exceptions.
Immer wenn ich versuche eine kleine TestKomponente zu laden
kommt es zu folgender exceptions:
Es besagt, dass er das CompnentInterface nicht finden kann, was ja eigentlich nicht sein darf, da die KLasse ComponentContainser selber dieses Interface einbindet.
Weiter fällt auf, dass die Exception component/ComponentInterface anstelle von component.ComponentInterface schreibt ?!?!
Ich weiss nicht mehr weiter ...
Wenn Jemand bitte sich dass mal anschauen würde und mir mal einen Wink mit dem Zaun geben würde wo es hakt ...
Zum Testen fogendens machen:
Java-Bibliothek erstellen mit dem ComponentInterface
Java Bibliothek erstellen mit den Klassen RunTime und ComponentContainer (nicht vergessen die fertige libComponent einbinden).
Komponente (Java Bibliothek) HelloWorldComponent erstellen (auch hier die libComponent einbinden)
Die Gui Anwendung RTManager mit den Klassen RTManager und CListModel erstellen und die libRuntime einbinden.
Beim Start der RTManager-Anwendung auf load klicken und im FileChooser das Jar-File der HelloWorldComponent raussuchen und sich über die Exception freuen.
Vielen Dank
Angus
mal wieder das leidige Thema und Probleme mit dem URLClassloader.
Könnte bitte jemand auf den code schauen und den/die Fehler finden? danke
Problemstellung:
Für einen FH-Kurs soll jede Gruppe ein Komponentenmodel ala OSGI und Konsorten entwickelt und programmiert werden. Natürlich nicht der komplette Funktionsumfang nur ein proof of concept.
Dabei soll es eine RuntimeEnviroment geben, die Komponenten auf wunsch des Users laden, entladen, starten, stoppen, ... etc. kann.
Unser Entwurf sieht so aus:
2 Java Bibliotheken: libComponent, libRuntime
Die libComponent beinhaltet nur ein Interface ComponentInterface.
Der User muss nur um eine Komponente zu erstellen die libComponent einbinden und seine Komponentenklasse muss das Interface implementieren.
Die Runtime baut auf der libCompnent auf und beinhaltet 2 Klassen zum verwalten der Komponenten (Runtime und ComponentContainer). Die Runtime-Klasse besitzt ein public Interface damit eine Anwendung, die die libRuntime einbindet diese steuern kann.
ComponentInterface:
Java:
package component;
public interface ComponentInterface {
public String getStatus();
public String getDescription();
public String[] getDependencies();
public String[] provides();
public boolean start();
public boolean stop();
}
nun die Klasse Runtime:
Java:
package runtime;
import component.ComponentInterface;
import java.util.List;
public class RunTime{
private ComponentContainer cc;
// singleton pattern
private RunTime(){
cc = new ComponentContainer();
}
private static RunTime rt = new RunTime();
public static RunTime getRuntime(){
return rt;
}
public List<String> loadComponent(String compPath) throws Exception{
return cc.loadClass(compPath);
}
public void startComponent(String compName)throws Exception{
cc.startComponent(compName);
}
public void stopComponent(String compName)throws Exception{
cc.stopComponent(compName);
}
public String unloadComponent(String compName) throws Exception{
return cc.unloadComponent(compName);
}
private void unloadDependencies(){
}
public boolean isComponentRunning(String compName){
return cc.isRunning(compName);
}
public ComponentInterface getComponent(String compName) {
return cc.getComponent(compName);
}
public Object getInterface(String interfaceName) {
return cc.getInterface(interfaceName);
}
public String getDescription(String compName) {
return cc.getComponent(compName).getDescription();
}
public String getStatus(String compName) {
return cc.getStatus(compName);
}
public String getStatus() {
return cc.getStatus();
}
}
Die CopmponentContainer-Klasse (wo die eigentliche Arbeit verrichtet wird -- Und wo es die Probleme hagelt)
Java:
package runtime;
import component.ComponentInterface;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
class ComponentContainer {
private static enum status {
deployed, started, stopped
};
// speichert Instanzen geladener Komponenten
private Map<String, ComponentInterface> compMap;
// speichert den Status zu den geladenen Komponenten
private Map<String, status> compStatus;
// brauche ich später für den Classloader
private static final Class[] parameters = new Class[]{URL.class};
// soll später verwendung finden, damit man weiss welche Komponenten welche Funktionalität bereitstellen
private Map<String, Object> compByInterface;
ComponentContainer() {
compMap = new HashMap<String, ComponentInterface>();
compStatus = new HashMap<String, status>();
compByInterface = new HashMap<String, Object>();
}
void startComponent(String compName) {
if (isLoaded(compName) && !isRunning(compName)) {
compStatus.put(compName, status.started);
compMap.get(compName).start();
}
}
void stopComponent(String compName) {
if (isLoaded(compName)) {
compMap.get(compName).stop();
compStatus.put(compName, status.stopped);
}
}
boolean isLoaded(String compName) {
if (compMap.containsKey(compName)) {
return true;
}
return false;
}
boolean isRunning(String compName) {
if (compStatus.get(compName).equals(status.started)) {
return true;
}
return false;
}
String getStatus(String compName) {
return compStatus.get(compName).toString();
}
public String getStatus() {
String retVal = "";
retVal += "Loaded Components: " + String.valueOf(compMap.size()) + "\n";
return retVal;
}
String unloadComponent(String compName) {
if (isLoaded(compName)) {
if (isRunning(compName)) {
stopComponent(compName);
}
compMap.remove(compName);
compStatus.remove(compName);
return compName;
}
return null;
}
ComponentInterface getComponent(String compName) {
if (isLoaded(compName)) {
return compMap.get(compName);
}
return null;
}
Object getInterface(String intName) {
if (compByInterface.containsKey(intName)) {
return compByInterface.get(intName);
}
return null;
}
// hier fängt der Aerger an!!!
List<String> loadClass(String compPath) throws Exception {
File jar = new File(compPath);
if (!(jar.isFile() && jar.getName().endsWith(".jar"))) {
return null;
}
boolean isComp = false;
// hole mit der hilfsmethode Alle klassen aus der jar
List<String> classNames = getClassNames(jar.getAbsolutePath());
List<String> compNames = new ArrayList<String>();
// iteriere über alle KLasse
for (String className : classNames) {
// Remove the ".class" at the back
String name = className.substring(0, className.length() - 6);
// System.out.println(name);
// Lade die klassen FEHLER IN DIESER METHODE
Class clazz = getClass(jar, name);
// Hole alle interfaces der klasse
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces) {
// Implement the ComponentInterface
// Wenn die Klasse das KomponentenInterface implementiert speicher eine Instanz der Klasse
if (c.getName().equals("component.ComponentInterface")) {
compMap.put(clazz.getName(), ((ComponentInterface) clazz.newInstance()));
isComp = true;
}
}
// when it is a component then add it's interfaces to the compByInterfaceMap
if (isComp) {
compNames.add(name);
for (Class c : interfaces) {
// get the interface of the class
if (!c.getName().equals("component.ComponentInterface")) {
compByInterface.put(c.getName(), c.cast(clazz));
}
}
}
}
// gebe der Aufrufenden Methode eine Liste der KLassennamen zurück
// damit z.b. in einer GUI-Anwendung diese in eine Liste gepackt werden können
return compNames;
}
private List<String> getClassNames(String jarName) throws IOException {
ArrayList<String> classes = new ArrayList<String>(10);
JarInputStream jarFile = new JarInputStream(new FileInputStream(jarName));
JarEntry jarEntry;
while (true) {
jarEntry = jarFile.getNextJarEntry();
if (jarEntry == null) {
break;
}
if (jarEntry.getName().endsWith(".class")) {
//System.out.println(jarEntry.getName());
classes.add(jarEntry.getName().replaceAll("/", "\\."));
}
}
return classes;
}
// Diese Methode funzt einfach nicht ...hilfe
private Class getClass(File file, String name) throws Exception {
// addURL(file.toURI().toURL());
URLClassLoader clazzLoader;
Class clazz;
String filePath = file.getAbsolutePath();
//filePath = "jar:file://" + filePath + "!/";
//URL url = new File(filePath).toURI().toURL();
URL url = new URL("jar", "", "file:" + filePath + "!/");
clazzLoader = new URLClassLoader(new URL[]{url}, this.getClass().getClassLoader());
clazzLoader.loadClass(ComponentInterface.class.getName());
// Class.forName(name, false, clazzLoader);
clazz = clazzLoader.loadClass(name); // <---- throws exceptions like lucky luke
return clazz;
}
// Methode um eine Jar (eine URL) zum systemklassenpfad hinzuzufügen
// wird nciht verwendet (hilft auch nicht), da sonst eine klasse nicht mehr "vergessen" wird
private void addURL(URL u) throws IOException {
URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
URL urls[] = sysLoader.getURLs();
for (int i = 0; i < urls.length; i++) {
if (urls[i].toString().equalsIgnoreCase(u.toString())) {
return;
}
}
Class sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod("addURL", parameters);
method.setAccessible(true);
method.invoke(sysLoader, new Object[]{u});
} catch (Throwable t) {
throw new IOException("Error, could not add URL to system classloader");
}
}
}
Den Code zum laden der Klassen aus einer Jar stammt nicht von mir, google sei dank ... Dachte ihn genug verstanden zu haben damit er funktioniert.
Tja so irrt man sich ... Die vielen anderen Beispiele hier aus dem Forum und von Google sahen jetzt nicht viel anders aus, als dass ich Denken würde,
dass konzeptionell etwas nicht stimmt.
Jedenfalls mit einer kleinen Testanwendung (RTManager - Code im folgenden Post, da sonst zu lang):
kommt es immer wieder zu exceptions.
Immer wenn ich versuche eine kleine TestKomponente zu laden
Java:
import component.ComponentInterface;
public class HWComp implements ComponentInterface{
public String getStatus() {
throw new UnsupportedOperationException("Not supported yet.");
}
public String getDescription() {
return "a simple hello world example component";
}
public String[] getDependencies() {
return null;
}
public String[] provides() {
return null;
}
public boolean start() {
System.out.println("Hello World");
return true;
}
public boolean stop() {
System.out.println("component stoppped");
return true;
}
}
Java:
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: component/ComponentInterface
at runtime.ComponentContainer.getClass(ComponentContainer.java:197)
at runtime.ComponentContainer.loadClass(ComponentContainer.java:137)
at runtime.RunTime.loadComponent(RunTime.java:35)
...
Es besagt, dass er das CompnentInterface nicht finden kann, was ja eigentlich nicht sein darf, da die KLasse ComponentContainser selber dieses Interface einbindet.
Weiter fällt auf, dass die Exception component/ComponentInterface anstelle von component.ComponentInterface schreibt ?!?!
Ich weiss nicht mehr weiter ...
Wenn Jemand bitte sich dass mal anschauen würde und mir mal einen Wink mit dem Zaun geben würde wo es hakt ...
Zum Testen fogendens machen:
Java-Bibliothek erstellen mit dem ComponentInterface
Java Bibliothek erstellen mit den Klassen RunTime und ComponentContainer (nicht vergessen die fertige libComponent einbinden).
Komponente (Java Bibliothek) HelloWorldComponent erstellen (auch hier die libComponent einbinden)
Die Gui Anwendung RTManager mit den Klassen RTManager und CListModel erstellen und die libRuntime einbinden.
Beim Start der RTManager-Anwendung auf load klicken und im FileChooser das Jar-File der HelloWorldComponent raussuchen und sich über die Exception freuen.
Vielen Dank
Angus