ClassLoader für Variablen einer Klasse setzen

  • Themenstarter Themenstarter derdergeht
  • Beginndatum Beginndatum
D

derdergeht

Gast
Hallo,

ich würde mich über eine Lösung für folgende Situation freuen:
Eine Klasse enthält einige Variablen. Sie wird von einer anderen Klasse B geladen, instanziert und verwendet. Ich habe Zugriff auf den Code von Klasse A (nicht auf Klasse B!) und möchte dafür sorgen, dass alle Variablen der Klasse A von einem eigenen ClassLoader geladen werden.
Wie mache ich das am besten?

LG und vielen Dank im Voraus!
 
Und als kleine Nebenfrage: Sollte ich den normalen ClassLoader oder einen URLClassLoader erweitern, wenn Klasse B die Klasse A über einen URLClassLoader geladen hat, Klasse A aber keine weiteren expliziten Aufrufe des ClassLoaders macht? Anmerkung: Klasse A muss ggf. trotzdem Klassen aus URLs laden, die Klasse B dem URLClassLoader eingebracht hat.
 
Also Variablen werden erstmal nicht von einem ClassLoader geladen, aber ich vermute mal du meinst, die Klassen für die Typen deiner Variablen.
Wenn du keinen Zugriff auf den Code von B hast wird das auf direktem Wege unmöglich. Alle Klassen auf die im Code von A direkt verwiesen wird, werden mit dem selben ClassLoader wie A geladen. Das einzige was du machen kannst ist eine "Proxy-Klasse" dazwischen zu schalten, die du dann in A dynamisch lädst (also über deinen eigenen ClassLoader per loadClass) und dann nur mit Reflections verwenden kannst.
 
Also Variablen werden erstmal nicht von einem ClassLoader geladen, aber ich vermute mal du meinst, die Klassen für die Typen deiner Variablen.
Du hast natürlich Recht.

Das einzige was du machen kannst ist eine "Proxy-Klasse" dazwischen zu schalten, die du dann in A dynamisch lädst (also über deinen eigenen ClassLoader per loadClass) und dann nur mit Reflections verwenden kannst.
Meinst du es so, dass ich A zu C umbenenne, eine neue Klasse A die gleiche (von B geforderte) Struktur wie die alte hat und A dann C instanziert und alle Aufrufe per invoke weitergibt?

LG
 
Ja, so wars es gemeint.
Du kannst natürlich auch C ein allgemeines Interface implementieren lassen, auf das du dann castest (Die in dem Interface genutzten Klassen dürfen natürlich keine sein, die von deinem eigenen ClassLoader geladen werden müssen). Dann ersparst du dir das Rumgebastle mit Reflections und musst nur das eigentliche Object mit newInstance() erzeugen.
 
Also einen ClassLoader brauchts in Java eigentlich nur dann, wenn man Klassen ausserhalb des vordefinierten Class- und Ressourcepfads oder verschlüsselte laden will.
Um klassenintern spezielle Variablentypen nach aussen hin "geheim" zu halten, benötigt man sie jedenfalls nicht, geschweige denn "newInstance()" oder anderes dubioses Reflection-Zeugs.
Die Geheimhaltung einer Klasse sieht dann ungefähr so aus.
Java:
abstract class A {
  public abstract A getImpl();
}

class B extends A {
  public A getImpl() {
    return this; // simples Beispiel... schon klar, das man für sowas nichts abstraktes braucht
  }
}
B kann nun aber irgendwo liegen und nicht unbedingt im Klassenpfad. B könnte also auch über einen evtl. versteckten CustomClassLoader geladen worden sein. Wenn nun ein Ahnungsloser versucht anders (z.B. mit einem simplen loadClass) an die Klassendefinition von B zu kommen, wird er erst mal scheitern.
Solche Konstrukte werden natürlich erst bei der Entwicklung eigener APIs interessant, mit denen dann andere widerum eigenes programmieren können. Als Beispiel könnte man hier Java2D bringen. Wer hätte gedacht, dass man dort eigentlich stets ein SunGraphics2D- statt einem Graphics2D bzw. Graphics-Objekt bekommt? Ist so, aber wen interessierts? Wichtig ist nur die Graphics-Funktion des APIs. Die konkrete Implementation wird erst dann wichtig, wenn man z.B. wissen will, wie es implementiert wurde oder Methoden der konkreten Klassen aufrufen will. Im ersten Fall ist's Neugier, im zweiten verletzt man schlicht API-Konventionen. In beiden Fällen aber, sind's Entwickler (oder Cracker), die es interessieren könnte.
 
