Dynamic Java - PowerPoint PPT Presentation

1 / 58
About This Presentation
Title:

Dynamic Java

Description:

Chariot project teams include experienced project managers, business analysts, ... Chariot's extensive Java tools and application framework can give any Java ... – PowerPoint PPT presentation

Number of Views:32
Avg rating:3.0/5.0
Slides: 59
Provided by: newch
Category:
Tags: chariot | dynamic | java

less

Transcript and Presenter's Notes

Title: Dynamic Java


1
Dynamic Java
Classes Without Code
  • Aaron Mulder
  • Chief Technical Officer
  • Chariot Solutions

2
Learning Objectives
  • In this presentation, we'll discuss
  • Dynamic Proxies
  • Dynamic Classes, or generating Java classes
    without source code
  • Where these techniques can be useful

3
About Aaron Mulder
  • Chief Technical Officer at Chariot Solutions
  • Co-author of Professional EJB (Wrox Press, 2001)
  • Presented at JavaOne 2001 JavaOne 2002
  • A member of the JSR-88 Expert Group (J2EE
    Deployment API)
  • Contributed to the JBoss and OpenEJB projects,
    including an implementation of Dynamic Proxies
    for JDK 1.2

4
About Chariot Solutions
  • Chariot Solutions is an IT service provider,
    specializing in J2EE
  • Chariot project teams include experienced project
    managers, business analysts, technical
    architects, and developers
  • Chariot's extensive Java tools and application
    framework can give any Java project a head start
  • Flyers around the room have more information

5
Today's Problem
6
Presentation Agenda
  • Reflection Review
  • Dynamic Proxies
  • Project 1 Tracing JDBC Driver
  • Project 2 EJB 1.1 Client Stubs
  • Dynamic Classes
  • Project 3 EJB 2.0 CMP
  • Wrapup/QA

7
Reflection Review
8
Reflection Review Class
  • java.lang.Class provides information on the
    constructors, methods, fields, interfaces
    implemented, superclass, package, etc. for a class

public class Class Field getFields()
Field getField(String name) Method
getMethods() Method getMethod(String name,
Class params) Constructor
getConstructors() ... getDeclaredXXX(...)
...
9
Reflection Review Method Constructor
  • java.lang.reflect.Method provides information on
    the parameters, return type, access modifiers,
    exceptions, etc. for a method
  • java.lang.reflect.Constructor is very similar

public class Method String getName()
Class getReturnType() Class
getParameterTypes() Class
getExceptionTypes() int getModifiers()
...
10
Reflection Review Field
  • java.lang.reflect.Field provides information on
    the name, type, access modifiers, etc. for a field

public class Field String getName()
Class getType() int getModifiers() ...
11
Reflection Review Modifier
  • java.lang.reflect.Modifier decodes the Modifiers
    property (an int) on methods, fields, etc.

public class Modifier boolean isPublic(int
modifiers) boolean isPrivate(int
modifiers) boolean isAbstract(int
modifiers) boolean isStatic(int modifiers)
boolean isFinal(int modifiers) ...
12
Dynamic Proxies
13
Dynamic Proxies
  • Introduced in JDK 1.3
  • java.lang.reflect.Proxy
  • java.lang.reflect.InvocationHandler
  • Allow you to implement arbitrary interfaces at
    runtime
  • All calls to Proxy methods are dispatched to an
    InvocationHandler for processing
  • Proxies rely heavily on Reflection

14
Dynamic Proxy Diagram
Client
Proxy (impl. interfaces)
Invocation Handler
15
InvocationHandler
  • java.lang.reflect.InvocationHandler has one
    method, which takes as arguments
  • The proxy that was called
  • The Method object representing the method which
    was called
  • The objects which were passed as parameters to
    the method
  • It returns an Object the value to return from
    the method call, or null for void methods
  • It throws Throwable

16
InvocationHandler API
public interface InvocationHandler public
Object invoke(Object proxy,
Method method,
Object args) throws Throwable
17
Proxy
  • java.lang.reflect.Proxy has methods which
  • Create a new proxy class (based on the
    combination of interfaces)
  • Create a new proxy instance (uses a proxy class
    created above, for a specific InvocationHandler)
  • Get the InvocationHandler for a proxy
  • Check whether an arbitrary object is a proxy

