Klassen aus einem Package ermitteln und per Laufzeit laden

Christopher25

Bekanntes Mitglied
Hallo zusammen. Ich habe keine Frage an euch.
Ich habe hier eine Methode geschrieben, welche mir während der Laufzeit
zu einem bestimmten Package die Klassen als Class-Objekt ermittelt um dann
die classen einer Liste innerhalb eines Inviroments-Objektes hinzuzufügen.
Hier der bisherige Code:

Java:
public void registerMonster(String packageName, Enviroments env) {
        ArrayList<File> directories = new ArrayList<File>();
        try {
            ClassLoader cld = Thread.currentThread().getContextClassLoader();
            if (cld == null) {
                throw new ClassNotFoundException("Can't get class loader.");
            }
            Package pack = Package.getPackage(packageName);
            if (pack == null) return;
            packageName = pack.getName();
            String path = packageName.replace('.', '/');
            // Ask for all resources for the path
            Enumeration<URL> resources = cld.getResources(path);
            
            while (resources.hasMoreElements()) {
                directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
            }
	        List<Class<?>> classes = new ArrayList<Class<?>>();
	        // For every directory identified capture all the .class files
	        for (File directory : directories) {
	            if (directory.exists()) {
	                // Get the list of the files contained in the package
	                String[] files = directory.list();
	                for (String file : files) {
	                    // we are only interested in .class files
	                    if (file.endsWith(".class")) {
	                        // removes the .class extension
	                            classes.add(Class.forName(packageName + '.'
	                                    + file.substring(0, file.length() - 6)));
	                    }
	                }
	            } else {
	                throw new ClassNotFoundException(packageName + " ("
	                        + directory.getPath()
	                        + ") does not appear to be a valid package");
	            }
	        }
	        for (Class m : classes) {
	        	 env.getMonsters().add(m);
	        }
        } catch (Exception e) {
			e.printStackTrace();
		}
	}

Der funktioniert in Eclipse super. Ich gebe dort die Package namen an und der lädt mir alle
Klassen innerhalb eines Package rein.
Wenn ich jedoch eine ausführbare .jar Datei erzeuge, kann er anscheinend nichts mehr richtig ermittelnt. Ich sehe nur die Auswirkungen, dass keine Monster mehr angezeigt werden,
wenn ich ein Enviroment betrete. Woran kann das liegen und wie kann ich das beheben?
 
Zuletzt bearbeitet von einem Moderator:
Das liegt vermutlich daran, dass ein JAR ZIP komprimiert ist. Du müsstest also irgendwie hinter den ZIP kommen. Z. B. mit einem ZipInputStream.

env.getMonsters().add(Class) ist ein etwas unglückliches Konstrukt. Implementiere lieber env.addMonsters()
 
File kann nur auf Dateien und Ordner zeigen, auch wenn sie (noch) nicht vorhanden sind. ZIP komprimierte Ordner werden aber als eine Datei behandelt.
Der ClassLoader kann zwar aus ZIP Dateien Klassen laden, allerdings kann File eben nicht darauf zeigen. Deshalb müsstest du anderweitig zu den geforderten Klassen navigieren. Vielleicht hilft dir diese Seite dabei weiter.
 
Ich habe das mit dem ZipInputStream versucht, jedoch bekomm ich einen "ZugriffVerweigert" - Error,
weil dich die jar ja in Benutzung vom Betriebssystems steckt und deshalb nicht eingelesen werden kann. (jedenfalls meine Erlärung)
 
Ja das wäre auch meine Erklärung.

Es liegt an der Klasse File. Da du die nicht verwenden kannst und der ZipInputStream anscheinend auch nicht funktioniert, kannst du nur noch versuchen mit Boardmitteln des ClassLoaders etwas zu werden oder du lagerst die zu ladenden Klassen in ein anderes JAR aus.
 
Habs jetzt mal versucht die Klassen einfach in einem Nebenordner zu extrahieren und von dort aus zu laden.
Aber bekomm das irgendwie nicht hin. Der Classloader kann die irgendwie nicht erkennen.
Hat denn niemand sowas mal versucht Dynamisch alle Klassen aus einem Package zu laden??
 
Die Idee wäre durch alle Einträge in allen jars zu iterieren die, sich in dem Pfad
Java:
   File path = new File(System.getProperty("java.home"), "lib");
und alle Bibliotheken, die sich auf dem ClassPath befinden (da bin ich mir nicht mehr sicher, wie man an die kommt, ist aber auch leichter als gedacht) zu iterieren.

Das Iterieren durch die Einträge der jars geht mit der Klasse JarFile ganz einfach!

Der Name der Einträge haben z.B. die Form "java/util/ArrayList.class", welche du dann mit Stringmanipulationen mit deinem gesuchten Package vergleichen kannst.

Um die Klassen dann zu laden müsstest du dir evtl einen eigenen ClassLoader schreiben, der mit Hilfe eines InputStreams und einem kompletten Klassennamen (z.B. "java.util.ArrayList") eine Klasse definiert.
Den Stream bekommst du von den Einträgen des JarFiles.