Zuletzt bearbeitet von einem Moderator:
Vielleicht erklärt derdergeht mal kurz, was er denn eigentlich erreichen möchte.

Bis jetzt hat er ein Problem, glaubt eine Lösung dafür zu haben und fragt wie er diese vermeintliche Lösung hinbekommt.
Classloading ist alles andere als trivial und deiner Frage nach hast du davon eher weniger Ahnung.

Erkläre dein Problem und wir können über eine Lösung reden.
 
Also, was ich erreichen möchte ist folgendes:

Eine Anwendung (besitzt Klasse B) startet eine weitere Anwendung, die sie dank UrlClassLoader in den Classpath einbringt und per Reflecting startet. Hauptklasse dieser Anwendung ist A. Ich kann den Quellcode von dieser Anwendung nicht wirklich ändern. Klingt wohl sehr obskur, dennoch muss ich einige Klassen unter bestimmten Bedingungen aus einem wiederum anderen Platz (.class Ordner) nachladen und Klassen dieser Anwendung damit "überschreiben".
 
Ich habe mich jetzt bemüht die Erklärung von Kevin94 umzusetzen. Es funktioniert im Test sehr gut. Allerdings sind Klasse A (also auch C) und B Applets. Applet B lädt Applet A. Ich muss zugeben, dass ich mich bisher wenig mit Applets beschäftigt habe. Kann mir jemand sagen, wie ich Klasse A als Proxy-Klasse so gestalte, dass sie praktisch das Applet C darstellt? Applet B implementiert AppletStub und ruft u.a. instanceOfA.setStub(this) auf. Was muss ich alles beachten?
 
Ich habe mich jetzt bemüht die Erklärung von Kevin94 umzusetzen. Es funktioniert im Test sehr gut. Allerdings sind Klasse A (also auch C) und B Applets. Applet B lädt Applet A. Ich muss zugeben, dass ich mich bisher wenig mit Applets beschäftigt habe. Kann mir jemand sagen, wie ich Klasse A als Proxy-Klasse so gestalte, dass sie praktisch das Applet C darstellt? Applet B implementiert AppletStub und ruft u.a. instanceOfA.setStub(this) auf. Was muss ich alles beachten?
OMG... Ich ahne und vermute...
Jede wette, du programmierst gerade ein oder zwei Applets, ob gewollt oder nicht, die nur lokal in Eclipse laufen sollen, jedoch niemals in Browsern. Classloader sind, wie bereits erwähnt, selber schon nicht ganz trivial. Das Ganze jetzt auch noch in Applets zu versuchen, wo ein Sicherheitsmechanismus greift, der einem das Hinzufügen von Classloadern auch noch verbietet, ist schlicht... um himmels Willen, mach's anders!
 
Ach du... Die Sicherheitsregeln bei Applets hatte ich ganz vergessen -.-

Das klingt ja schon einmal... schlecht.
 
Gibt es denn keine Möglichkeit, mein Problem anders zu lösen? Die Anwendung läuft ja vorerst als normale Application mitsamt Frame. Erst im Nachhinein wird in den Frame ein Applet geladen.
 
