Zum Inhalt springen

File.class in Programm laden als Class<?>


Sporticus

Empfohlene Beiträge

Hi Community,

ich bin noch ganz neu hier und habe mich zu meinem momentanem Problem schon mal ein wenig umgesehen, allerdings keine Lösung gefunden (weder hier noch bei Google).

Ich programmiere mir momentan ein Programm, welches anhand von Java-Klassen ein Klassen-UML-Diagramm erstellen soll. Das erstellen und pipapo funktioniert einwandfrei.

Allerdings steh ich noch vor dem Rätsel, wie ich eine class aus einem x beliebigen Projekt/Ordner/etc. laden kann und in meinem Programm verwenden kann. (Ich kann das Programm nur auf Klassen aus dem aktuellen Projekt laufen lassen)

Bisher hab ich es probiert, die x beliebige Klasse über den ClassLoader zu laden, allerdings hab ich festgestellt, dass dieser nur Klassen aus dem aktuellen Projekt laden kann!

Ich würde mich über ein paar Anregungen, Vorschläge, etc. sehr freuen.

Danke :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi flashpixx,

danke ersteinmal für deine schnelle Antwort. Die Seite, die du mir gezeigt hast, hab ich auch schon einmal gefunden, allerdings dabei nur überflogen.

Ich habe jetzt versucht das, was auf dieser Seite steht, umzusetzen. Mein Code sieht momentan so aus:

public class JarClassLoader extends ClassLoader

{

	/**

	 * @param jarFile - enthält Pfad zur *.class Datei: C:\Beispiel\Beispiel.class 

	 * @param binaryClassName - Format: java.lang.ClassLoader

	 * @return

	 * @throws ClassNotFoundException

	 */

	public Class<?> loadClass(File jarFile, String binaryClassName) throws ClassNotFoundException

	{

		// ---

		// lesen über den ClassLoader

		Class<?> findLoadedClass = findLoadedClass(binaryClassName);

		if(findLoadedClass != null) {

			return findLoadedClass;

		}


		// ---

		// lesen über den InputStream

		byte[] data = readClassFromFile(jarFile, binaryClassName);


		if(data == null) {

			throw new ClassNotFoundException(binaryClassName);

		}


		// ---

		// Class definieren

		Class<?> defineClass = defineClass(binaryClassName, data, 0, data.length);


		System.out.println(defineClass.getName());


		return defineClass;

	}


	private byte[] readClassFromFile(File jarFile, String binaryClassName)

	{

		String fileName = jarFile.getAbsolutePath();


		InputStream in = getClass().getClassLoader().getResourceAsStream(fileName);

		if(in != null) {

			try {

				List<Integer> byteList = new LinkedList<Integer>();

				int oneByte = 0;

				while((oneByte = in.read()) != -1) {

					byteList.add(new Integer(oneByte));

				}


				byte[] data = new byte[byteList.size()];


				for(int index = 0; index < byteList.size(); index++) {

					data[index] = byteList.get(index).byteValue();

				}


				return data;

			} catch(IOException e) {

				e.printStackTrace();

			} finally {

				IOUtils.closeQuietly(in);

			}

		}

		return null;

	}

}

Allerdings bleibt die ClassNotFoundException erhalten :(

Vielleicht bin ich auch einfach so auf den Code fokusiert, dass ich meinen Fehler einfach übersehe. Aber vielleicht hat einer von euch einen besseren Blick darauf. :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das hab ich als aller erstes schon versucht. Das hat allerdings auch nicht funktioniert.

Meine Vermutung:

Da die Datei.class nicht in dem gleichen Projekt liegt, in dem das Programm ausgeführt wird, muss ich den vollständigen Pfad zu dieser Datei.class angeben.

Was ich gerade noch ausprobiert habe:

InputStream in = null;

		try {

			in = new FileInputStream(jarFile); // File über den FileInputStream einlesen?!


			if(in != null) {

				List<Integer> byteList = new LinkedList<Integer>();

				int oneByte = 0;

				while((oneByte = in.read()) != -1) {

					byteList.add(new Integer(oneByte));

				}


				byte[] data = new byte[byteList.size()];


				for(int index = 0; index < byteList.size(); index++) {

					data[index] = byteList.get(index).byteValue();

				}


                                Class<?> defineClass = defineClass(StringUtils.removeEnd(jarFile.getName(), ".class"), data, 0, data.length);

			}

		} catch(IOException e) {

			e.printStackTrace();

		} finally {

			IOUtils.closeQuietly(in);

		}
Danach bekomm ich dann eine NoClassDefFoundError...
Exception in thread "main" java.lang.NoClassDefFoundError: Test (wrong name: de/test/Test)

	at java.lang.ClassLoader.defineClass1(Native Method)

	at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)

	at java.lang.ClassLoader.defineClass(ClassLoader.java:616)

	at java.lang.ClassLoader.defineClass(ClassLoader.java:466)

        ...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das hab ich als aller erstes schon versucht. Das hat allerdings auch nicht funktioniert.

