PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java: Klasse zu byte[]


SavageX
2007-08-23, 11:57:26
Hallöchen,

ich benötige einen Weg, um von einer Klasse an ein Array von Bytes zu kommen. Dieses Array soll dann per RMI übertragen und auf der anderen Seite an einen ClassLoader verfüttert werden (damit die urprüngliche Klasse dort auch verfügbar ist). Klingt ja eigentlich ganz einfach, ich möchte aber nicht das Dateisystem nach Klassen und JARs durchstöbern.

Ein Versuch:


String name = Beispielklasse.class.getCanonicalName();

InputStream is = Beispielklasse.class.getClassLoader().getResourceAsStream(name);


Zu dumm, dass is danach immer null ist.

Fällt jemandem was Kluges ein?

del_4901
2007-08-23, 12:17:46
Schau dir Serializable an

Monger
2007-08-23, 12:21:16
Schau dir Serializable an

Ja, aber wie willst du denn die Klasse selbst serializable machen? Das Objekt davon - klar, aber die Klasse selbst?

SavageX
2007-08-23, 12:25:35
Ja, also mit Serializable geht das nicht. Hab ja auch mal (mit Bauchschmerzen) versucht, einfach Beispielklasse.class zu serialisieren und auf der anderen Seite wieder zu einem Class-Objekt zusammenzusetzen. Steigt mit einer ClassNotFoundException aus. Anscheinend kapselt Class nur Informationen über die Klasse, nicht aber die Klasse selbst.

del_4901
2007-08-23, 12:26:03
Ja, aber wie willst du denn die Klasse selbst serializable machen? Das Objekt davon - klar, aber die Klasse selbst?
Sag blos, das kann Java ned. ... Das ist ja schwach ^^

del_4901
2007-08-23, 12:28:12
Tja warscheinlich musst du das .class file rüberschicken, und dort irgendwie registrieren ... wenn das denn geht?

SavageX
2007-08-23, 12:35:14
Tja warscheinlich musst du das .class file rüberschicken, und dort irgendwie registrieren ... wenn das denn geht?

Das ist die leichteste Übung. Sobald man die Klassendaten als Datenhaufen rüberwuchtet (üblicherweise als byte[]) hat man gewonnen - schliesslich gibt es ja

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ClassLoader.html#defineClass(java.lang.String,%20byte[],%20int,%20int)

Ich brauche also einfach nur eine Möglichkeit, vom Klassenamen auf ein byte[] zu kommen. Im schlimmsten Fall das Dateisystem durchwühlen - aber das finde ich hässlich. Mir würde ja schon reichen zu wissen, von wo die Klasse geladen wurde - die Datei dann auszulesen ist ja kein Problem.

Monger
2007-08-23, 12:52:29
Sag blos, das kann Java ned. ... Das ist ja schwach ^^

Wie ich grad sehe: jedes Class Objekt ist sowieso schon serializable... das ist also nicht das Problem.

@SavageX: wieso versuchst du es nicht einfach so wie mit jedem anderen Objekt auch? Einen ObjectStream aufmachen, das Class Objekt rüberschieben, drüben laden, und dann... nunja... irgendwas damit machen.

SavageX
2007-08-23, 12:59:40
@SavageX: wieso versuchst du es nicht einfach so wie mit jedem anderen Objekt auch? Einen ObjectStream aufmachen, das Class Objekt rüberschieben, drüben laden, und dann... nunja... irgendwas damit machen.

Habe ich schon versucht (halt normal mit ObjectOutputStream und auf der anderen Seite mit ObjectInputStream) - hat leider nicht geklappt.

http://www.forum-3dcenter.org/vbulletin/showpost.php?p=5778709&postcount=4

Monger
2007-08-23, 13:24:04
Sorry, da war ich mit den Gedanken wirklich daneben.

Nach kurzer Suche habe ich das Instrument (http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-frame.html) Package gefunden. Schau dir davon vorallem mal den ClassFileTransformer an. Der sieht so aus, als würde es das ClassFile einer genannten Klasse in ein byte[] packen.

SavageX
2007-08-23, 14:15:02
Nach kurzer Suche habe ich das Instrument (http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-frame.html) Package gefunden. Schau dir davon vorallem mal den ClassFileTransformer an. Der sieht so aus, als würde es das ClassFile einer genannten Klasse in ein byte[] packen.

Das sieht interessant aus. Nur leider ist das ein Interface, für dass es irgendwie keine fertige Implementierung zu geben scheint...

SavageX
2007-08-23, 15:13:52
So, ich habe mir was zusammengeschraubt, welches dann doch Jar-Archive durchwühlt und aus denen die Klassen ausliest.

Und ja, ist hässlich und muss von mir noch aufgeräumt werden:


package jobDispatcher;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import jobDispatcher.jobDistributor.DistributorImpl;

