Klassen dynamisch aus jar-datei laden

angus

Mitglied
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:
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;
    }
}
kommt es zu folgender exceptions:

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
 
Hier noch der Code für die Gui-Anwendung zum steuern der Runtime:

Java:
package view;

import java.io.File;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.filechooser.FileFilter;
import runtime.RunTime;

public class RTManager extends javax.swing.JFrame {

    /** Creates new form View */
    public RTManager() {

         try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
            Logger.getLogger(RTManager.class.getName()).log(Level.SEVERE, null, ex);
        }

        initComponents();

        componentList.setModel(new CListModel());

        rt = RunTime.getRuntime();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        componentList = new javax.swing.JList();
        unloadButton = new javax.swing.JButton();
        loadButton = new javax.swing.JButton();
        startStopButton = new javax.swing.JButton();
        statusButton = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        statusArea = new javax.swing.JTextArea();
        jButton1 = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        componentList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jScrollPane1.setViewportView(componentList);

        unloadButton.setText("Unload");
        unloadButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                unloadButtonActionPerformed(evt);
            }
        });

        loadButton.setText("Load");
        loadButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                loadButtonActionPerformed(evt);
            }
        });

        startStopButton.setText("Start / Stop");
        startStopButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startStopButtonActionPerformed(evt);
            }
        });

        statusButton.setText("Status");
        statusButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                statusButtonActionPerformed(evt);
            }
        });

        jLabel1.setText("Loaded Components");

        jLabel2.setText("Status");

        statusArea.setColumns(20);
        statusArea.setRows(5);
        jScrollPane2.setViewportView(statusArea);

        jButton1.setText("Description");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 351, Short.MAX_VALUE)
                    .addComponent(jLabel2)
                    .addComponent(jLabel1)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 244, Short.MAX_VALUE)
                        .addGap(18, 18, 18)
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(loadButton)
                            .addComponent(unloadButton)
                            .addComponent(startStopButton)
                            .addComponent(statusButton)
                            .addComponent(jButton1))))
                .addContainerGap())
        );

        jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {jButton1, loadButton, startStopButton, statusButton, unloadButton});

        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 256, Short.MAX_VALUE)
                        .addGap(18, 18, 18)
                        .addComponent(jLabel2))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(loadButton)
                        .addGap(18, 18, 18)
                        .addComponent(unloadButton)
                        .addGap(18, 18, 18)
                        .addComponent(startStopButton)
                        .addGap(18, 18, 18)
                        .addComponent(statusButton)
                        .addGap(18, 18, 18)
                        .addComponent(jButton1)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 185, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>                        

    private void loadButtonActionPerformed(java.awt.event.ActionEvent evt) {                                           
        JFileChooser jfc = new JFileChooser();

        jfc.setFileFilter(new FileFilter() {

            @Override
            public boolean accept(File f) {
                if (f.isDirectory() || f.getName().toLowerCase().endsWith(".jar")) {
                    return true;
                }

                return false;
            }

            @Override
            public String getDescription() {
                return "Ordner oder Jar-Files (*.jar)";
            }
        });


        int retVal = jfc.showOpenDialog(this);

        if (retVal == JFileChooser.APPROVE_OPTION) {

            File f = jfc.getSelectedFile();
            try {
               List<String> compList =  rt.loadComponent(f.getAbsolutePath());

               if(compList != null && !compList.isEmpty())
                   for(String s: compList)
                        ((CListModel)componentList.getModel()).add(s);
            } catch (Exception ex) {
                Logger.getLogger(RTManager.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }                                          

    private void unloadButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             
        try {
           String cName =  rt.unloadComponent(componentList.getSelectedValue().toString());
           ((CListModel)componentList.getModel()).remove(cName);
        } catch (Exception ex) {
            Logger.getLogger(RTManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }                                            

    private void startStopButtonActionPerformed(java.awt.event.ActionEvent evt) {                                                
        
        String cName = componentList.getSelectedValue().toString();       
        
        if(rt.isComponentRunning(cName)){
            try {
                rt.stopComponent(cName);
            } catch (Exception ex) {
                Logger.getLogger(RTManager.class.getName()).log(Level.SEVERE, null, ex);
            }
        }else{
            try {
                rt.stopComponent(cName);
            } catch (Exception ex) {
                Logger.getLogger(RTManager.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }                                               

    private void statusButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             

        String status;
        
        if(componentList.isSelectionEmpty())
            status = rt.getStatus();

        else
            status = rt.getStatus(componentList.getSelectedValue().toString());

        statusArea.append(status);
        statusArea.append("\n");
    }                                            

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
        if(componentList.isSelectionEmpty())
            return;

        String des = rt.getDescription(componentList.getSelectedValue().toString());

        statusArea.append(des);
        statusArea.append("\n");
    }                                        

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new RTManager().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify                     
    private javax.swing.JList componentList;
    private javax.swing.JButton jButton1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JButton loadButton;
    private javax.swing.JButton startStopButton;
    private javax.swing.JTextArea statusArea;
    private javax.swing.JButton statusButton;
    private javax.swing.JButton unloadButton;
    // End of variables declaration                   
    private RunTime rt;
}

Java:
package view;

import java.util.LinkedList;
import java.util.List;
import javax.swing.AbstractListModel;

public class CListModel extends AbstractListModel {

    private List<Object> data;

    public CListModel() {
        data = new LinkedList<Object>();
    }

    public int getSize() {
        return data.size();
    }

    public Object getElementAt(int index) {
        return data.get(index);
    }

    public void add(Object k) {
        data.add(k);
    }

    public void set(Object k, int index) {
        if (index < getSize()) {
            data.set(index, k);
        }
    }

    public void remove(int index) {
        if (index < getSize()) {
            data.remove(index);
        }
    }

    public void remove(Object k) {
        data.remove(k);
    }
}
 
Wie weit läuft Dein Tool denn? Bei mir funktioniert zumindest das Laden. Auch kein Problem mit
Code:
clazz = clazzLoader.loadClass(name); // <---- throws exceptions like lucky luke

Code:
java -version
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) Server VM (build 16.3-b01, mixed mode)

P.s.: Füge mal in CListModel.add()
Code:
fireContentsChanged(this, 0, data.size());
ein.
P.p.s.: JCL - Jar Class Loader
 
Zuletzt bearbeitet:
Hallo!
Also ich starte das Programm, klicke auf laden
und wähle mit dem Filechooser die zu ladene Jar aus.
Dann raised die im 1. Post beschriebene Exception.

Meine Kollegen konnten die auch reproduzieren ... (mac java)
Natürlich der Vorführeffekt, dass das Laden der Komponente bei Dir klappt :-(

Meine JVM/JDK:
Code:
java -version
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode)

Danke für den fix mit der Liste ... im Eifer des Gefechts übersehen.
Die JCL-Bibliothek schau im mir mal an, Danke!
Leider können wir die für die gegeben Aufgabe wohl nicht benutzen.

Wenn Dir an dem Code nochwas auffallen sollte immer nur raus damit 🙂

Viele Grüße
Angus
 
Mir scheint, daß der URLClassLoader keinen Fehler wirft, wenn man ihm eine URL gibt, die er nicht finden kann. Vielleicht liegt es nur daran. Instantiiert mal einen URLClassLoader mit der JAR Url, ruft darauf getResourceAsStream("HWComp.class") auf und versucht die Klassenbytes zu lesen.
 
Wieder mal JAVA und Plug-In's @ tutorials.de: Tutorials, Forum & Hilfe

da hab ich das thema schon mal lang und breit auseinander genommen ...
es ist zwar nicht vollständig das nach dem du suchst aber es funktioniert ohne problem

ACHTUNG

was dort noch nicht steht *weil es erst später dazu kam* ist das ENTLADEN von plug-in's ...
sämtliche klassen die von ClassLoader abgeleitet sind haben das problem das scheinbar referrenzlose und entladene JAR-files weiterhin gelockt bleiben
seit Java7 hat URLClassLoader die methode close()
für frühere versionen als 7 musst du einen CustomURLClassLoader ableiten und dort die das Closeable interface selbst implementieren ...
weitere infos dazu hier

Applikation updaten @ tutorials.de: Tutorials, Forum & Hilfe

ebenfalls ein von mir losgetrender thread in dem das "hacken" des URLClassLoaders beschrieben ist

ich denke diese beiden threads sollte dir genug infos zu deinem problem liefern

ich selber habe mitlerweile mein voll funktionsfähiges Pluginsystem
es bietet neben der von dir gewünscht HOT-PLUG-fähigkeit *plug-in's wieder vollständig entladen* auch noch weitere möglichkeiten die aber spezifisch für meine verwendung sind und nicht in ein allgemeines plug-in-system gehören

ich kann dir gerne das gesamte system zur verfügung stellen wenn du möchtest da ich wie du an den threads und der zeitspanne erkennen kannst LANGE meine probleme damit hatte ...
sowas kann man halt nicht in 2 wochen programmieren ... dafür braucht man schon einige zeit
 

Zurück
Oben