18
Proxy API
public class Proxy public Class
getProxyClass(ClassLoader loader,
Class interfaces) public
Object newProxyInstance(
ClassLoader loader, Class
interfaces,
InvocationHandler handler) public
InvocationHandler getInvocationHandler(
Object proxy) public boolean
isProxyClass(Class class)
19
Project 1 Tracing JDBC Driver
  • What if you want a log of all SQL statements
    executed?
  • But you don't want to add code everywhere you
    issue a JDBC command (or the app server is doing
    all the JDBC for you)
  • And you want to be able to turn it on or off via
    config files
  • And you want to see all the values in place in
    every PreparedStatement

20
Why Dynamic Proxies Fit
  • The JDBC API is made up entirely of interfaces
  • Which driver is used is (typically) controlled by
    config files
  • One driver can wrap another
  • Implementing all the interfaces is a ton of code
    (100s of methods), when we only want to act on a
    couple of them (executeQuery), and pass the rest
    directly to the wrapped instance

21
JDBC Driver Class
public boolean acceptsURL(String url)
return url.startsWith("jdbctrace") public
Connection connect(String url,
Properties info) String driver
info.getProperty("driver") if(driver ! null
!loaded.contains(driver)) // Load the
driver String realUrl "jdbc"url.substring(
11) Connection con DriverManager.getConnect
ion( realUrl,
info) return (Connection)Proxy.newProxyInstan
ce( getClass().getClassLoader(),
new Classjava.sql.Connection.class,
new ConnectionHandler(con))
22
Connection Proxy Diagram
Client
Proxy (impl. Connection)
Invocation Handler
Real Connection
23
ConnectionHandler Class
private Connection con ... public Object
invoke(Object proxy, Method method,
Object args) throws Throwable
if(method.getName().equals("isClosed"))
return con null if(con null)
throw new SQLException("Con. closed!")
Method conMeth con.getClass().getMethod(
method.getName(),
method.getParameterTypes())
Object result conMeth.invoke(con, args)
if(method.getName().equals("close"))
con null ... // To Be Continued!
24
ConnectionHandler, continued
if(method.getName().equals("createStatement"))
return (Statement)Proxy.newProxyInstance
( getClass().getClassLoader(),
new Classjava.sql.Statement.class,
new StatementHandler((Statement)result))
if(method.getName().equals("prepareStatement
")) return (PreparedStatement)
Proxy.newProxyInstance(
getClass().getClassLoader(), new
Classjava.sql.PreparedStatement.class,
new PreparedStatementHandler(
(PreparedStatement)result,
(String)args0) // this is the SQL
) return result
25
StatementHandler
private Statement st ... public Object
invoke(Object proxy, Method method,
Object args) throws Throwable
if(method.getName().equals("executeQuery")
method.getName().equals("executeUpdate"))
log(args0) // args0 is the SQL!
Method meth st.getClass().getMethod(
method.getName(),
method.getParameterTypes()) Object
result meth.invoke(st, args)
if(method.getName().equals("close")) st
null return result
26
PreparedStatementHandler
private PreparedStatement st private String
sql private Map values new HashMap() ... publi
c Object invoke(Object proxy, Method method,
Object args) throws Throwable
if(method.getName().startsWith("set"))
values.put(args0, args1) // args0
index, args1 value
if(method.getName().startsWith("execute"))
log(writeValues(sql, values))
values.clear() // execute the method
on the underlying PS // return the
result private static String writeValues(String,
Map)...
27
Sample JDBC Driver Output
  • JDBC URL jdbcdriver...
  • no output
  • JDBC URL jdbctracedriver...

SELECT WL0.check_date, WL0.company_no,
WL0.create_datetime, WL0.create_username,
WL0.cutoff_date, WL0.due_date_flag,
WL0.payables_selected, WL0.update_datetime,
WL0.update_username FROM dbo.ap_selection WL0
WHERE (WL0.company_no 6)
28
Project 2 EJB Client Stubs
  • EJB Architecture
  • Client deals with remote interface
  • Container must implement remote interface, handle
    RMI and translate calls to run against a bean
    instance
  • Bean instance isn't remote and doesn't implement
    remote interface, so container must create some
    "glue" code

Bean Instance
Client
Remote Interface
Container
What implements this?
29
Solution 1 "That Other App Server"
  • At deployment time, container generates a class
    which is remote, implements the remote interface,
    and dispatches calls back to the container
  • Java source code is written to disk
  • Java compiler is run (in another process)
  • Source code is deleted
  • Class files are loaded from disk
  • Deploying lots of beans takes... a long time.

30
Generated Code Diagram
Client
Generated Remote Bean Stub
Remote Bean Impl
Container
31
Solution 2 JBoss Dynamic Proxies
  • A Proxy is generated for each bean to implement
    the EJB's Remote Interface
  • One prewritten InvocationHandler class is used
    for all beans of a given type (Entity, Stateless
    Session, etc.)
  • The InvocationHandler has a reference to the
    Container (as a remote object)
  • The InvocationHandler parameterizes the Method
    and sends it with the arguments to the Container
    for processing

32
JBoss Proxy Diagram
Client
Proxy (impl EJB Remote Interface)
Invocation Handler
Container Remote Stub
Container Impl
33
Sample InvocationHandler Code
public Object invoke(Object proxy, Method method,
Object args) throws
Throwable if(proxy instanceof EJBHome)
return Container.executeHome(proxy,
encode(method), args)
else return Container.executeRemote(prox
y, encode(method),
args)
  • Real code lives in org.jboss.ejb.plugin.jrmp for
    JBoss 2.x (but the classes named "Proxy" are
    really the InvocationHandlers)
  • Method objects regrettably aren't Serializable

34
Dynamic Proxy Review
  • Dynamic Proxies can be used to implement
    arbitrary interfaces at runtime
  • The client casts the Proxy to an instance of one
    of the implemented interfaces
  • An InvocationHandler handles all calls to the
    Proxy
  • Dynamic Proxies can't be used to extend existing
    classes

35
Dynamic Classes
36
Dynamic Classes
  • The hardcore solution
  • Involves assembling bytecode into classes
  • Requires an understanding of bytecode
    instructions (essentially, assembly language for
    the Java Virtual Machine)
  • However, existing bytecode libraries can help
    manage the worst parts of it

37
Typical Bytecode
public static final int fac(int n) return
(n 0)? 1 n fac(n - 1) 0 iload_0 1
ifne 8 4 iconst_1 5 goto
16 8 iload_0 9 iload_0 10 iconst_1 11
isub 12 invokestatic SomeClass.fac (I)I
(12) 15 imul 16 ireturn
38
Alternatives to Dynamic Classes
  • Write Java code, run a compiler, delete the code,
    load the class, blah, blah, blah...
  • Interpret some other minimal "language" regular
    expressions, for example
  • The alternatives are usually somewhat clearer
    (i.e. not bytecode), but bigger and slower
  • If you do proceed... use a library
  • BCEL _at_ http//jakarta.apache.org/bcel/
  • gnu.bytecode _at_ http//sources.redhat.com/kawa/api/
    gnu/bytecode/

39
BCEL (Byte Code Engineering Library)
  • Includes numerous helper classes
  • ClassGen, for creating a class
  • MethodGen, for creating a method
  • FieldGen, for creating a field
  • ConstantPoolGen, for managing the Constant Pool
    (a necessary feature of Java classes)
  • InstructionList, for assembling instructions
  • Constant, a list of helpful constants

40
Project 3 EJB 2.0 CMP
  • EJB 2.0 CMP entity bean instances are abstract
    classes

public abstract class UserBean implements
EntityBean public abstract int
getUserID() public abstract void
setUserID(int userID) public abstract
String getUsername() public abstract void
setUsername(String name) public abstract
String getPassword() public abstract void
setPassword(String pw) ...
  • Container must generate a subclass in order to
    instantiate bean instances

41
Solution 1 Write Code, Run Compiler, Load
Class...
  • This should be familiar by now

42
Solution 2 Extend Dynamic Proxies
  • JBoss 3.x uses this approach
  • Must re-implement Dynamic Proxies in order to
    extend their capabilities
  • Add the ability to extend an abstract class in
    addition to implementing interfaces
  • Abstract method calls are passed to the
    InvocationHandler just like calls to interface
    methods
  • JBoss uses BCEL to do this, but it's way too
    complicated to go into here

43
Solution 3 Generate a Concrete Subclass
  • Generate a dynamic class which extends the
    abstract bean instance class
  • Lets you hardcode more container-specific
    behavior as compared to extending Dynamic Proxies
    (i.e. a modified flag)
  • The subclass can look pretty much like an EJB 1.x
    bean (with CMR and other new features, of course)

44
Dynamic Class Procedure
  • Figure out what the generated class should look
    like
  • Write Java code for an example desired class (in
    this case, create the abstract superclass too)
  • Write a test class to test all the features of
    the output class, using Reflection
  • Run "javap -c" on the desired class to see its
    bytecode
  • Write the BCEL code to produce the output class

45
Desired Java Code
public class UserBeanTemplate extends UserBean
// Custom instance variables private
boolean modified false // Instance
variables for CMP fields public int userID
public String username public String
password public String fullName public
String email public Timestamp createDate
// Methods for CMP fields public int
getUserID() return userID public void
setUserID(int userID) this.userID
userID modified true public
String getUsername() return username
public void setUsername(String username) ...
46
Test Class
private Class outputCls ...
testConstructor() testCMPFields()
testCMPMethods() ... private void
testConstructor() throws BadClassEx try
Constructor con outputCls.getConstructor(
new Class...)
if(!Modifier.isPublic(con.getModifiers()))
throw new BadClassEx("...")
catch (NoSuchMethodException e) throw
new BadClassEx("...") catch
(SecurityException e) throw new
BadClassEx("...")
47
BCEL Preparing To Create A Class
  • In order to generate a class we need
  • The name of the new class
  • The name of the superclass
  • The name of the source file it came from (we'll
    make something up)
  • The modifiers for the class (public, etc.)
  • The names of any interfaces the class implements

48
BCEL Initializing a class
private Class source private ClassGen
clsGen private ConstantPoolGen pool private
InstructionList il private Map fields private
String subclassName ... subclassName
source.getName()"Impl" clsGen new
ClassGen(subclassName,
source.getName(),
"ltgeneratedgt", Constants.ACC_PUBLIC
Constants.ACC_FINAL,
new String0) pool clsGen.getConstantPool(
) il new InstructionList() fields
new HashMap()
49
BCEL Adding a Field
  • In order to add a field, we need the field
    modifiers (public, etc.), field type, field name,
    and a reference to the Constant Pool

ClassGen clsGen ... FieldGen fg for(int i0
iltproperties.length i) fg new
FieldGen(Constants.ACC_PUBLIC,
BCELUtilities.getType(propertiesi.type),
propertiesi.name,
pool) Field f fg.getField()
fields.put(propertiesi.name, f)
clsGen.addField(f)
50
BCEL Preparing to Add a Method
  • In order to add a method, we need
  • The method modifiers (public, etc.)
  • The return type
  • The parameter types
  • The parameter names
  • The method name
  • The owning class name
  • The code for the method (including Exception
    handling)
  • A reference to the Constant Pool

51
BCEL Adding a Method
ClassGen clsGen ... InstructionList il
... private void createConstructor()
MethodGen mg new MethodGen(
Constants.ACC_PUBLIC,
Type.VOID, new
Type..., new
String...,
"ltinitgt",
subclassName, il,
pool) il.append(new
ALOAD(0)) il.append(new PUTFIELD(...))
clsGen.addMethod(mg.getMethod())
il.dispose() // InstructionList is reusable
52
BCEL Generating the Bytecode
  • Once you've added the fields and methods, it's
    easy to get the resulting bytecode

ClassGen clsGen ... byte code
clsGen.getJavaClass().getBytes()
  • However, loading the class is an adventure too
  • Default ClassLoaders aren't prepared to load a
    class from a byte array in memory

53
Loading A Dynamic Class
public class DynLdr extends ClassLoader
private Class dynClass private String
clsName public ProxyLoader(ClassLoader
parent, String clsName,
byte code) super(parent)
this.clsName clsName dynClass
defineClass(className, code,
0, code.length) protected
synchronized Class loadClass(
String name, boolean resolve)
throws ClassNotFoundException
if(name.equals(clsName)) return dynClass
return getParent().loadClass(name)
54
Dynamic Class Review
  • It's painful to write in bytecode
  • But it's much faster to generate bytecode
    directly at runtime, compared to generating
    source code and then running the compiler
  • Dynamic classes should only be used when the
    performance advantage is significant
  • Other potential uses include Serialization,
    Regular Expressions, extensions to the Java
    language (Aspects, Generics, etc.), and more

55
Summary
  • Dynamic Java is an excellent tool for
  • avoiding generating compiling Java code
  • avoiding interpreting complex languages
  • avoiding ongoing Reflection at runtime
  • Dynamic Proxies can easily implement interfaces
    at runtime
  • Dynamic Classes are more challenging, but can
    solve more problems as well
  • Use these tools wisely make sure there's a
    substantive advantage

56
One For The Road
Would it make sense to implement a JSP container
using Dynamic Classes?
57
(No Transcript)
58
Slides
  • Slides from this presentation and the complete
    code for all snippets will be available soon at
  • http//www.chariotsolutions.com/phillyjug/
Write a Comment
User Comments (0)
About PowerShow.com