Meine Vermutung:

Da die Datei.class nicht in dem gleichen Projekt liegt, in dem das Programm ausgeführt wird, muss ich den vollständigen Pfad zu dieser Datei.class angeben.

Eine Klasse wird nur über den Namen (und ggf. Packagenamen) und nicht den Dateinamen geladen. Folgende beiden Codes funktionieren:


class run {


    public static void main(String[] args) {

        try {

            Class.forName("load");

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

und die entsprechende load-Klasse:

class load {

    int x = 5;

}

Sofern die load-Klasse im Classpath liegt, wird sie auch geladen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Also ich hab mir jetzt nochmal ein paar Seiten im Internet zum Thema "File.class während der Laufzeit zum Classpath hinzufügen" angesehen. Soweit ich das aus meiner jetzigen Sicht sagen kann, ist es nur möglich eine File.jar während der Laufzeit zum Classpath hinzuzufügen.

Dieses Beispiel habe ich gefunden, um ein Jar-File in den Classpath mit hinzuzufügen (Website):


	private static final Class<?>[] parameters = new Class[] { URL.class };


	public static void addFile(String s) throws IOException

	{

		File f = new File(s);

		addFile(f);

	}


	public static void addFile(File f) throws IOException

	{

		addURL(f.toURI().toURL());

	}


	public static void addURL(URL u) throws IOException

	{

		URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();

		Class<?> sysclass = URLClassLoader.class;

		try {

			Method method = sysclass.getDeclaredMethod("addURL", parameters);

			method.setAccessible(true);

			method.invoke(sysloader, new Object[] { u });

		} catch(Throwable t) {

			t.printStackTrace();

			throw new IOException("Error, could not add URL to system classloader");

		}

	}
Ein anderer Lösungsansatz, um ein Jar-File zu laden: (Website)
/**

   * @param args

   */

  public static void main(String[] args) {

    try {

      Class.forName("com.google.inject.Injector");

      System.out.println("Found");

    } catch (ClassNotFoundException e) {

     System.out.println(e.getMessage() + " not found...");

    }

    addJarsToClassPath(Thread.currentThread().getContextClassLoader(), new File[]{new File("D:/stuff/google/guice/1.0/guice-1.0.jar")});

    try {

      Class.forName("com.google.inject.Injector");

      System.out.println("Found");

    } catch (ClassNotFoundException e) {

      System.out.println(e.getMessage() + " not found...");

    }

  }



  private static void addJarsToClassPath(ClassLoader classLoader, File[] jars) {

    if (classLoader instanceof URLClassLoader) {

      try {

        Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });

        addUrlMethod.setAccessible(true);

        if (null != addUrlMethod) {

          for (File jar : jars) {

            try {

              addUrlMethod.invoke(classLoader, jar.toURI().toURL());

            } catch (Exception e) {

              e.printStackTrace();

            }

          }

        }

      } catch (Exception e) {

        e.printStackTrace();

      }


    }

  }

In meinem Fall hat keines der beiden Beispiele funktioniert. :(

Sofern die load-Klasse im Classpath liegt, wird sie auch geladen.

Wenn ich eine ClassNotFoundException bekomme, vermute ich mal, dass die Klasse dann nicht im Classpath liegt. Kann ich diese Klasse denn dann nachträglich noch in den Classpath mit aufnehmen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Mir ist gerade aufgefallen, dass ich im prinzip genau die Funktion brauche, die Eclipse bei der Suche nach einem String (z.B.: enthalten in Methoden, Fields, etc.).

Ist nur die Frage, ob bei der Suchefunktion von Eclipse die Datei geparsed wird, oder via den ClassLoader o.ä. geladen wird?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn ich eine ClassNotFoundException bekomme, vermute ich mal, dass die Klasse dann nicht im Classpath liegt.

Ja.

Kann ich diese Klasse denn dann nachträglich noch in den Classpath mit aufnehmen?

Soweit ich das im Kopf habe, geht das nicht, dass man dynamisch den Classpath erweitern kann. Zur Laufzeit ist meines Wissens keine Änderung möglich.

Ein Jar ist kein Dateistream im eigentlichen Sinne. Ein Jar ist ein Zip-Archiv, wobei man eben dort eine ganze Reihe an einzelnen Klassen hat, die aber durch Packages organisiert sein können. D.h. wenn Du die Klassen aus einem Jar einbinden willst, dann musst Du einen eigenen Classloader schreiben und damit ggf den Standard-Classloader ersetzen, denn Wenn z.B. eine Klasse aus dem Jar instantiiert wird und eine Klasse, die ebenfalls innerhalb des Jars liegt, instantiiert würde der Standard-Classloader eine Exception werfen, da er diese weitere Klasse nicht erreichen kann. Das ganze halte ich aber für extrem unhandlich und im Sinne, dass Du eine externe Komponente nutzen willst, auch für nicht sinnvoll.

Bei externen Komponenten wird man via Class.forName, die entsprechende Klasse laden, wobei man davon ausgeht, dass die Klasse/en im Classpath liegen, z.B. bei JDBC verfährt man so. Liegt der JDBC Treiber nicht im Classpath erhält man eine Exception und der User muss dann eben die fehlenden Daten ergänzen

Bearbeitet von flashpixx
Link zu diesem Kommentar
Auf anderen Seiten teilen

Soweit ich das im Kopf habe, geht das nicht, dass man dynamisch den Classpath erweitern kann. Zur Laufzeit ist meines Wissens keine Änderung möglich.

Doch, es ist schon möglich, zur Laufzeit den Classpath zu erweitern. Jeder Application Server mit Hot Deploy macht so etwas. Der OP kann sich z.B. mal das Classloading im JBoss anschauen. Es ist allerdings nicht trivial, so etwas zu ändern. Man sollte schon wissen, was man da macht.

Schöne Grüße,

Peter

Link zu diesem Kommentar
Auf anderen Seiten teilen

Jeder Application Server mit Hot Deploy macht so etwas. Der OP kann sich z.B. mal das Classloading im JBoss anschauen.

Genau das macht das Hot Deplayment doch eben nicht. Beim Hot Deployment existiert ein eigener Classloader, der auch dafür sorge trägt, dass dann Klassen aus unterschiedlichen Pfaden geladen / entladen werden. Beim Entladen läuft dann der GC drüber und entfernt die Objekte aus dem Speicher. Der Classloader koordiniert die Zugriffe auf der Dateiebene und sorgt für das Instantiieren. Formal wird nicht der Classpath erweitert, sondern der Classloader lädt anhand der Struktur die passenden Klassen (ggf. auch mehrfach).

Link zu diesem Kommentar
Auf anderen Seiten teilen

Guten Morgen!

Da hast Du eine andere Definition von Classpath als ich. Für mich ist der Classpath die Summe an Orten, an denen nach Klassen gesucht werden kann. Es gibt einen Bootclasspath, der beim Start des Servers feststeht, und den erweiterten Classpath, der durch dynamisches nachladen aufgebaut wird. Aber wir sind uns ja soweit einig, dass die Anzahl der für das Classloading verfügbaren Klassen während der Laufzeit einer Anwendung durch einen entsprechenden Classloader erweitert werden kann.

Das Abräumen mit dem GC funktioniert übrigens bei geladenen Klassen so nicht. Diese landen in der Permanent Generation und sind dann bei wiederholten Hot Deploys die Ursache für entsprechende OutOfMemoryErrors.

Schöne Grüße,

Peter

Link zu diesem Kommentar
Auf anderen Seiten teilen

Guten Morgen,

gibt es nicht irgendwelche Open Source Projekte/Programme, bei denen sowas gemacht wird? Dann könnte man sich den Code dort einfach mal ansehen und könnte dann sagen, wie es funktioniert.

Ich hab es ja schon einmal angesprochen, dass Eclipse eine Suche verwendet, die auch den Klasseninhalt (Variablen, Methoden) durchsucht und das Ganze im kompletten Workspace. Allerdings könnte es auch sein, dass hier das File einfach geparsed wird.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Da hast Du eine andere Definition von Classpath als ich.

*g* stimmt

Das Abräumen mit dem GC funktioniert übrigens bei geladenen Klassen so nicht. Diese landen in der Permanent Generation und sind dann bei wiederholten Hot Deploys die Ursache für entsprechende OutOfMemoryErrors.

Das stimmt, man muss im Grunde erst mal alle Referenzen auf die Klasse aus dem Speicher entfernen, bevor der GC dann wirklich aufräumen kann. Da der GC letztendlich zyklische via Mark & Sweep / Compact durch alle Objektreferenzen durchgeht, muss man bevor man eine Klasse entfernt eben erst einmal alle Objekte als "not-used" markieren, damit der GC sie entfernen kann. Erst wenn das geschehen ist, kann man die Klasse entladen.

@Topic: Nein Eclipse durchsucht nicht, sondern die Klasse wird während der Codings immer wieder kompiliert, denn z.B. korrekte Einrückung kann nur dadurch korrektes Parsen & Compilieren fest gestellt werden (Blockbildung Dyck-Sprache ist nur mit einer Kontextfreie Grammatik verarbeitbar). Die Klasse wird nicht geparst sondern wirklich instanziiert und dann zur Laufzeit verarbeitet.

Link zu diesem Kommentar
Auf anderen Seiten teilen

[...]

gibt es nicht irgendwelche Open Source Projekte/Programme, bei denen sowas gemacht wird? Dann könnte man sich den Code dort einfach mal ansehen und könnte dann sagen, wie es funktioniert.[...]

Schau Dir einfach den Classloading Mechanismus vom JBoss an. Der ist Open Source und macht so etwas. Du solltest allerdings ein bisschen Grundwissen über Classloading anschauen.

Schöne Grüße,

Peter

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 Wochen später...

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...