Transcript ppt

Dynamic Code Generation
in Java
Class Loading
• Class loading is the process of transforming a
byte code (e.g., a .class file) into a Java class
• A Java class can be loaded dynamically (i.e.,
during runtime)
• The class is then represented by an object of
class Class
• You can use the method Class.forName("name")
to generate a Class object, given its name
Dynamic Code Invocation
• Using dynamic class loading, we can
dynamically generate a .class file, load it,
instantiate it (class.newInstance()), and invoke its
methods
• Hence, we can dynamically write and invoke
programs
• How can we access a method of an object, if we
do not know its type on compile time?
• One way is to implement a known interface
Our Example
• In the following example, we will invoke the
method run() of a dynamically created object of
type Base
public interface Base { public void run(); }
An Example
public class Invoker {
public static void main(String[] argv) throws Exception {
String code = "public class C implements Base {\n"
+ " public void run() {\n"
+"
System.out.println(\"++++++++++\");\n"
+ " }}";
createClass(code);
Class classB = Class.forName("C");
Base b = (Base)classB.newInstance();
b.run();
}
An Example
public static void createClass(String code)
throws Exception {
OutputStream os =
new FileOutputStream(new File("C.java"));
os.write(code.getBytes());
os.close();
Process p = Runtime.getRuntime().
exec("javac -classpath . C.java");
p.waitFor();
}
}
The Whole Process
write
C.java
compile
C.class
load
Class
classB
ne
wI
ns
ta
n
ce
(
)
code
+++++
run()
Base
b
downcast
Object
b
Assumptions
The later code assumes the following:
• The command javac is known to the System
- e.g., the javac executable is in the PATH variable
• The directory "." is included in the class path of
Java
- Hence, Class.forName("C") will find that class
Class Reloading
String code1 = "public class C implements Base {\n"
+ "public void run() {\n"
+ "System.out.println(\"++++++++++\");\n" + "}}";
String code2 = "public class C implements Base {\n"
+ "public void run() {\n"
+ "System.out.println(\"----------\");\n" + "}}";
What is the problem here?
createClass(code1);
((Base)Class.forName("C").newInstance()).run();
createClass(code2);
((Base)Class.forName("C").newInstance()).run();
The System Class Loader
• Java classes are loaded using a class loader
• Class.forName(name) usually invokes
loadClass(name) of the system class loader
• The system class loader can be accessed by
ClassLoader.getSystemClassLoader()
• Hence, a class can equivalently be loaded by
ClassLoader.getSystemClassLoader().loadClass(name)
Bootstrap Class Loader
• Another class loader that exists in the system is
the bootstrap class loader
• This class loader is built in with the JVM
• This class is used to load built-in JVM classes,
and is used early in the runtime startup
• Classes that you write are usually loaded by the
system class loader, and not by the bootstrap one
Class Loading Method
• Every class loader has a parent class loader, which could
be null
• By default, class loading is done as follows:
- check if a class with the given name has already been loaded
by the same class loader
- check if the parent can load the class
• If parent is null, use the bootstrap class loader
- invoke the method findClass("class-name")
• When you load a class, all referenced classes are
recursively loaded by the same class loader
Back to our Example
• So what should we do to reload a class?
• Answer:
- use a new class loader to load the class C
- make sure that this loader does not have a parent
capable of loading class C
• For that, we can obtain a ClassLoader object by
instantiating java.net.URLClassLoader
URLClassLoader
• A URLClassLoader has an array of URLs
(“http://...”, “file://...”, etc.) of either directories
or JAR files, and it loads classes from the
resources of the URLs
• Constructor: URLClassLoader(URLs,parent)
• We will set the URLs to contain only the URL of
“.”, and the parent to be null
Fixed(?) Example
URL[] urls = {new File(".").toURL()};
createClass(code1);
ClassLoader loader = new URLClassLoader(urls,null);
Class classB = loader.loadClass("C");
What is the problem here?
((Base)classB.newInstance()).run();
createClass(code1);
loader = new URLClassLoader(urls,null);
classB = loader.loadClass("C");
((Base)classB.newInstance()).run();
A Problem
• The interface Base is also being loaded by the
new class loaders
- But the system already has one interface called Base
• Each newly created interface is considered as a
unique interface
• Hence, it is impossible to cast C to Base
Solutions
• Solution 1: to invoke run(), use reflection rather
than down casting
• Solution 2: use the system class loader as a
parent, but call findClass() directly, instead of
loadClass()
- problem: this method is protected
- Solution?
- Solution 2 is given in Exercise 3
Fixed(!) Example
URL[] urls = {new File(".").toURL()};
createClass(code1);
ClassLoader loader = new URLClassLoader(urls,null);
Class classB = loader.loadClass("C");
Method runMethod = classB.getMethod("run", null);
runMethod.invoke(classB.newInstance(),null);
createClass(code2);
classB = new URLClassLoader(urls,null).loadClass("C");
classB.getMethod("run",null).invoke(classB.newInstance(),null);