mfg keddelzz
 
Es könnte auch Klappen mit
Java:
   Class.forName(<klassenname>);
dazu müssen die Klassen auf dem ClassPath liegen.

Nur wenn sie das nicht tun, musst du sie mit einem ClassLoader nachladen.
 
Willst du nur selbstgeschriebene Klassen mit deiner Methode bekommen, oder auch Bibliotheksklassen?

Bei selbstgeschriebenen Klassen würde
Java:
    StringTokenizer cps = new StringTokenizer(System.getProperty("java.class.path"), ";");
    while (cps.hasMoreElements()) {
     	    String classPath = cps.nextToken();
            System.out.println(classPath);
    }
alle bekannten ClassPaths ausgeben. - Da sollte der Pfad deiner exportierten Jar dabei sein.
Die könntest du wie bereits beschrieben mit Hilfe der Klasse JarFile durchsuchen.

mfg keddelzz
 
Also ich bekomme das echt nicht hin.
Ich habe die Klassen als eigenständige Jar exportiert und lese die ein.
Aber der SystemClassLoader sagt ständig, dass er diese Klasse nicht finden kann
und gibt eine ClassNotFoundException aus.
 
Also ich bekomme das echt nicht hin.
Ich habe die Klassen als eigenständige Jar exportiert und lese die ein.
Aber der SystemClassLoader sagt ständig, dass er diese Klasse nicht finden kann
und gibt eine ClassNotFoundException aus.

PS: Bei diesem Tokenizer bekomm ich das JRE7 Verzeichnis in meinem Windows Verzeichnis raus.
Das ist Correkt so?? Wüsste nicht was das bringen soll, da die Jar doch nicht dort liegt.
 
Die Tokenizer-Methode funktioniert nur, wenn die jar-Datei "richtig" aufgerufen wird.
Mit dem Aufruf
Code:
java -jar <jardatei.jar>
kennt Java den ClassPath nicht.
stattdessen solltest du die jar mit
Code:
java -cp "<jardatei.jar>" package.MainClass
ausführen. So taucht dann auch der Pfad zu deiner Jar-Datei auf, anstatt nur der zu dem Pfad der JRE Installation.

Ich glaube, wenn Java deine Klassen kennt (wenn sie auf dem ClassPath liegen), dann solltest du mit
Java:
Class.forName(...);
die Klasse laden sollen.

Ansonsten müsstest du einen eigenen ClassLoader schreiben.

mfg keddelzz
 
Zuletzt bearbeitet:
So hallo zusammen.

Ich habe jetzt einige Zeit wenig gemacht aber mich heute wieder dran gesetzt bin nochmal alles durch gegangen und zu einem Ergebnis gekommen.
Eure Ansätze haben mir gut geholfen, besonders mit diesem JarFile etc. Ich poste hier nun mein Ergebnis und hoffe ich kann damit jemanden behilflich sein.
Java:
       /**
	 * Registriert die Monster in dem Package an das Enviroment
	 * @param packageName
	 */
	@SuppressWarnings("unchecked")
	public void registerMonster(String packageName, Enviroments env) {
		try {
			List<Class<?>> clazzez = getClasses(packageName);
			for (Class clazz : clazzez) {
				env.addMonsters(clazz);
			}
		} catch (ClassNotFoundException | IOException | URISyntaxException e) {
			JOptionPane.showMessageDialog(new JFrame(), "Spiel konnte wichtige Ressourcen nicht finden! \n" + e.getMessage(), "Fehlende Ressourcen", JOptionPane.OK_OPTION);
			System.exit(-1);
		}
	}
	
	@SuppressWarnings("static-access")
    public static List<Class<?>> getClasses(String packageName) throws IOException, URISyntaxException, ClassNotFoundException {
        List<String> names = getClassNamesFromPackage(packageName);
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for (String name : names) {
            classes.add(GameController.class.forName(name));
        }
        
        return classes;
    }
	
	private static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException, URISyntaxException{
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL packageURL;
        ArrayList<String> names = new ArrayList<String>();;

        packageName = packageName.replace(".", "/");
        packageURL = classLoader.getResource(packageName);

        if(packageURL.getProtocol().equals("jar")){
            String jarFileName;
            JarFile jf ;
            Enumeration<JarEntry> jarEntries;
            String entryName;
            
            jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
            jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
            jf = new JarFile(jarFileName);
            jarEntries = jf.entries();
            while(jarEntries.hasMoreElements()){
                entryName = jarEntries.nextElement().getName();
                if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
                    entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
                    String[] s = entryName.split("/");
                    names.add(packageName +s[0] + "."+ s[1]);
                }
            }
        } else{
        URI uri = new URI(packageURL.toString());
        File folder = new File(uri.getPath());
            File[] contenuti = folder.listFiles();
            String entryName;
            for(File actual: contenuti){
                entryName = actual.getName();
                entryName = entryName.substring(0, entryName.lastIndexOf('.'));
                names.add(packageName + "." + entryName);
            }
        }
        return names;
    }
 

Neue Themen


Zurück
Oben