Java Önelemzés Dynamikus Proxy Miskolci Egyetem Alkalmazott Informatikai Tanszék 2006 1
Önelemzés A Java virtuális gép futás közben meghatározza, hogy milyen részekből áll egy betöltött java osztály. A passzív vizsgálaton kívül, az osztály módosításra is van lehetőség. A modern J2EE alkalmazásszerverek egyik alapköve az önelemzés lehetősége.
Java Core Reflection API Futás időben elvégezhető műveletek: objektumok létrehozása változók lekérdezése, módosítása metódusok meghívása tömbök manipulációja JDK1.1 verziótól elérhető
Class object kinyerése Class objektum kinyerése egy objektumból: Class c = myObject.getClass(); Class objektum kinyerése egy leszármazottból: TextField t = new TextField(); Class c = t.getClass(); Class s = c.getSuperclass(); Class objektum kinyerése egy class fájl alapján: Class c = java.awt.Window.class; Class objektum kinyerése a név alapján: Class c = Class.forName(strg);
Osztály név meghatározása class DetermineName { public static void main(String[] args) { printName(new JFrame ()); } static void printName(Object o) { Class cls = o.getClass(); String s = cls.getName(); System.out.println(s);
Osztály módosítók class SampleModifier { public static void main(String[] args) { String s = new String(); printModifiers(s); } public static void printModifiers(Object o) { Class c = o.getClass(); int m = c.getModifiers(); if (Modifier.isPublic(m)) System.out.println("public"); if (Modifier.isAbstract(m)) System.out.println("abstract"); if (Modifier.isFinal(m)) System.out.println("final"); }
Interfészek meghatározása class DetermineInterface { public static void main(String[] args) { try { RandomAccessFile r = new RandomAccessFile("myfile", "rw"); printInterfaceNames(r); } catch (IOException e) { } static void printInterfaceNames(Object o) { Class c = o.getClass(); Class[] theInterfaces = c.getInterfaces(); for (int i = 0; i < theInterfaces.length; i++) { String interfaceName = theInterfaces[i].getName(); System.out.println(interfaceName);
Osztály mezőinek felfedezése class DetermineFields { public static void main(String[] args) { JFrame o = new JFrame(); printFieldNames(o); } static void printFieldNames(Object o) { Class c = o.getClass(); Field[] publicFields = c.getFields(); for (int i = 0; i < publicFields.length; i++) { String fieldName = publicFields[i].getName(); Class typeClass = publicFields[i].getType(); String fieldType = typeClass.getName(); System.out.println("Name: " + fieldName + ", Type: " + fieldType);
Objektumok futásidejű létrehozása Konstruktorok dinamikus meghívása: (ha nincs argumentum) Class classDefinition = Class.forName(className); Object object = classDefinition.newInstance(); Általános esetben: Class objektum létrehozása a létrehozandó objektumhoz Constructor object létrehozása getConstructor függvény hívásával. Bemenő paramétere egy tömb, amivel a konstruktor argumentumlistájának típusát lehet meghatározni. newInstance() meghívása a Constructor objektumon. newInstance egyetlen tömb-paramétere a konstruktornak átadandó objektumok tömbje. Példák: org.ait.reflection.NoArgsCreate org.ait.reflection.ArgsCreate
Tagváltozók manipulálása class GetSetFields { public static void main(String[] args) { Rectangle r = new Rectangle(100, 325); printHeight(r); } static void printHeight(Rectangle r) { Field heightField; Integer heightValue; Class c = r.getClass(); try { heightField = c.getField("height"); heightValue = (Integer) heightField.get(r); System.out.println("Height: " + heightValue.toString()); heightField.setInt(r, 123); System.out.println("New Height: " + heightValue.toString()); } catch (Exception e) { System.out.println(e);
Metódusok meghívása class MethodCall { public static void main(String[] args) { String firstWord = "Hello "; String secondWord = "everybody."; String bothWords = append(firstWord, secondWord); System.out.println(bothWords); } public static String append(String firstWord, String secondWord) { String result = null; Class c = String.class; Class[] parameterTypes = new Class[] { String.class }; Method concatMethod; Object[] arguments = new Object[] { secondWord }; try { concatMethod = c.getMethod("concat", parameterTypes); result = (String) concatMethod.invoke(firstWord, arguments); } catch (Exception e) { System.out.println(e); return result;
Smart proxy RMI (Remote Method Invocation) lehetővé teszi, hogy a távoli JVM objektumait elérjünk. távoli referencia (by reference): minden eljáráshívás továbbítva lesz a távoli objektumhoz. Ennek eléréséhez szükséges: interfész implementáció, amely a java.rmi.Remote interfészből származik egy megfelelően létrehozott ‘stub’ osztály, amely ugyanezt az interfészt implementálja érték szerint (by value): egy távoli objektum másolatán végzünk a kliens oldalon műveleteket. Érték szerinti elérés pl., ha a távoli metódus visszatérő értéke egy objektum. Minden ilyen objektumnak implementálnia kell a java.io.Serializable interfészt. A szerver elküldi kliens oldalra a szerializálható osztályt, ami kliens oldalon felhasználható.
Távolról el akarunk érni egy ‘Door’ osztályt, amely rendelkezik két metódussal. Egy doorServer osztályt implementálunk, ami egy hashtáblát tartalmaz. main() függvénye regisztrálja magát az rmiserver-be. DoorImpl implementálja a Door interfészt. DoorClient a névszolgáltatótól eléri a DoorServer interfészen keresztül a távoli objektumot. org.ait.smartproxy.example1
Smart proxy egy olyan osztály, amit a kliens JVM példányosít, és egy referenciát hordoz a távoli objektumra. Implementálja az objektum távoli interfészét és a hívásokat továbbítja a távoli objektum felé. Optimálhatja a kliens szerver hívásokat Kliens oldali cache is lehet A ‘stub’ és a proxy működése hasonló, de a proxy nagyobb mértékű dinamizmust tesz lehetővé. A kliens nem látja különbséget és nem is kell megváltoztatni a kliens oldali kódot. org.ait.smartproxy.example2
Fordítás és futtatás Stub-ok létrehozása: Rmi server indítás: Indítás: rmic org.ait.smartproxy.example1.DoorImpl rmic org.ait.smartproxy.example1.DoorServerImpl Rmi server indítás: start rmiserver Indítás: Server indítása: (önmagát regisztrálja az rmi névszolgáltatóba) start java org.ait.smartproxy.example1.DoorServerImpl Kliens indítása: java org.ait.smartproxy.example1.DoorClient
Dinamikus Proxy API A Java 1.3 bevezette a beépített dinamikus proxy lehetőségét Ez a proxy típus alapvetően interfészek ‘utánzására’ (mimic) alkalmazható A java.lang.reflect.Proxy és java.lang.reflect.InvocationHandler interfész valósítják meg az alapvető funkciókat A proxy általában azt jelenti, hogy a metódus hívások egy köztes proxy objektumokon keresztül valósulnak meg. Jellemző típusai: access proxy, facade, remote proxy, virtual proxy. access proxy: biztonságos elérést teszt lehetővé kliens szerver modell vagy általában adatelérés esetén facade: egy interfészen keresztül érhetünk el több (szerver oldali) objektumot remote proxy: maszkolja (elfedi) azt a tényt, hogy a kliens távoli objektummal dolgozik virtual proxy: ‘lazy’ és Just In Time példányosítása egy valódi objektumnak
+subject:ConcreteSubject Interface Subject Client <<call>> +Request():void SubjectProxy ConcreteSubject +subject:ConcreteSubject +Request():void +Request():void Proxy Design Pattern - UML osztály diagramm
public Class MyDynamicProxyClass implements java. lang. reflect public Class MyDynamicProxyClass implements java.lang.reflect.InvocationHandler { Object obj; public MyDynamicProxy(Object obj) this.obj = obj; } public Object invoke(Object proxy, Method m, Object args[]) throws Throwable try { // business logic catch(InvocationTargetException e) { throw(e); catch(Exception e) { … // az imitálandó interfész public interface MyProxyInterface { public Object MyMethod(); …// használat MyProxyInterface foo = (MyProxyInterface ) Proxy.newProxyInstance(obj.getClass().getClassLoader(), Class[] {MyProxyInterface.class}, new MyDynamicProxyClass(obj));
class Person interface IPerson +getName() +getAddress() +getName() +getAddress() class Employee interface IEmployee +getSalary() +getDepart() +getSalary() +getDepart() class Manager interface IManager +getTitle() +getTitle()
package org.ait.dynamicproxy; import java.util.HashMap; public class Example1 { public static void main(String[] args) { HashMap identity = new HashMap(); IPerson person = (IPerson) ViewProxy.newInstance(identity, new Class[] { IPerson.class }); person.setName("Bob Jones"); // 'identity' átalakítása 'employee'-ra IEmployee employee = (IEmployee) ViewProxy.newInstance(identity, new Class[] { IEmployee.class }); employee.setSSN("111-11-1111"); // IPerson interfész függvény is hívható System.out.println(employee.getName()); }
package org.ait.dynamicproxy; public class ViewProxy implements InvocationHandler { private Map map; public static Object newInstance(Map map, Class[] interfaces) { return Proxy.newProxyInstance(map.getClass().getClassLoader(), interfaces, new ViewProxy(map)); } public ViewProxy(Map map) { this.map = map; public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { String methodName = m.getName(); if (methodName.startsWith("get")) { String name = methodName.substring(methodName.indexOf("get") + 3); return map.get(name); } else if (methodName.startsWith("set")) { String name = methodName.substring(methodName.indexOf("set") + 3); map.put(name, args[0]); return null; } else if (methodName.startsWith("is")) { String name = methodName.substring(methodName.indexOf("is") + 2); return (map.get(name));