public class JarClassFileReader {

public static byte[] getClassBytes(Class<?> c) {
byte[] result = null;

String classFileName = c.getCanonicalName().replace(".", "/") + ".class";

boolean found = false;

Vector<File> jars = new Vector<File>();

findJarFiles(new File("."), jars);

for(File f : jars) {
try {
JarInputStream jis = new JarInputStream(new FileInputStream(f));

JarEntry entry = jis.getNextJarEntry();

while(entry != null) {
if(entry.getName().equals(classFileName)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();

int read = 0;
byte[] buffer = new byte[1024];
read = jis.read(buffer);
while(read > 0) {
baos.write(buffer, 0, read);
read = jis.read(buffer);
}

result = baos.toByteArray();

found = true;
break;
}

entry = jis.getNextJarEntry();
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

if(found)
break;

}

if(!found)
return null;

return result;
}

private static void findJarFiles(File start, Vector<File> files) {
if(start.isDirectory()) {

for(File f : start.listFiles()) {
if(f.isDirectory()) {
findJarFiles(f, files);
} else {
if(f.getName().endsWith(".jar"))
files.add(f);
}
}
}
}

public static void main(String[] args) {


byte[] result = JarClassFileReader.getClassBytes(DistributorImpl.class);
System.out.println(result.length);

}

}

Abnaxos
2007-08-26, 17:11:05
Der Ansatz mit getResourceAsStream() war schon richtig. Dein jetziger Ansatz hat das Problem, dass er tatsächlich die JARs durchsucht, davon ausgeht, dass die im Dateisystem liegen usw. -- das funktioniert nur in einfachen Fällen.

Die Daten für die Klasse my.package.MyClass findest du via MyClass.class.getResource("MyClass.class") oder MyClass.getClassLoader().getResource("my/package/MyClass.class") (bzw. deren getResourceAsStream()-Äquivalente). Damit bist du auch gleich fein raus, wenn du verschiedene Versionen von MyClass in verschiedenen ClassLoadern hast: Es wird diejenige Resource gefunden, von der diese spezielle Klasse auch tatsächlich geladen wurde.

Alternativ kannst du auch einen ClassLoader schreiben, der sich das byte[] jeder Klasse, die er geladen hat, merkt, damit du es nicht ständig von der Disk holen musst.

Edit:

Übrigens würde ich eher mit getName() arbeiten, nicht mit getCanonicalName().

Ja, aber wie willst du denn die Klasse selbst serializable machen? Das Objekt davon - klar, aber die Klasse selbst?
Sag blos, das kann Java ned. ... Das ist ja schwach ^^
Das ist leider tatsächlich nicht so einfach, da moderne JVMs die Klassen ziemlich "verunstalten", der Performance halber (Inlining, Loop-Unrolling etc). Nachdem die Klasse geladen ist, wird sehr bald nichts mehr vom Bytecode übrig sein, geschweige denn, dass man das Ding dann noch (womöglich sogar noch über verschiedene Architekturen) irgendwo durchserialisieren kann.

Zudem kennt Java keine Meta-Klassen. Das Package, mit dem sich Java zur Laufzeit selber untersuchen kann, heisst "reflection" und der Name hat einen Grund: java.lang.Class und die Klassen aus java.lang.reflect sind eben nur Reflektionen, also Abbilder. MyClass.class ist also nicht die Klasse selber, es ist nur eine Reflektion davon, im Unterschied zu Sprachen wie SmallTalk, wo Klassen eben auch Objekte sind (=> Meta-Klassen).

SavageX
2007-08-29, 09:03:11
Der Ansatz mit getResourceAsStream() war schon richtig. Dein jetziger Ansatz hat das Problem, dass er tatsächlich die JARs durchsucht, davon ausgeht, dass die im Dateisystem liegen usw. -- das funktioniert nur in einfachen Fällen.

Die Daten für die Klasse my.package.MyClass findest du via MyClass.class.getResource("MyClass.class") oder MyClass.getClassLoader().getResource("my/package/MyClass.class") (bzw. deren getResourceAsStream()-Äquivalente). Damit bist du auch gleich fein raus, wenn du verschiedene Versionen von MyClass in verschiedenen ClassLoadern hast: Es wird diejenige Resource gefunden, von der diese spezielle Klasse auch tatsächlich geladen wurde.


Wow, vielen Dank! :eek:

Hatte die Hoffnung auf eine gute Lösung ja schon aufgegeben...

Xmas
2007-08-29, 14:58:42
Zudem kennt Java keine Meta-Klassen. Das Package, mit dem sich Java zur Laufzeit selber untersuchen kann, heisst "reflection" und der Name hat einen Grund: java.lang.Class und die Klassen aus java.lang.reflect sind eben nur Reflektionen, also Abbilder. MyClass.class ist also nicht die Klasse selber, es ist nur eine Reflektion davon, im Unterschied zu Sprachen wie SmallTalk, wo Klassen eben auch Objekte sind (=> Meta-Klassen).
Dein Erklärungsversuch in allen Ehren, aber der Name kommt eigentlich von (Selbst-)Reflexion, sprich über das eigene Sein nachzudenken. Für Code ist dies dann die Fähigkeit, die eigene Struktur zu ermitteln. ;)