Wofür wird das Applet denn benötigt? Tut es nicht auch ein Panel?
Die einzigen Vorteile, die man bei Applets hat, sind doch, dass man Audio- und Image-Dateien einfacher abspielen kann. Instanziert man es als Component in einer Anwendung mit einer Main, bekommt es im Normalfall auch keinen SicherheitsManager, allerdings bekommt es auch keinen AppletStub oder -Context. Ein Context wird aber zum Laden von Images und Audios benötigt und man müsste also einen Stub und in diesem einen Context selbst implementieren. Schon ist man an dem Punkt, wo man die Appletfunktionalität auch ebensogut in einem Panel einbauen kann.
[EDIT]Ich erinner' mich da noch dunkel an meine HybridAnwendung. Die darauf folgende Antwort sollte einem zu Denken geben. 😉[/EDIT]
 
Zuletzt bearbeitet von einem Moderator:
Vielleicht anders: kannst du dein Problem in funktionalen und nicht funktionalen Anforderungen beschreiben? Dazu gehört auch, dass du nicht in Klassen denkst sondern beschreibst, was du mit deinen Anstengungen bzgl. Classloading und Appletgedönse bewerkstelligen möchtest...

Beispiel: ich möchte, dass meine Javaapplikation von Entwicklern erweitert werden kann. Dabei sollen die Erweiterungen keine Abhängigkeiten untereinander haben ...usw ... usw...
 
Vielen Dank zunächst für eure Bemühungen.

Ich versuche, es wie vorgeschlagen zu erklären:

Ich möchte eine bestehende Javaanwendung so verändern, dass einzelne Klassen (die in einer Konfiguration festgelegt sind) aus einem Ordner als class-Datei nachgeladen werden. Diese sollten dann anstatt der Originalklassen verwendet werden. Dabei darf die Struktur des Programmes nicht verändert werden und die Änderungen an bestehenden Klassen geringfügig sein. Der Einstiegspunkt meiner Anwendung ist ein Applet (kann ich nicht ändern), dass von einem zugehörigen Starter in einem Applet(Stub) in einem Frame platziert wird.
Auf den Code des Starters kann ich keinen Einfluss nehmen.

Was ich bisher gemacht habe:
Ich habe Stevens Vorschlag versucht umzusetzen. Bei Tests (über einen temporären Einstiegspunkt in Form einer main-Funktion) scheint das Laden der Klassen einwandfrei zu funktionieren. Genaues Testen ist ohne Starter nicht möglich.
Folglich habe ich versucht, den Proxy für das Applet zu implementieren. Hier scheitere ich allerdings am Einbinden des Applets in den Starter (Stichwort Applet replace, da eigentlich der Proxy angezeigt wird, es soll nun aber das von ihm geladene Applet angezeigt werden). Da benötige ich Hilfe 🙂 Ich gebe zu, es ist mittlerweile vom Thema abgekommen, aber vielleicht versteht der eine oder andere die Situation mittlerweile gut genug, um mir zu erklären wie ich das Applet einbinden muss.

Kurz: Wie zeige ich ein Applet in diesem Fall in einem Applet an?
 
Wenn's nur um das Anzeigen geht: Ein Applet ist ja auch nix anderes als ein Panel, also kannst du es wie ein Panel zu einem Panel hinzufügen. Mit geeigenetem LayoutManager und Constraints-Object (z.B. BorderLayout und CENTER) merkt man optisch keinen Unterschied.
 
Das dachte ich bis gestern auch. Bloß bleibt mein Applet leider schwarz.

Ich vermute, das Problem liegt daran, dass die Proxyklasse einerseits die Darstellung durch geeinete Methodenaufrufe regeln muss, als Proxy aber eigentlich ja alle Aufrufe direkt weiterleiten muss. (Falls jemand diesen Satz versteht: ) Kann mir jemand ein einfaches Beispiel zeigen? Enthalten sein müssten meiner Meinung nach ein Applet als Starter, eines als Proxy und das eigentliche Applet. Das Proxyapplet muss ohne Objekttypen des "Hauptprogrammes" zu verwenden letzteres Applet mit irgendeinem Classloader laden.

