Klassen aus Verzeichnis laden

  • Themenstarter Themenstarter Guest
  • Beginndatum Beginndatum
Status
Nicht offen für weitere Antworten.
G

Guest

Gast
Hallo,

ich habe folgende Aufgabenstellung:
Zur Laufzeit generiere ich eine Java Klasse, die ich anschließend compiliere und mithilfe eines Tools Stub Klassen daraus erzeuge. Diese Stub Klassen werden dynamisch erzeugt und in einem Verzeichnis abgelegt. Diese Stub Klassen werden dann anschließend benötigt um die selbst generierte Klasse zu nutzen.

Das Problem ist, dass ich zwar die einzelnen Klassen aus dem Verzeichniss laden kann aber sie dann anschließend trotzdem nicht gefunden werden. Mithilfe von einem URLClassLoader kann ich zwar selber Instanzen der Klassen erstellen aber sie werden eben nicht von anderen Klassen gefunden, die per Class.forName nach ihnen suchen.

Frage: Wie kann ich die Klassen aus dem Verzeichnis so laden, dass ich, wenn ich mit Class.forName("Classenname"); nach ihnen suche sie auch finde?

Code:
try
            //load classes
                       
            URL url = new File(path).toURI().toURL();
            URLClassLoader cl = new URLClassLoader(new URL[] { url });

            //get names from auto generated classes
            Stack<File> dirs = new Stack <File>();
            List<String> filesName = new ArrayList<String>(256);
            File classDir = new File (path+"/jaxws");
            Pattern p = Pattern.compile("(.*\\.class$)",Pattern.CASE_INSENSITIVE);
            if (classDir.isDirectory()&&classDir.exists()){
                dirs.push(classDir);
            }else{
                throw new IOException("Error while loading auto generated classes");
            }
            while(dirs.size()>0){
                for (File file : dirs.pop().listFiles()){
                    if(p.matcher(file.getName()).matches()){
                        filesName.add(file.getName());
                        //System.out.println("Name of the file to Load: " + file.getName());
                    }                  
                }
            }
             
            //load auto generated classes
            Iterator<String> i = filesName.iterator();
            while (i.hasNext()){
                String className = i.next();
                //remove .class form name
                className = className.substring(0, className.length()-6);
                //add package to name
                className = "jaxws." + className;
                System.out.println("Load " + className);
                cl.loadClass(className);
            }
                      
            //TEST: load Class Copy_to_cache
            Class<?> testClass = Class.forName("jaxws.Copy_to_cache");
            testClass.newInstance();
            System.out.println(testClass.toString());

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

(Das ganze findet im Rahmen von WebServices statt. Die Methoden eines WebServices sollen mithilfe einer XML Datei beschrieben werden. Aus dieser generiere ich eine .java Datei, die compilirt und anschließend darauf das wsgen Tool verwendet wird. Dann will ich mit dieser Service Klasse einen Endpoint publishen aber der benötigt eben die von wsgen generierten Stub Klassen, die zuerst geladen werden müssen)
 
Die anderen Klassen müssen von einem Classloader geladen werden in dessen Hierarchie auch die generierten Klassen gefunden werden können.
 
meddins hat gesagt.:
Was meinst du genau mit die anderen Klassen?
Die statischen Klassen, die Zugriff auf die dynamischen benötigen.
Üblicherweise sind Classloader in einer Baumstruktur angelegt. Eine Klasse fragt nach einer anderen Klasse an, dann geht es im Baum erst zurück zur Wurzel, wenn dort nichts gefunden wird eine Ebene tiefer, wieder eine Tiefer, bis man wieder im Ursprungsblatt ist. Hat in dieser Kette kein Classloader die Klasse gefunden, wird nicht weitergesucht.
Die Klassen die Zugriff auf die dynamischen Klassen benötigen, müssen also von einem Classloader geladen werden, der die dynamischen finden kann.
 
Wird bei Class.forName nicht immer der primordial ClassLoader verwendet, egal von wo aus der Aufruf passiert?
 
Hmm, das hilft mir noch nicht weiter. Die Klassen, die die dynamisch zu ladenden Klassen nutzen werden ja automatisch von dem jvm geladen.
 
Du kannst durchaus deine eigenen Classloader zwischenschalten. Bei OSGi und Application Servern wird ähnlich Verfahren. Ganz trivial ist die Sache allerdings zugegebenermaßen nicht.
 
Gast hat gesagt.:
Hmm, das hilft mir noch nicht weiter. Die Klassen, die die dynamisch zu ladenden Klassen nutzen werden ja automatisch von dem jvm geladen.

Nimm einen URLClassLoader, der deine "dynamischen" Klassen lädt. Als Parent nimmst du den ContextClassLoader. Dieser Ansatz ist relativ straight-forward.

Wichtig: die Mengen der Ressourcen, die über die verschiedenen ClassLoader geladen werden müssen disjunkt sein! Das geht reicht einfach, indem der Klassenpfad beim Start nicht die Teile enthält, wo dynamische Ressourcen landen. Damit konfigurierst du dann den URLClassLoader.
 
also gibt es keine "einfache" Möglichkeit die Klassen zur Laufzeit so zu laden, dass sie mit hilfe von Class.forName() gefunden werden?

Edit: hab den letzten Post nicht gesehen, wie genau geht das? Gibt es dazu irgendwo ein kleines Code Beispiel?
der Konstruktor des URL Klassenladers sieht jetzt so aus:
Code:
URLClassLoader cl = new URLClassLoader(new URL[] { url }, Thread.currentThread().getContextClassLoader());
 
ja, ich kenne Google. Habe auch gesucht aber nicht zu meinem Problem gefunden. Ich habe es ja geschafft eine Klasse zu laden und per reflection zu nutzen aber die Klassen, die dynamisch geladen werden müssen werden eben nicht gefunden. Der Classloader hat sie, das sieht man im Debug Modus aber von außerhalb kann man nicht auf sie zugreifen und ich probiere da schon ziemlich lange dran rum. Die tutorials die ich im Internet gefunden habe nutzen die geladenen Klassen immer direkt mit Hilfe von Reflection.
Ich wende mich nicht ohne Grund an die Community in diesem Forum, da ich zur Zeit leider echt am ende meines Latains bin.
 
Es ist gut sich an den Quasi-Standard zu halten: nutze den ContextClassLoader zum laden der Klassen Class.forName oder getClass().getClassloader() ist nicht optimal.

Erstelle einen neuen URLClassLoader mit den neuen Klassenpfaden und setze den Parent auf den ContextClassLoader. Setze deinen Classloader als ContextClassLoader.

Von jetzt an werden die Klassen über deinen ClassLoader geladen: erst wird (das ist der Standard) in den Parentes gesucht, dann in deinem Classloader.

Das sollte genügen.
 
Vielen Dank, das setzten von dem URLClassLoader als ContextClassLoader hat das Problem gelöst 🙂. Ich hab da ziemlich lang dran gekaut.
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben