Ausarbeitung - Universität Münster

Werbung
Westfälische Wilhelms-Universität Münster
Ausarbeitung
Varianten zur Laufzeit: Java Reflection
Im Rahmen des Hauptseminars „Variantenmanagement“
Christian Müller
Themensteller: Prof. Dr. Herbert Kuchen
Betreuer: Dipl.-Wirt. Inform. Christoph Lembeck
Institut für Wirtschaftsinformatik
Praktische Informatik in der Wirtschaft
Inhaltsverzeichnis
1
Variantenmanagement zur Laufzeit........................................................................... 3
2
Java Reflection - Grundlagen .................................................................................... 4
3
4
2.1
Grundbegriffe bei Java Reflection.................................................................... 4
2.2
Möglichkeiten von Java Reflection .................................................................. 5
2.3
Sicherheitsaspekte bei Java Reflection ........................................................... 14
Beispielhafte Ausführungen zum Thema Reflection............................................... 17
3.1
Reflection in der Praxis – JUnit...................................................................... 17
3.2
Reflection in der Anwendung – Ein EventDispatcher.................................... 18
Java Reflection – Flexibilität für die Applikation ................................................... 23
A.
Zeitliche Charakterisierung des Reflection......................................................... 24
B.
Beispielklasse C .................................................................................................. 25
C.
JUnit-Methode isTestMethod(Method m) .......................................................... 26
D.
Ereignisbehandlung mit und ohne EventDispatcher........................................... 27
Literaturverzeichnis ........................................................................................................ 28
II
Kapitel 1: Variantenmanagement zur Laufzeit
1 Variantenmanagement zur Laufzeit
Die zunehmende Komplexität heutiger Applikationen erfordert eine Flexibilität, die es
gestattet, Anwendungen im Rahmen ihres Lebenszyklus nicht als statisch zu begreifen.
Vielmehr ist es heutzutage notwendig, Voraussetzungen zu schaffen, die es
ermöglichen, eine Applikation an die sich ändernden Anforderungen zu erweitern und
anzupassen. Für diese Zielsetzung werden Metaarchitekturen benötigt, durch die
Informationen über die Struktur und Ausführungszustand der Applikation zur Laufzeit
bereitgestellt werden.
Mit Java Reflection wurde eine Software-Bibliothek geschaffen, welche dem
Softwareentwickler
eine
Metaarchitektur
zur
Verfügung
stellt,
die
eine
Variantenbildung zur Laufzeit ermöglicht. Varianten einer Applikation werden
begründet durch Hinzufügen neuer oder Ändern bereits bestehender Funktionalität,
können aber auch aus der Modifikation weniger relevanter Implementierungsdetails
resultieren. [Ma94, S. 74 f]
Aus diesem Grund soll in der vorliegenden Arbeit ein thematischer Überblick zum
Reflection erfolgen, der sowohl Grundlagen und Anwendungsfelder reflexiver
Programmierung als auch Sicherheitsaspekte beleuchtet.
Im Kapitel 2 der Arbeit befinden sich grundlegende Ausführungen zur Thematik des
Reflection, in denen eine definitorische Abgrenzung von Begriffen vorgenommen wird,
die im Rahmen der Themenstellung relevant sind. Daran anschließend erfolgt eine
Vorstellung der sich aus der Nutzung von Reflection ergebenden Möglichkeiten bspw.
für die Softwareentwicklung. Die durch Reflection garantierte Flexibilität bietet jedoch
auch geeignete Angriffspunkte für die schadhafte Auslegung und Nutzung von
Applikationen, so dass hierzu kurz Sicherheitsaspekte diskutiert werden.
Den theoretischen Überlegungen folgend, werden in Kapitel 3 zwei exemplarische
Einsatzfelder des Reflection skizziert. Zum einen erfolgt eine kurze Darstellung des
Test-Frameworks JUnit und die dort erfolgreich verwendeten reflexiven Bestandteile.
Zum anderen wird ein EventDispatcher vorgestellt, der es durch Einsatz reflexiver
Programmierung
ermöglicht,
die
Registrierungsarbeiten
im
Rahmen
der
Ereignisbehandlung in Java zu automatisieren bzw. die konkrete Ereignisbehandlung
austauschbar gestaltet.
3
Kapitel 2: Java Reflection - Grundlagen
2 Java Reflection - Grundlagen
2.1 Grundbegriffe bei Java Reflection
Der Einsatz reflexiver Programmierung, bereitgestellt durch java.lang.reflect.*,
ermöglicht es, Zugriff auf Strukturinformationen von Klassen zu erlangen und
Statusinformationen von Objekten zur Laufzeit in einem begrenzten Rahmen zu
modifizieren. Darüber hinaus erlaubt die Nutzung der Bibliothek eine Ausführung von
Methoden, deren Name und Signatur erst zur Laufzeit bekannt sind. Im Rahmen der
Diskussion des Reflections finden sich unterschiedliche Begriffe, die zunächst
definitorisch abgegrenzt werden sollen.
Reflection umfasst laut Definition die Fähigkeit sowohl Objekte zu beobachten als auch
Zustandsänderungen von Objekten herbeizuführen. Um die auch introspection genannte
Untersuchung von Objekten und deren Zustand zu garantieren, werden entsprechende
Daten über die konkrete Datenstruktur, ergo Metadaten zur Verfügung gestellt.
[FoFo04, S. 15] Wird von intercession gesprochen, ist die Modifikation von Objekten
und Ausführung von Programmcode gemeint. [FoFo04, S. 74] Vorgehalten werden
diese Metadaten durch Meta-Level-Objekte, die Funktionalität zur Verfügung stellen,
um Meta-Level-Operationen auf Basisobjekte zu erlauben. Eine Untermenge der MetaLevel-Objekte sind die Metaobjekte, welche strukturelle Informationen über Attribute,
Konstruktoren und Methoden beinhalten und die Struktur sog. Basisobjekte darstellen.
Bspw. fungieren Instanzen der Klasse java.lang.Class als Metaobjekte. Die Zielsetzung
der Metaprogrammierung ist eine Separation von funktionalen und nicht-funktionalen
Aspekten der Applikation. Basisobjekte werden als funktionale Aspekte verstanden, die
mit dem Zweck der Applikation einhergehen, Meta-Level-Objekte hingegen dienen zur
Administration der Objekte des Basislevels. [MiGo97, S. 6]
Abbildung 1: Beziehungen zwischen Meta- und Basissystem
4
Kapitel 2: Java Reflection - Grundlagen
Für Anwendungen, wie User-Interface-Builder (UI-Builder) oder Debugger sind MetaLevel-Objekte und die dadurch gekapselten Metadaten von großer Bedeutung. Erst
durch die Meta-Level-Objekte ist es bspw. für Debugger möglich Objekte zur Laufzeit
zu inspizieren und für den Anwender auf bequeme Weise zu präsentieren. Ebenso
verwenden UI-Builder notwendigerweise diese Daten.
Weiterhin ist es möglich, Reflection-Arten hinsichtlich zeitlicher Kriterien zu
charakterisieren. An dieser Stelle wird zwischen Reflection zur Kompilierzeit (compiletime reflection), zur Programmladezeit (load-time reflection) und Reflection zur
Laufzeit (run-time reflection) differenziert. [MiGo97, S. 9 f] Im Mittelpunkt der
Charakterisierung steht der Zeitpunkt, bei dem das Basissystem an das Metasystem
gebunden wird. An dieser Stelle erfolgt keine weitere Diskussion der Arten. (Vgl.
Anhang A für eine schematische Darstellung)
2.2 Möglichkeiten von Java Reflection
Um einführend die Nutzungsmöglichkeiten von Java Reflection aufzuzeigen, werden
die Kernelemente reflexiver Programmierung vorgestellt und anhand geeigneter
Beispiele erläutert. Dazu wird zunächst das Metaobjekt java.lang.Class thematisiert, mit
dessen Nutzung zur Laufzeit Statusinformationen von Objekten verfügbar werden. Im
Anschluss daran werden die Ausführung von Methoden und die Instanziierung von
Objekten zur Laufzeit angesprochen.
Metaobjekt Class
Das Java Reflection API bietet verschiedene Möglichkeiten, um an Informationen über
Objekte zur Laufzeit zu gelangen. Zentraler Bestandteil von Java Reflection ist die
Nutzung des Class-Objekts. Für jede geladenen Klasse werden von der Java Virtual
Machine (JVM) Class-Objekte [JADCl05] erzeugt, mit denen der Zugriff auf weitere
Metaobjekte wie java.lang.reflect.Method oder java.lang.reflect.Field möglich ist, die
jeweils die Methoden und Felder einer Klasse repräsentieren.
Informationen zur Laufzeit
Häufig ergeben sich Situationen, in denen bestimmte Informationen erst zur Laufzeit
bekannt sind bzw. dynamisch geladen werden sollen. Ausgangspunkt reflektiver
Programmierung bildet meist das Bemühen, das Class-Objekt der jeweiligen Klasse
oder des Objektes zu erhalten. Das folgende Beispiel illustriert unterschiedliche
5
Kapitel 2: Java Reflection - Grundlagen
Möglichkeiten, um Zugriff auf das Class-Objekt zu erlangen. Im Beispiel wird
differenziert zwischen folgenden Fällen:
a) Es existiert bereits vor dem Kompilierzeitpunkt eine Referenz auf das zu
untersuchende Objekt. Die Klasse Object definiert die Methode getClass(),
mittels derer die objekteigene Class-Referenz ausgelesen werden kann. In
diesem Fall liegt die Klasse schon in instanziierter Form als Objekt vor d. h. der
Classloader hat die Klasse schon geladen.
b) Der Name der Klasse ist vor dem Kompilierzeitpunkt verfügbar. Das ClassObjekt ist über eine konstante Referenz ansprechbar, wobei hierfür der voll
qualifizierte Name (Identifier) der Klasse benötigt wird.
c) Der voll qualifizierte Name der Klasse wird erst zur Laufzeit spezifiziert. Die
mithin flexibelste Art, um an eine Referenz des Class-Objektes zu gelangen,
erfolgt durch die Nutzung der statischen Class-Methode forName(String s).
In allen drei Fällen resultiert ein Class-Objekt, dem Statusinformationen des Objekts
und Strukturinformationen der zugehörigen Klasse entnommen werden können.
public class GetClassBsp {
public static void main(String[] args) {
Class classA, classB, classC;
javax.swing.JFrame frame = new javax.swing.JFrame();
try {
/** Fall a */
classA = frame.getClass();
/** Fall b */
classB = javax.swing.JFrame.class;
/** Fall c */
classC = Class.forName(“javax.swing.JFrame”);
System.out.println("Fall
System.out.println("Fall
System.out.println("Fall
} catch (Exception e) {/**
a: " + classA.toString());
b: " + classB.toString());
c: " + classC.toString());
Exception Handling */}
}
}
Eine Ausführung des Programms führt zu folgender Ausgabe:
Fall a: class javax.swing.JFrame
Fall b: class javax.swing.JFrame
Fall c: class javax.swing.JFrame
Nun ist es möglich, mit Hilfe des Class-Objekts weitere Informationen über das Objekt
und dessen Zustand zu bekommen. Für diesem Zweck verfügt das Class-Objekt über die
6
Kapitel 2: Java Reflection - Grundlagen
Methoden getField(String Feldname) und getFields(), mit denen sich gezielt einzelne
aber auch alle Felder einer Klasse ansprechen lassen, um bspw. den Inhalt oder die
Modifier des Feldes zu ermitteln. Die Methode getFields() gibt ein Array mit
Metaobjekten vom Typ java.lang.reflect.Field zurück, die den deklarierten oder
geerbten Feldern der Klasse mit dem Modifier public entsprechen. Im Unterschied dazu
erfolgt durch die Methode getDeclaredFields() eine Rückgabe aller in der Klasse
deklarierten Felder, unabhängig vom zugehörigen Modifier des Feldes. Bei der
Betrachtung von Sicherheitsaspekten wird in Kapitel 2.3 nochmals auf die Methode
getDeclaredFields() eingegangen.
Die Klasse C verfügt über zwei Felder – intValue vom primitiven Typ int und
stringValue vom Typ String. Beide Felder sind mit dem Modifier public versehen. (Vgl.
Anhang B für Klasse C)
/** relevanter Teil der Klasse C.java */
public int intValue;
public String stringValue;
/** relevanter Teil der Applikation.java */
public static void readC(Object c) {
Class clazz;
try {
clazz = c.getClass();
Field[] fields = clazzA.getFields();
/** Auflisten aller Felder der Klasse */
for (Field field: fields) {
System.out.println("Name des Feldes: " +
field.getName());
System.out.println("Typ: " + field.getType().getName());
/** Liefert einen int zurück... */
int modifier = field.getModifiers();
/** ...der durch die Nutzung dieser statischen
* Methode umgewandelt wird */
System.out.println("Modifizierer: " +
Modifier.toString(modifier));
/** Auslesen und Verändern der Werte */
if (field.getType().equals(int.class)) {
System.out.println("Inhalt von: " + field.getInt(c));
/** Modifizierung des Datenfeldes */
System.out.println("--modifiziert--");
field.setInt(c, 112);
System.out.println("Inhalt von: " + field.getInt(c));
} else if (field.getType().equals(String.class)) {
System.out.println("Inhalt von: " + field.get(c));
System.out.println("--modifiziert--");
field.set(c, new String("Es schneit!"));
System.out.println("Inhalt von: " + field.get(c));
}}} catch (Exception e) {/** Exception Handling */}
}
7
Kapitel 2: Java Reflection - Grundlagen
Die Ausgabe des Programms liefert:
Name des Feldes: intValue
Typ: int
int-Modifizierer: 1
Modifizierer: public
Inhalt von: 4711
--modifiziert-Inhalt von: 112
Name des Feldes: stringValue
Typ: java.lang.String
int-Modifizierer: 1
Modifizierer: public
Inhalt von: Hallo
--modifiziert-Inhalt von: Es schneit!
Unter Verwendung der Methoden getInt(Object obj), get(Object obj), sowie
setInt(Object obj, int i) und set(Object obj, Object value), die durch die Klasse Field
bereitgestellt werden, ist es möglich Datenfelder auszulesen und deren Inhalte
entsprechend zu manipulieren. Darüber hinaus stehen get-/set-Methoden für jeden
anderen primitiven Datentyp, bspw. getBoolean(Object obj) und setBoolean(Object obj,
boolean z), zur Verfügung.
Methoden zur Laufzeit finden und ausführen
Nachdem Felder und Strukturinformationen über die Klasse C ausgelesen werden
können, ist ein weiterer Schritt bspw. deklarierte und geerbte Methoden von C zu
ermitteln. Ausgangspunkt hierzu bildet wieder das Vorliegen eines Class-Objekts der zu
analysierenden Klasse. Durch die Verwendung von getMethod(String name, Class[]
parameterTypes) oder getMethods(), bereit gestellt durch Class, werden die einzelnen
Methoden der spezifizierten Klasse zurück gegeben. Eine Methode der zu
untersuchenden Klasse wird jeweils repräsentiert von einem Metaobjekt des Typs
java.lang.reflect.Method. Auf die Verwendung der Methode getDeclaredMethods()
wird in Kapitel 2.3 detaillierter eingegangen. Es sei hier kurz erwähnt, dass sich
getDeclaredMethods() in ähnlicher Weise verhält wie die bereits aufgeführte Methode
getDeclaredFields(). Wird bspw. ein Array gefüllt mit Method-Objekten der einzelnen
Klassenmethoden zurückgegeben, kann dieser Array durchlaufen werden, um
Informationen über die Methoden zu erhalten. Hierbei ermöglicht z. B. die durch
Method bereit gestellte Methode getParameterTypes() die Signatur der zu
untersuchenden Methode zu ermitteln. Darüber hinaus können die durch die Methode
8
Kapitel 2: Java Reflection - Grundlagen
ausgelösten Exceptions (getExceptionTypes()), der Typ der zurückgegebenen Werte
(getReturnType()) oder die Modifizierer der Methode (getModifiers()) erfragt werden.
Folgende Methode showMethods(Object c) illustriert einen Teil der dargestellten
Möglichkeiten, um an Informationen über die Methoden der Beispielklasse C zu
gelangen.
static void showMethods(Object c) {
Class clazz = c.getClass();
Method[] methods = clazz.getMethods();
for (Method method: methods) {
String methodName = method.getName();
System.out.println("Name: " + methodName);
String returnType = method.getReturnType().getName();
System.out.println("Return Type: " + returnType);
Class[] parameterTypes = method.getParameterTypes();
System.out.print("Parameter Types:");
for (Class parameterType: parameterTypes) {
String parameterString = parameterType.getName();
System.out.print(" " + parameterString);
}
System.out.println();
}
}
Die Ausführung der Methode resultiert in folgender gekürzter Ausgabe:
Name: getIntValue
Return Type: int
Parameter Types: int java.lang.String
Name: getIntValue
Return Type: int
Parameter Types:
Name: printString
Return Type: void
Parameter Types:
[…]
/** weiter Auflistung */
Reflection bietet die Möglichkeit, die auszuführenden Methoden eines Objektes erst zur
Laufzeit zu spezifizieren. Diese Ausführung wird ermöglicht durch die Nutzung der
Funktionalität des bereits erwähnten Metaobjekts Method.
Durch Überladen sind die Methoden einer Klasse, im Gegensatz zu deren Feldern, nicht
eineindeutig mithilfe der Methodennamen ermittelbar. Um eine Methode eindeutig zu
bestimmen bzw. auszuführen, wird daher die Kenntnis der Methodensignatur
vorausgesetzt.
9
Kapitel 2: Java Reflection - Grundlagen
Es sei bemerkt, dass primitive Datentypen, wie int oder boolean ebenso über ein ClassObjekt verfügen, welches bspw. durch int.class oder boolean.class abgebildet wird.
[FoFo04, S. 13] Diese Class-Objekte werden meist benötigt, um Methoden zu
identifizieren, deren Ausführung eine Übergabe primitiver Datentypen erfordern.
Die auszuführende Methode wird spezifiziert durch die Übergabe eines Objekt-Arrays,
der Class-Objekte enthält. Die übergebenen Class-Objekte wiederum repräsentieren in
ihrer Art und Reihenfolge die Signatur der zu findenden Methode. Unter der
Verwendung der Metaobjekt-Methode invoke(Object obj, Object… args) ist es
letztendlich möglich, die gewünschte Methode auszuführen. Hierbei finden jedoch die
Class-Objekte der primitiven Datentypen keine Verwendung. Vielmehr kommen die
korrespondierenden Wrapper-Objekte stellvertretend für die primitiven Datentypen zum
Einsatz. (z. B. int Æ java.lang.Integer) Die Methode invoke() konvertiert vor der
eigentlichen Ausführung die übergebenen Wrapper-Objekte in primitive Datentypen.
Grundsätzlich resultiert auch das Ergebnis einer in dieser Art ausgeführten Methode mit
Rückgabewert in Form eines Objektes. [FoFo04, S. 16 f] Im folgenden Beispielcode
wird deutlich, dass die Methode getIntValue(int i, String s) unter Verwendung von
int.class identifiziert wird, aber mit einem zu übergebenen Integer-Objekt aufgerufen
wird.
/** Relevanter Teil der Klasse C */
public int getIntValue(int i, String s) {/** Quellcode */}
/** Relevanter Teil der Applikation */
static void invokeMethods(Object c) {
try {
Class clazz = c.getClass();
Method method = clazz.getMethod("getIntValue",
new Class[] { int.class, String.class });
Integer myInt = new Integer(5);
String myString = "fuenf";
Object returnedObject = method.invoke(c,
new Object[] { myInt, myString });
System.out.println("Ergebnis: " + returnedObject + "\n Typ:"
+ returnedObject.getClass().getName());
} catch (Exception e) {/** Exception Handling */}
}
Es resultiert folgende Ausgabe:
getIntValue erfolgreich: 5 fuenf
Ergebnis: 5
Typ:java.lang.Integer
10
Kapitel 2: Java Reflection - Grundlagen
Um den Ablauf bzw. die beteiligten Objekte bei der Nutzung der invoke-Methode zu
skizzieren, sei auf Abbildung 2 verwiesen.
Abbildung 2: Sequenzdiagramm: Nutzung der invoke-Methode
Objekte zur Laufzeit instanziieren
Die Anforderungen denen Applikationen gerecht werden müssen, ändern sich im Laufe
ihrer Verwendung. Dies erfordert die Implementierung von Mechanismen, welche
neuen Programmcode in einer bereits kompilierten Applikation berücksichtigen. Um
den geforderten Grad an Flexibilität zu bieten, erlaubt eine reflexive Programmierweise,
neue Klassen zur Laufzeit einzubinden und deren bereitgestellte Funktionalität zu
nutzen. [FoFo04, S. 50]
Speziell die Forderung eine Applikation in Verbindung mit unterschiedlichen
Komponenten zu betreiben, setzt ein hohes Maß an Flexibilität voraus. In diesem Fall
sind zum Zeitpunkt des Kompilierens keine Informationen über Ablageort und
Funktionalität der Erweiterungen bekannt. Um den Sachverhalt der Instanziierung von
Objekten zur Laufzeit exemplarisch zu beschreiben, werden im Rahmen der
Ausführungen explizit Entwurfsmuster, deren Einsatz für die Problemstellung
zweckmäßig erscheint, genannt und kurz erläutert. Ist es bspw. notwendig, der
Applikation den Zugriff auf unterschiedliche Datenbanken zu ermöglichen, muss eine
Schnittstelle für jede Datenbank geschaffen werden, die sowohl heutigen als auch
zukünftigen Anforderungen genügt. Natürlich wäre diesbezüglich ein Einsatz der
JDBC-Technologie [JDBC05] wesentlich geeigneter. Um jedoch die Problemstellung in
11
Kapitel 2: Java Reflection - Grundlagen
geeigneter Weise zu skizzieren, wurde dieser Sachverhalt gewählt. In diesem Kontext
eignet sich der Einsatz des Strukturmusters Fassade [GHJ+97, S. 189] kombiniert mit
dem Erzeugungsmuster Fabrik [GHJ+97, S. 115]. Hierzu wird das Entwurfsmuster
Fassade verwendet, um die Funktionalität des Zugriffs auf die Datenbank zu kapseln.
Abbildung 3: Entwurfsmuster Fabrik und Fassade
Der Applikation wird auf diese Weise ein standardisierter Zugriff ermöglicht, wobei die
Fabrik kontextabhängig eine Fassade generiert, die den Anforderungen der Datenbank
entspricht. Dazu wird eine Instanz der Fassade beim ersten Zugriff dynamisch geladen,
d. h. die Fassaden-Klasse liegt in kompilierter Form an entsprechender Stelle im Paket
und wird von der JVM zur Laufzeit geladen. [FoFo04, S. 53] Ist bspw. der voll
qualifizierte Name der Klasse in einer Konfigurationsdatei hinterlegt, wird die Datei
beim Programmstart ausgelesen und geladen. Der Classloader gibt unter Verwendung
der Methode forName(String className) mithilfe des voll qualifizierten Namen das
Class-Objekt der gewünschten Fassade zurück. [FoFo04, S. 55] In diesem Fall wird zu
Beginn kein Class-Objekt der Fassade existieren, sodass der Classloader entsprechend
dem Verzeichnis- und Paketpfad eine .class Datei sucht und diese nach erfolgreicher
Suche lädt.
Besitzt die Fassadenklasse einen einfachen Konstruktor der Form public DBFassade1(),
ist es durch den Aufruf von fassadeCls.getInstance() möglich, eine Instanz der Klasse
zu erzeugen. Weist der Konstruktor jedoch eine höhere Komplexität auf, da bspw. ein
String
bei
der
Instanziierung
erforderlich
ist,
findet
das
Metaobjekt
java.lang.Constructor Verwendung. [FoFo04, S. 57 f] Das Constructor-Objekt ist dem
bereits erwähnten Method-Objekt ähnlich, bietet aber zusätzlich Informationen zu den
jeweiligen Konstruktoren einer Klasse an. Bevor jedoch eine neue Instanz der Fassade
erzeugt werden kann, muss der passende Konstruktor vorliegen. Dies erfordert ein
Class-Objekt vom im Konstruktor übergebenen Objekt. Der passende Konstruktor der
12
Kapitel 2: Java Reflection - Grundlagen
die gewünschte Signatur zur Instanziierung besitzt, wird durch den Aufruf
getConstructor(new Class[] {Class c}) ermittelt. Die Erzeugung einer Instanz der
Klasse erfolgt durch den Methodenaufruf newInstance(new Object[] {initargs}).
Der voll qualifizierte Name der Fassade (nameFassade), die Art des zu übergebenden
Objekts
(paramter)
und
der
Übergabeparameter
für
den
Konstruktor
(contentConstructor) können bspw. durch Verwendung von java.util.Properties aus
einer Konfigurationsdatei gelesen.
public class Fabrik {
/** Quellcode */
private AbstractFassade createDB(String nameFassade) {
if (fassade == null) {
try {
Class fassadeCls = Class.forName(nameFassade);
Class parameterCls = Class.forName(parameter);
Constructor fassadeCons = fassadeCls.getConstructor(
new Class[] {parameterCls});
fassade = (AbstractFassade) fassadeCons.newInstance(
new Object[] {content});
} catch (Exception e) {/** Exception Handling */}
}
return fassade;
}
}
/** beispielhafter Auszug aus der Konfigurationsdatei */
Fassade.Name=wwu.de.inf.SemArbBsp.DynLaden.DBFassade1
Konstruktor.Parameter=java.lang.String
Konstruktor.Inhalt=Dies ist ein Test!
Durch diese Vorgehensweise ist es möglich, Fassadenobjekte zur Laufzeit oder beim
Programmstart einzubinden, die zum Erstellungszeitpunkt der Applikation noch nicht
existierten. Es ist lediglich notwendig, die entsprechende .java Datei der Fassade zu
kompilieren und als .class Datei an die spezifizierte Stelle des Verzeichnispfads zu
hinterlegen und die erforderlichen Einstellungen in der Konfigurationsdatei zu pflegen.
Im Rahmen der vorangegangen Ausführungen wurde ein Überblick zu den vielseitigen
Einsatzmöglichkeiten von Java Reflection gegeben. Diese Flexibilität zur Laufzeit
bietet jedoch auch Angriffspunkte für Intentionen, die weniger schadfrei sind.
Dementsprechend folgt an dieser Stelle eine Beleuchtung der Sicherheitsaspekte im
Zusammenhang mit der Nutzung von Reflection.
13
Kapitel 2: Java Reflection - Grundlagen
2.3 Sicherheitsaspekte bei Java Reflection
Wie in den vorangegangen Kapiteln bereits deutlich geworden ist, werden mit Java
Reflection Methoden zur Verfügung gestellt, mit denen sich Informationen und Inhalte
von Feldern anzeigen oder Methoden ausführen lassen, die über die Modifier protected
oder private verfügen. Um zu verhindern, dass bewährte Sicherheitskonzepte, wie die
Sichtbarkeit [So98, S. 18 f] an Bedeutung verlieren, müssen Schutzmechanismen
geschaffen werden, die den Einsatz von Reflection überwachen und reglementieren.
Die erste Restriktion im Umgang mit Reflection bildet die Einschränkung der Objekte,
die berechtigt sind ein Metaobjekt instanziieren. Um Veränderungen an Feldinhalten,
Instanziierungen von Objekten oder Methodenausführungen vorzunehmen, bedarf es
der Metaobjekte Field, Constructor oder Method, die jedoch nur von Class-Objekten
erzeugt werden können. [JCR05] Dementsprechend stellen Basisobjekte keine
Funktionalität bereit, die eine direkte Erzeugung von Metaobjekten, abgesehen von
Class-Objekten, gestattet.
Ein weiteres Element zum Schutz der Applikation und der Laufzeitumgebung vor
unerwünschten Modifikationen, ist die Verwendung des Java-eigenen SecurityManagers (java.lang.SecurityManager), der beim Start der Applikation explizit geladen
werden muss. Um die einzelnen Stufen der Sicherheit zu erläutern dient als
Beispielcode wiederum ein Auszug der Klasse C sowie der dazu relevante Bestandteil
einer
Beispielapplikation.
Dies
ermöglicht
die
entsprechend
vorgenommene
Konfiguration des Security-Manager und die damit verbundenen Konsequenzen zu
erläutern.
/** Auszug aus der Klasse C.java */
private String stringValue;
private int getInt(int i, String s) {/** Quellcode */}
/** Auszug aus der Applikation.java */
public static final boolean ACCESS = true;
classA = Class.forName("C");
/** Ein Field-Objekt des Feldes "stringValue" bekommen */
Field field = classA.getField("stringValue");
Field declaredField = classA.getDeclaredField("stringValue");
/** Ein Method-Objekt der Methode "getInt" bekommen */
Method method = classA.getMethod("getInt",
new Class[] {int.class, String.class});
Method declaredMethod = classA.getDeclaredMethod("getInt",
new Class[] {int.class, String.class});
14
Kapitel 2: Java Reflection - Grundlagen
if (!Modifier.isPublic(declaredField.getModifiers()) ||
(!Modifier.isPublic(declaredMethod.getModifiers()))) {
declaredField.setAccessible(ACCESS);
declaredMethod.setAccessible(ACCESS);
}
Object obj = clazzA.newInstance();
System.out.println(declaredField.getInt(obj));
declaredMethod.invoke(obj, new Object[] {new Integer(5),
new String("Test")});
Die Ausführung des Programmteils führt bei getField() und bei getMethod() jeweils zu
einer java.lang.NoSuchFieldException oder java.lang.NoSuchMethodException, da das
gesuchte Feld und die gesuchte Methode jeweils mit der Sichtbarkeitsstufe private
deklariert wurden. Aus dem Aufruf getDeclaredField() sowie getDeclaredMethod()
resultieren jedoch unabhängig vom Modifier ein Field- oder ein Method-Objekt
[FoFo04, S. 11 und S. 32], welche in bereits vorgestellter Weise verwendet werden
können.
Die Standardkonfiguration des Java-eigenen Security-Manager verbietet grundsätzlich
alle weiteren Reflectionaktivitäten, die bspw. über ein Anzeigen der deklarierten Felderund
Methodennamen
(declaredField.getInt(obj))
hinaus
gehen.
auszulesen
Sowohl
als
auch
der
die
Versuch
Methode
das
zu
Feld
starten
(declaredmethod.invoke([…]) resultiert in einer java.lang.IllegalAccessException.
Verhindert wird diese Nutzung durch das auf false gesetzte Accessible-Flag, da sowohl
Method als auch Field die übergeordnete Klasse java.lang.reflect.AccessibleObject
besitzen. AccessibleObject implementiert das Accessible-Flag, welches im Rahmen der
Zugriffskontrolle vom Security-Manager verwendet wird. Ein Aktivieren (=true) des
Accessible-Flag durch setAccessible(boolean b) ermöglicht einen unbeschränkten
Zugriff auf das jeweilige Basisobjekt. [FoFo04, S. 38 f] Dieses Setzen des Flags wird
jedoch
vom
Security-Manager
verhindert,
sodass
der
Versuch
mit
einer
java.security.AccessControlException endet.
Wird die Konfigurationsdatei (hier java.property) des Security-Managers um den
folgenden Eintrag ergänzt, kann das Accessible-Flag aktiviert werden.
/** Auszug aus der java.property */
grant {
permission java.lang.reflect.ReflectPermission
"suppressAccessChecks";
};
15
Kapitel 2: Java Reflection - Grundlagen
Die Änderung der Konfiguration erfordert einen Neustart der Applikation oder
zumindest des Security-Managers, wobei letzteres im Rahmen der Standardsicherheitskonfiguration nicht möglich sein sollte. Der entsprechend dem oben
aufgeführten Auszug der java.property konfigurierte Security-Manager erlaubt das
Setzen des Accessible-Flags, welches wiederum die Grundlage für ein Ausführen der
Methode und Auslesen des Feldes darstellt. Darüber hinaus ist eine detaillierte
Konfiguration der ReflectPermission für einzelne Pakete oder Klassen möglich.
Zusammenfassend lässt sich sagen, dass mehrere Sicherheitsebenen existieren, die eine
schadhafte Verwendung von Reflection verhindern. Durch die Definition von
Sichtbarkeiten (public, protected, private) der Methoden und Felder, werden diese vor
unkontrollierten Zugriff geschützt. Auf dieser Ebene ist es jedoch möglich, Namen
deklarierter Felder und Methoden zu erfahren. Eine weiterer Schutz vor ungewollter
Nutzung und Ausführung der Programmteile wird durch das AccessibleObject mit dem
Accessible-Flag geboten. Dieses Flag wird bei reflexiven Zugriffsversuchen durch den
Sicherheitsmanager geprüft und verhindert ggf. den Zugriff. Darüber hinaus kann der
Security-Manager den eigenen Bedürfnissen entsprechend konfiguriert werden, sodass
dennoch eine reflexive Programmierweise zur Steigerung der Applikationsflexibilität
ermöglicht wird. [JCR05]
16
Kapitel 3: Beispielhafte Ausführungen zum Thema Reflection
3 Beispielhafte Ausführungen zum Thema Reflection
3.1 Reflection in der Praxis – JUnit
Das
Testen
von
Applikationen
stellt
einen
integralen
Bestandteil
heutiger
Programmierarbeit dar. Aus diesem Grund wurde mit JUnit [JU05] ein Test-Framework
geschaffen, um Testfälle zu formulieren. Diese vergleichen die Ergebnisse der
bereitgestellten Funktionalität einer Applikation in automatisierter Weise mit den
erwarteten Ergebnissen und identifizieren Methoden, deren Resultate von den
Erwartungen abweichen. Für Vertreter der agilen Softwareentwicklung ist die
Bedeutung von JUnit unbestritten, da die gestellten Anforderungen an die Software
durch Testfälle prüfbar sind und auch nach Änderungen bereits implementierte
Funktionalität garantieren.
Für die Erstellung und Ausführung von erstellten Testfällen, ist ein gewisser Grad an
Flexibilität notwendig, der durch den Einsatz reflexiver Programmierung bereit gestellt
werden kann. Aus diesem Grund werden exemplarisch zwei Stellen im JUnitFramework aufgezeigt, an denen der Einsatz von Reflection deutlich wird.
Testfälle
dienen
im
Zusammenhang
mit
einigen
agilen
Vertretern
zur
Softwareentwicklung (bspw. eXtreme Programming) als Rahmen zur Implementierung
der Funktionalität. Dementsprechend ist es notwendig, die Definition und Ausführung
von Tests auf möglichst bequeme Art und Weise zur Verfügung zu stellen. Um die
spezifizierten Testfälle automatisch auszuführen, sind bei der Spezifikation der Testfälle
bestimmte Implementierungsbedingungen zu berücksichtigen. So muss bspw. die
Klasse, welche die Testmethoden enthält, von junit.framework.TestCase erben. Darüber
hinaus erfordern die Testmethoden den Präfix „test“, um identifiziert werden zu können.
Zu Beginn der Testausführung werden alle Methoden, die den Präfix „test“ besitzen auf
ihre Eignung geprüft und in einer Testsuite zusammengetragen.
/** Auszug aus TestSuite.java */
while (Test.class.isAssignableFrom(superClass)) {
Method[] methods= superClass.getDeclaredMethods();
for (int i= 0; i < methods.length; i++) {
addTestMethod(methods[i], names, theClass);
}
superClass= superClass.getSuperclass();
}
17
Kapitel 3: Beispielhafte Ausführungen zum Thema Reflection
Nachdem alle Testmethoden in einer Testsuite gesammelt wurden, ist ein weiterer
Bestandteil des Frameworks die Möglichkeit, formulierte Testfälle automatisch zu laden
und auszuführen.
/** Auszug aus TestCase.java */
protected void runTest() throws Throwable {
assertNotNull(fName);
Method runMethod= null;
try {
runMethod= getClass().getMethod(fName, null);
} catch (NoSuchMethodException e) {
fail("Method \""+fName+"\" not found");
}
if (!Modifier.isPublic(runMethod.getModifiers())) {
fail("Method \""+fName+"\" should be public");
}
try {
runMethod.invoke(this, new Class[0]);
} catch (Exception e) {/** Exception Handling */}
}
Es wird ersichtlich, dass JUnit die Methode getDeclaredMethods() nutzt, um alle
Methoden des spezifizierten Testfalls zu laden. Im Anschluss daran werden die
Methoden auf das Vorhandensein des Präfixes „test“ und hinsichtlich der notwendigen
öffentlichen Sichtbarkeit (public) geprüft. Ziel ist es alle deklarierten Testfälle und
Testmethoden in einer TestSuite zusammenzufassen und auszuführen. (Vgl. hierzu
Anhang C) Durch invoke() werden schließlich alle Testmethoden aufgerufen und das
Ergebnis des Tests präsentiert.
3.2 Reflection in der Anwendung – Ein EventDispatcher
Die grafische Ereignisprogrammierung in Java unter Verwendung der durch
java.awt.event.* bereitgestellten Klassen gestaltet sich relativ aufwendig. (Vgl. Anh. D)
Die Listener, die für das Event-Handling installiert werden, haben i. d. R. nur eine
Adapter-Funktionalität – sie delegieren die eigentliche Arbeit an ein anderes Objekt,
welches die eigentliche Methode für das Event-Handling beinhaltet. Bevor diese
Delegation stattfinden kann, müssen jedoch alle Ereignisquellen bei den entsprechenden
Ereignissenken registriert werden. Da beim Abschluss der Programmierarbeiten alle
Ereignisbehandlungen bekannt sind, ist es denkbar, eine zentrale Instanz einzurichten,
welche die Registrierung zum Programmstart automatisiert vornimmt. Diese zentrale
Instanz wird im Weiteren als EventDispatcherCreator bezeichnet.
18
Kapitel 3: Beispielhafte Ausführungen zum Thema Reflection
Weiterhin ist es denkbar, die eigentliche Ereignisbehandlung austauschbar zu gestalten,
sodass ein Austausch von class-Dateien eine andere Reaktion der Applikation
hervorruft.
Abbildung 4: Klassendiagramm des EventDispatcher
Im folgenden soll unter Verwendung von Reflection eine Lösung skizziert werden, die
eine Registrierung von Adapterinstanzen bei den Eventquellen automatisiert vornimmt.
Um diesen Automatismus zu garantieren, sind zunächst formale Kriterien zur
Benennung der Methoden für die Ereignisbehandlung zu treffen. Aus dem
Methodennamen muss die auslösende Komponente und die Ereignisart ermittelt werden
können. Wird bspw. das Drücken eines Knopfes auf der Oberfläche als Ereignis
gewünscht,
bezeichnet
sich
die
Methode
zur
Ereignisbehandlung
als
eine
Zusammensetzung aus dem Namen des Knopfes (bGreen) und dem Ereignistyp
(actionPerformed).
/** Auszug aus Application.java */
private Button bGreen = new Button("green");
/** Quellcode */
private void bGreen_actionPerformed(ActionEvent e) {
this.setBackground(Color.green);
}
Beim
Start
des
Programms
werden
durch
die
Implementierung
des
EventDispatcherCreator alle Felder und Methoden der Klasse ausgelesen und geprüft,
inwieweit eine Ereignisregistrierung möglich und notwendig ist. Durch die Methode
19
Kapitel 3: Beispielhafte Ausführungen zum Thema Reflection
Component.class.isAssignableFrom(Class cls) werden die zu berücksichtigenden
Elemente eingeschränkt auf Objekte, die von java.awt.Component erben. [JADC05]
/** Auszug aus EventDispatcherCreator.java */
public EventDispatcherCreator(Object target) {
this.target = target;
this.methods = target.getClass().getDeclaredMethods();
this.fields = target.getClass().getDeclaredFields();
for (Field field: fields) {
if (!Component.class.isAssignableFrom(field.getType()))
continue;
for (Method method: methods) {
try {
tryToAddListener(field, method);
/** Quellcode */
}
}
}
}
In der Methode tryToAddListener(Field f, Method m) wird die Registrierung des
EventListener bei der entsprechenden Komponente vorgenommen, um Ereignisquelle
und das bei Ausführung gewünschte Event-Handling zu verknüpfen.
/** Auszug aus EventDispatcherCreator.java */
Method addMethod = obj.getClass().getMethod(
md.getAddMethodName(), new Class[] { md.getIface() });
/** EventMetaData md – Objekt, welches Metadaten über
die Actionklasse beinhaltet */
if (addMethod == null)
return;
Class cls = Class.forName("reflection5." + md.getEventName() +
"EventDispatcher");
Constructor constr = cls.getConstructor(
constructorParamTypes);
Object dispatcher = constr.newInstance(new Object[] {
this.target, field.getName() });
addMethod.invoke(obj, new Object[] { dispatcher });
Im Rahmen der Ereignisbehandlung werden die in Java auftretenden Ereignisse
unterschiedlichen Ereignisklassen zugewiesen. So setzt bspw. die Verwendung des
Interfaces java.awt.event.ActionListener die Implementierung von Methoden , die u. a.
nach dem Drücken eines Knopfes ausgeführt werden. Damit letztendlich die Methode
zur Ereignisbehandlung ausgeführt wird, ist in der Klasse EventDispatcher die Methode
callHandler(String methodName, ActionEvent e) implementiert.
20
Kapitel 3: Beispielhafte Ausführungen zum Thema Reflection
/** Auszug aus ActionEventDispatcher.java */
public void actionPerformed(ActionEvent e) {
this.callHandler("actionPerformed", e);
}
Der Methode callHandler() wird hierfür der auszuführende Methodennamen zur
Ereignisbehandlung und ein Objekt des Typs java.awt.event.ActionEvent übergeben.
Die Ausführung der Methode geschieht durch die Verwendung der eingangs
vorgestellten Methode invoke() des Metaobjekts Class.
/** Auszug aus EventDispatcher.java */
protected void callHandler(String methodName, EventObject e) {
try {
methodName = this.name + "_" + methodName;
Method method = this.target.getClass().getDeclaredMethod(
methodName, new Class[] {this.eventClass });
method.setAccessible(true);
method.invoke(this.target, new Object[] { e });
} catch (Exception e) {/** Exception Handling */}
}
myFrame
bGreen
actionEventDispatcher
eventDispatcher
actionPerformed(ActionEvent e)
callHandler(String m, ActionEvent e)
bGreen_actionPerformed(ActionEvent e)
Abbildung 5: Sequenzdiagramm für den EventDispatcher
Das vorliegende Sequenzdiagramm skizziert den Ablauf der Ereignisbehandlung.
Der zweite Teil der Ausführungen zur Verwendung reflexiver Programmierung
skizziert die Möglichkeit die Ereignisbehandlung austauschbar zu gestalten. Hierbei
sind unterschiedliche Herangehensweisen denkbar. Wird davon ausgegangen, dass die
hier EventlClass genannte Klasse zur Ereignisbehandlung immer den gleichen Aufbau
und Namen besitzt, kann EventClass.class verwendet werden. Ist der Name der
EventClass zum Zeitpunkt der Kompilierung jedoch noch nicht bekannt, hilft
Class.forName(String s) und das vorhergehende Auslesen einer Konfigurationsdatei,
um den Klassennamen und Pfad erst zur Laufzeit zu spezifizieren. Ebenso kann die
21
Kapitel 3: Beispielhafte Ausführungen zum Thema Reflection
Festlegung der auszuführenden Methode (eventMethod) der EventClass auf diesem
Weg erfolgen. Im folgenden Beispiel wird die Methode eventMethod der EventClass
verwendet, welche eine Referenz auf ein Fenster vom Typ java.awt.Frame erfordert.
Diese Referenz kann zu Änderungen des Erscheinungsbildes des Fensters verwendet
werden und nach der Ereignisauslösung bspw. in einer Änderung der Hintergrundfarbe
resultieren.
private void bYellow_actionPerformed(ActionEvent e) {
try {
Class eventClass = null;
Object eventObject = null;
Method eventMethod = null;
eventClass = Class.forName("EventClass");
//eventClass = EventClass.class;
eventObject = eventClass.newInstance();
eventMethod = eventClass.getDeclaredMethod("eventMethod",
new Class[] {Frame.class});
eventMethod.setAccessible(true);
eventMethod.invoke(eventObject, new Object[] {this});
} catch (Exception e) {/** Exception Handling */}
}
Dieses Vorgehen erlaubt ein Austausch oder Hinzufügen von class-Dateien zur
Laufzeit, die zu einer geänderten Ereignisbehandlung führen.
22
Kapitel 4: Java Reflection – Flexibilität für die Applikation
4 Java Reflection – Flexibilität für die Applikation
Durch
den
Einsatz
von
Java
Reflection
ist
es
möglich,
Struktur-
und
Statusinformationen von Applikationen zur Laufzeit zu erfahren, die dazu verwendet
werden können in flexibler Weise funktionale Ergänzungen und Änderungen der
Applikation vorzunehmen.
In der vorliegenden Arbeit wurden die Grundlagen aufgezeigt, die eine Verwendung
von Reflection auszeichnet. Bedingt durch die Flexibilität, die Reflection im
Zusammenhang mit der Anwendung bietet, wurden Sicherheitsaspekte diskutiert,
inwieweit eine schadhafte Ausführung und Nutzung der Applikation möglich ist. Es
wurde festgestellt, dass die Regelungen zur Sichtbarkeit von Methoden und Feldern
einzelner Klassen durch die Verwendung des Java-eigenen Security-Managers ergänzt
werden, der jegliche Methodenaufrufe prüft und nicht autorisierte Aktionen verhindert.
Im zweiten Teil der Ausarbeitung wurde zunächst ein Anwendungsbereich des Java
Reflection skizziert, der sich speziell im Bereich der agilen Softwareentwicklung
enormer Beliebtheit erfreut. JUnit, ein Test-Framework, ermöglicht die Formulierung
und automatisierte Ausführung von Testfällen, so dass die Funktionalität der
Applikation sowohl im täglichen Programmieralltag als auch nach umfangreichen
Software-Restrukturierungsmaßnahmen garantiert werden kann. Der zweite skizzierte
Anwendungsbereich
bezog
sich
auf
eine
automatisierte
Registrierung
von
Ereignisbehandlern bei entsprechenden (grafischen) Komponenten in Java. Hierzu
wurde eine Klasse formuliert, die zum einen die entstehende Registrierungsarbeit der
Ereignisbehandler übernimmt und die Ausführung der konkreten Ereignisbehandlung
delegiert. Des Weiteren wurde skizziert, inwieweit Reflection verwendet werden kann,
um den Austausch von Klassen der Ereignisbehandlung zur Laufzeit zu realisieren.
Es ist deutlich geworden, dass der bewusste Einsatz von Java Reflection zu einer
nachhaltigen Steigerung der Applikationsflexibilität führt, der heutzutage notwendig ist,
um Anwendungen dynamisch an die sich ändernden Anforderungen anzupassen, ohne
jedoch sicherheitskritische Angriffspunkte innerhalb der Applikation zu bieten.
23
Anhang B: Titel von Anhang 2
A.
Zeitliche Charakterisierung des Reflection
Abbildung 6: Erstellen der Meta-Architektur zur Kompilierzeit
Abbildung 7: Erstellen der Meta-Architektur beim Laden des Programms
Abbildung 8: Erstellen der Meta-Architektur zur Laufzeit
24
Anhang B: Titel von Anhang 2
B.
Beispielklasse C
public class C {
public int intValue;
public String stringValue;
/** Konstruktor der Klasse C */
public C() {
this.intValue = 4711;
this.stringValue = "Hallo";
}
public void printString() {
System.out.println("printString erfolgreich: " +
this.intValue + " "+ this.stringValue);
}
public int getIntValue(int i, String s) {
System.out.println("getIntValue erfolgreich:
" + i + " " + s);
this.intValue = i;
this.stringValue = s;
return this.intValue;
}
public void setStringValue(String s) {
this.stringValue = s;
}
public void setIntValue(int i) {
this.intValue = i;
}
public String getStringValue() {
return this.stringValue;
}
public int getIntValue() {
return this.intValue;
}
}
25
Anhang B: Titel von Anhang 2
C.
JUnit-Methode isTestMethod(Method m)
/** Auszug aus junit.framework.TestSuite.java */
private boolean isTestMethod(Method m) {
String name = m.getName();
Class[] parameters = m.getParameterTypes();
Class returnType = m.getReturnType();
return parameters.length == 0 && name.startsWith("test")
&& returnType.equals(Void.TYPE);
}
26
Anhang B: Titel von Anhang 2
D. Ereignisbehandlung mit und ohne EventDispatcher
Ereignisbehandlung ohne Verwendung des EventDispatcher
class MyFrame extends Frame {
private Button bSave = new Button ("Save");
/** Quellcode */
public MyFrame () {
this.add (this.bSave);
this.bSave.addActionListener (new ActionListener () {
public void actionPerformed (ActionEvent e) {
bSave_actionPerformed (e);}
});
/** Quellcode */
}
private void bSave_actionPerformed (ActionEvent e) {
/** Event-Handling */
}
}
Ereignisbehandlung mit Verwendung des EventDispatcher
class MyFrame extends Frame {
private Button bSave = new Button ("Save");
/** Quellcode */
public MyFrame () {
this.add (bSave);
new EventDispatcherCreator (this);
/** Quellcode */
}
private void bSave_actionPerformed (ActionEvent e) {
/** Event-Handling */
}
}
27
Literaturverzeichnis
[FoFo04] Ira R. Forman, Nate Forman: Java Reflection in Action, Manning
Publications, 2004.
[GHJ+97] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides:
Entwurfsmuster, Elemente wieder verwendbarer objektorientierter
Software, Addison-Wesley-Longman, 1997.
[JADC05] Java J2SE API Documentation - Component,
http://java.sun.com/j2se/1.5.0/docs/api/index.html,
Zugriffsdatum: 2005-11-13.
[JADCl05] Java J2SE API Documentation – Class,
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html, Zugriffsdatum:
2005-11-13.
[JCR05]
Java Core Reflection - Security Model,
http://java.sun.com/j2se/1.4.2/docs/guide/reflection/spec/javareflection.doc.html, Zugriffsdatum: 2005-11-27.
[JD05]
JDBC technology, http://java.sun.com/products/jdbc/,
Zugriffsdatum: 2005-12-01.
[JU05]
JUnit.org, http://www.junit.org/index.htm, Zugriffsdatum: 2005-11-08.
[Ma94]
Axel Mahler: Variants: Keeping things together and telling them apart. In
Configuration Management, W. F. Tichy, Ed., Vol. 2, Trends in Software,
Wiley, 73–98, 1994.
[MiGo97] Michael Golm: Design and Implementation of a Meta Architecture for Java,
http://www4.informatik.uni-erlangen.de/Projects/PM/Java/DA.ps.gz,
Zugriffsdatum: 2005-11-03.
[SO98]
Scott Oaks: Java Security, O’Reilly & Associates, Inc., 1998.
Herunterladen