Es tut mir Leid, wenn das zu viel verlangt ist, aber ich komme gerade überhaupt nicht weiter, und das Laden von Applets via eigenem ClassLoader und das Anzeigen von diesen via Proxyklasse scheint nicht sehr häufig zu sein.
 
Somit sieht das für mich nach zwei Problemen aus.

Bei der Sache mit dem Applet kann ich nicht sinnvoll helfen.

Bzgl. Classloading:
Wenn du Klassen anstatt einer anderen Klasse verwenden möchtest, dann muss die gewünschte Klasse lediglich im Klassenpfad vor der anderen zu finden sein. Das erreicht man z.B. mit dem Endorsed-Mechanismus der VM oder einfach durch anpassen des Klassenpfades. Du solltest dir im Klaren darüber sein, dass das aber kein guter Aufbau einer Applikation ist. Ich würde so etwas produktiv nicht verwenden, außer als temporären Workaround um einen Hotfix-Patch zu realsieren.

Sonst kannst du für die Ausführung des "Plugins" einen eigenen Classloader definieren, der den Klassenpfad erweitert und NICHT (wie der Standard URL Classloader) zuerst an den Parent delegiert. So etwas kann man selber schreiben oder findet man im Netz. Ab hier begibst du dich aber auf dünnes Eis, wenn du mit Classloading nicht auf Du und Du bist..

EDIT
-----
P.S. du solltest für unterschiedliche Themen auch einzelne Threads starten, um die Diskussion zu vereinfachen
 
Zuletzt bearbeitet:
Zum neuen Thread: habe ich ja oben schon begründet.


Das Ändern des Classpaths habe ich auch versucht, war aber beim ClassLoader deutlich erfolgreicher. Ich traue es mir durchaus zu, das zum Funktionieren zu bringen. Verzweifeln tue ich nur am Applet 🙁
 
Das Ändern des Classpaths habe ich auch versucht, war aber beim ClassLoader deutlich erfolgreicher.
Interessant.

Ich traue es mir durchaus zu, das zum Funktionieren zu bringen.
Das hoffe ich. Das laden der Klasse ist ja auch nur der kleinste Teil des Problems. Dummerweise muss man mit diesen Klassen bzw. deren Instanzen auch interagieren. Beliebt sind hier Fehler wie ClassCastException, IllegalAccessError, VerifyError und LinkageError. Dazu kommt noch verändertes Verhalten durch statische Inhalte (blöd, zeigt sich nämlich nicht durch eine explizite Fehlermeldung) und nicht zu vergessen ClassNotFoundExceptions und nicht gefundene Ressourcen bzw. falsche Ressourcen.

Viel Erfolg!
 
Ich frag' mal frech... welches Applet möchtest du wo sichtbar machen und vor allem warum?
Glaskugel sagt: Da gibt es ein Applet im Internet, welches ich lokal auf meinem Rechner laufen lassen will. Leider bezieht dieses Applet seitenbezogene Infos vom Server, ohne die es nicht lauffähig ist.
Und wo wir schon dabei sind... Benötigt das Applet zufällig irgendwelche Parameter? Wenn dem so ist, kannst du dich bis zum Hexenschuss quer verrenken und bringst es nicht zur Anzeige. Wenn du aber die Parameter in den Stub bzw. in den Context bringst, gehts evtl. auch ohne Verrenkungen.
 
Habe ich oben schon angedeutet. Fehlende Parametrisierung des Applets. Evtl. postest du mal den Teil des Seitencodes der das Applet anzeigt (also dort, wo es funktioniert).
Viele Applets benötigen zum Anzeigen nämlich Parameter ("<param>"-Tag in HTML), vor allem dann, wen eine Grafik zur Anzeige gebracht werden soll.
 
Oder du überschreibst nicht alle Methoden. Du musst nicht nur die Methoden, die in Applet definiert sind über den Proxy delegieren, sondern auch die in den Oberklassen, was so an die 100 Stück oder mehr sein dürften.
 

Neue Themen


Zurück
Oben