diff options
author | chiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2003-04-23 17:08:37 +0000 |
---|---|---|
committer | chiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2003-04-23 17:08:37 +0000 |
commit | cdeddfd6fc34a06734f9fa525cf5c7437a6c8fb6 (patch) | |
tree | 0471a4d9b985b11969ecd6f521f660e3d468f1d1 /tutorial/tutorial.html | |
parent | fb431982111b03608b888953f7ed8ba7e98f421c (diff) | |
download | javassist-cdeddfd6fc34a06734f9fa525cf5c7437a6c8fb6.tar.gz javassist-cdeddfd6fc34a06734f9fa525cf5c7437a6c8fb6.zip |
Changed the copyright notices and removed tab characters.
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@9 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
Diffstat (limited to 'tutorial/tutorial.html')
-rw-r--r-- | tutorial/tutorial.html | 1148 |
1 files changed, 588 insertions, 560 deletions
diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html index 3fac03ee..29fcaec4 100644 --- a/tutorial/tutorial.html +++ b/tutorial/tutorial.html @@ -1,560 +1,588 @@ -<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>Javassist Tutorial</title>
- <link rel="stylesheet" type="text/css" href="brown.css">
-</head>
-<body>
-
-<b>
-<font size="+3">
-Getting Started with Javassist
-</font>
-
-<p><font size="+2">
-Shigeru Chiba
-</font>
-</b>
-
-<p><div align="right"><a href="tutorial2.html">Next page</a></div>
-
-<ul>1. <a href="#read">Reading bytecode</a>
-<br>2. <a href="#def">Defining a new class</a>
-<br>3. <a href="#mod">Modifying a class at load time</a>
-<br>4. <a href="#load">Class loader</a>
-<br>5. <a href="tutorial2.html#intro">Introspection and customization</a>
-</ul>
-
-<p><br>
-
-<a name="read">
-<h2>1. Reading bytecode</h2>
-
-<p>Javassist is a class library for dealing with Java bytecode.
-Java bytecode is stored in a binary file called a class file.
-Each class file contains one Java class or interface.
-
-<p>The class <code>Javassist.CtClass</code> is an abstract representation
-of a class file. A <code>CtClass</code> object is a handle for dealing
-with a class file. The following program is a very simple example:
-
-<ul><pre>
-ClassPool pool = ClassPool.getDefault();
-CtClass cc = pool.get("test.Rectangle");
-cc.setSuperclass(pool.get("test.Point"));
-pool.writeFile("test.Rectangle"); // or simply, cc.writeFile()
-</pre></ul>
-
-<p>This program first obtains a <code>ClassPool</code> object,
-which controls bytecode modification with Javassist.
-The <code>ClassPool</code> object is a container of <code>CtClass</code>
-object representing a class file.
-It reads a class file on demand for constructing a <code>CtClass</code>
-object and contains the constructed object until it is written out
-to a file or an output stream.
-
-<p>To modify the definition of a class, the users must first obtain a
-reference to the <code>CtClass</code> object representing that class.
-<code>ClassPool.get()</code> is used for this purpose.
-In the case of the program above, the <code>CtClass</code> object
-representing a class <code>test.Rectangle</code> is obtained from
-the <code>ClassPool</code> object
-and it is assigned
-to a variable <code>cc</code>. Then it is modified so that
-the superclass of <code>test.Rectangle</code> is changed into
-a class <code>test.Point</code>.
-This change is reflected on the original class file when
-<code>ClassPool.writeFile()</code> is finally called.
-
-<p>Note that <code>writeFile()</code> is a method declared in not
-<code>CtClass</code> but <code>ClassPool</code>.
-If this method is called, the <code>ClassPool</code>
-finds a <code>CtClass</code> object specified with a class name
-among the objects that the <code>ClassPool</code> contains.
-Then it translates that <code>CtClass</code> object into a class file
-and writes it on a local disk.
-
-<p>There is also <code>writeFile()</code> defined in <code>CtClass</code>.
-Thus, the last line in the program above can be rewritten into:
-
-<ul><pre>cc.writeFile();</pre></ul>
-
-<p>This method is a convenient method for invoking <code>writeFile()</code>
-in <code>ClassPool</code> with the name of the class represented by
-<code>cc</code>.
-
-<p>Javassist also provides a method for directly obtaining the
-modified bytecode. To do this, call <code>write()</code>:
-
-<ul><pre>
-byte[] b = pool.write("test.Rectangle");
-</pre></ul>
-
-<p>The contents of the class file for <code>test.Rectangle</code> are
-assigned to a variable <code>b</code> in the form of byte array.
-<code>writeFile()</code> also internally calls <code>write()</code>
-to obtain the byte array written in a class file.
-
-<p>The default <code>ClassPool</code> returned
-by a static method <code>ClassPool.getDefault()</code>
-searches the same path as the underlying JVM.
-The users can expand this class search path if needed.
-For example, the following code adds a directory
-<code>/usr/local/javalib</code>
-to the search path:
-
-<ul><pre>
-ClassPool pool = ClassPool.getDefault();
-pool.insertClassPath("/usr/local/javalib");
-</pre></ul>
-
-<p>The search path that the users can add is not only a directory but also
-a URL:
-
-<ul><pre>
-ClassPool pool = ClassPool.getDefault();
-ClassPath cp = new URLClassPath("www.foo.com", 80, "/java/", "com.foo.");
-pool.insertClassPath(cp);
-</pre></ul>
-
-<p>This program adds "http://www.foo.com:80/java/" to the class search
-path. This URL is used only for searching classes belonging to a
-package <code>com.foo</code>.
-
-<p>You can directly give a byte array to a <code>ClassPool</code> object
-and construct a <code>CtClass</code> object from that array. To do this,
-use <code>ByteArrayClassPath</code>. For example,
-
-<ul><pre>
-ClassPool cp = ClassPool.getDefault();
-byte[] b = <em>a byte array</em>;
-String name = <em>class name</em>;
-cp.insertClassPath(new ByteArrayClassPath(name, b));
-CtClass cc = cp.get(name);
-</pre></ul>
-
-<p>The obtained <code>CtClass</code> object represents
-a class defined by the class file specified by <code>b</code>.
-
-
-<p>Since <code>ClassPath</code> is an interface, the users can define
-a new class implementing this interface and they can add an instance
-of that class so that a class file is obtained from a non-standard resource.
-
-<p><br>
-
-<a name="def">
-<h2>2. Defining a new class</h2>
-
-<p>To define a new class from scratch, <code>makeClass()</code>
-must be called on a <code>ClassPool</code>.
-
-<ul><pre>
-ClassPool pool = ClassPool.getDefault();
-CtClass cc = pool.makeClass("Point");
-</pre></ul>
-
-<p>This program defines a class <code>Point</code>
-including no members.
-
-<p>A new class can be also defined as a copy of an existing class.
-The program below does that:
-
-<ul><pre>
-ClassPool pool = ClassPool.getDefault();
-CtClass cc = pool.makeClass("Point");
-cc.setName("Pair");
-</pre></ul>
-
-<p>This program first obtains the <code>CtClass</code> object
-for class <code>Point</code>. Then it gives a new name <code>Pair</code>
-to that <code>CtClass</code> object.
-If <code>get("Point")</code> is called on the <code>ClassPool</code>
-object, then a class file <code>Point.class</code> is read again and
-a new <code>CtClass</code> object for class <code>Point</code> is constructed
-again.
-
-<ul><pre>
-ClassPool pool = ClassPool.getDefault();
-CtClass cc = pool.makeClass("Point");
-CtClass cc1 = pool.get("Point"); // cc1 is identical to cc.
-cc.setName("Pair");
-CtClass cc2 = pool.get("Pair"); // cc2 is identical to cc.
-CtClass cc3 = pool.get("Point"); // cc3 is not identical to cc.
-</pre></ul>
-
-<p><br>
-
-<a name="mod">
-<h2>3. Modifying a class at load time</h2>
-
-<p>If what classes are modified is known in advance,
-the easiest way for modifying the classes is as follows:
-
-<ul><li>1. Get a <code>CtClass</code> object by calling
- <code>ClassPool.get()</code>,
- <li>2. Modify it, and
- <li>3. Call <code>ClassPool.write()</code> or <code>writeFile()</code>.
-</ul>
-
-<p>If whether a class is modified or not is determined at load time,
-the users can write an event listener so that it is notified
-when a class is loaded into the JVM.
-A class loader (<code>java.lang.ClassLoader</code>) working with
-Javassist must call <code>ClassPool.write()</code> for obtaining
-a class file. The users can write an event listener so that it is
-notified when the class loader calls <code>ClassPool.write()</code>.
-The event-listener class must implement the following interface:
-
-<ul><pre>public interface Translator {
- public void start(ClassPool pool)
- throws NotFoundException, CannotCompileException;
- public void onWrite(ClassPool pool, String classname)
- throws NotFoundException, CannotCompileException;
-}</pre></ul>
-
-<p>The method <code>start()</code> is called when this event listener
-is registered to a <code>ClassPool</code> object.
-The method <code>onWrite()</code> is called when <code>write()</code>
-(or similar methods) is called on the <code>ClassPool</code> object.
-The second parameter of <code>onWrite()</code> is the name of the class
-to be written out.
-
-<p>Note that <code>start()</code> or <code>onWrite()</code> do not have
-to call <code>write()</code> or <code>writeFile()</code>. For example,
-
-<ul><pre>public class MyAnotherTranslator implements Translator {
- public void start(ClassPool pool)
- throws NotFoundException, CannotCompileException {}
- public void onWrite(ClassPool pool, String classname)
- throws NotFoundException, CannotCompileException
- {
- CtClass cc = pool.get(classname);
- cc.setModifiers(Modifier.PUBLIC);
- }
-}</pre></ul>
-
-<p>All the classes written out by <code>write()</code> are made public
-just before their definitions are translated into an byte array.
-
-<p><center><img src="overview.gif" alt="overview"></center>
-
-<p>The two methods <code>start()</code> and <code>onWrite()</code>
-can modify not only a <code>CtClass</code> object specified by
-the given <code>classname</code> but also
-<em>any</em> <code>CtClass</code> objects contained
-in the given <code>ClassPool</code>.
-They can call <code>ClassPool.get()</code> for obtaining any
-<code>CtClass</code> object.
-If a modified <code>CtClass</code> object is not written out immediately,
-the modification is recorded until that object is written out.
-
-<p><center><img src="sequence.gif" alt="sequence diagram"></center>
-
-<p>To register an event listener to a <code>ClassPool</code>,
-it must be passed to a constructor of <code>ClassPool</code>.
-Only a single event listener can be registered.
-If more than one event listeners are needed, multiple
-<code>ClassPool</code>s should be connected to be a single
-stream. For example,
-
-<ul><pre>Translator t1 = new MyTranslator();
-ClassPool c1 = new ClassPool(t1);
-Translator t2 = new MyAnotherTranslator();
-ClassPool c2 = new ClassPool(c1, t2);</pre></ul>
-
-<p>This program connects two <code>ClassPool</code>s.
-If a class loader calls <code>write()</code> on <code>c2</code>,
-the specified class file is first modified by <code>t1</code> and
-then by <code>t2</code>. <code>write()</code> returns the resulting
-class file.
-
-First, <code>onWrite()</code> on <code>t1</code> is called since
-<code>c2</code> obtains a class file by calling <code>write()</code>
-on <code>c1</code>. Then <code>onWrite()</code> on <code>t2</code>
-is called. If <code>onWrite()</code> called on <code>t2</code>
-obtains a <code>CtClass</code> object from <code>c2</code>, that
-<code>CtClass</code> object represents the class file that
-<code>t1</code> has modified.
-
-<p><center><img src="two.gif" alt="two translators"></center>
-
-<p><br>
-
-<a name="load">
-<h2>4. Class loader</h2>
-
-<p>Javassist can be used with a class loader so that bytecode can be
-modified at load time. The users of Javassist can define their own
-version of class loader but they can also use a class loader provided
-by Javassist.
-
-<p><br>
-
-<h3>4.1 Using <code>javassist.Loader</code></h3>
-
-<p>Javassist provides a class loader
-<code>javassist.Loader</code>. This class loader uses a
-<code>javassist.ClassPool</code> object for reading a class file.
-
-<p>For example, <code>javassist.Loader</code> can be used for loading
-a particular class modified with Javassist.
-
-<ul><pre>
-import javassist.*;
-import test.Rectangle;
-
-public class Main {
- public static void main(String[] args) throws Throwable {
- ClassPool pool = ClassPool.getDefault();
- Loader cl = new Loader(pool);
-
- CtClass ct = pool.get("test.Rectangle");
- ct.setSuperclass(pool.get("test.Point"));
-
- Class c = cl.loadClass("test.Rectangle");
- Object rect = c.newInstance();
- :
- }
-}
-</pre></ul>
-
-<p>This program modifies a class <code>test.Rectangle</code>. The
-superclass of <code>test.Rectangle</code> is set to a
-<code>test.Point</code> class. Then this program loads the modified
-class into the JVM, and creates a new instance of the
-<code>test.Rectangle</code> class.
-
-<p>The users can use a <code>javassist.Translator</code> object
-for modifying class files.
-Suppose that an instance of a class <code>MyTranslator</code>,
-which implements
-<code>javassist.Translator</code>, performs modification of class files.
-To run an application class <code>MyApp</code> with the
-<code>MyTranslator</code> object, write a main class:
-
-<ul><pre>
-import javassist.*;
-
-public class Main2 {
- public static void main(String[] args) throws Throwable {
- Translator t = new MyTranslator();
- ClassPool pool = ClassPool.getDefault(t);
- Loader cl = new Loader(pool);
- cl.run("MyApp", args);
- }
-}
-</pre></ul>
-
-<p>To run this program, do:
-
-<ul><pre>
-% java Main <i>arg1</i> <i>arg2</i>...
-</pre></ul>
-
-<p>The class <code>MyApp</code> and the other application classes
-are translated by <code>MyTranslator</code>.
-
-<p>Note that <em>application</em> classes like <code>MyApp</code> cannot
-access the <em>loader</em> classes such as <code>Main</code>,
-<code>MyTranslator</code> and <code>ClassPool</code> because they
-are loaded by different loaders. The application classes are loaded
-by <code>javassist.Loader</code> whereas the loader classes such as
-<code>Main</code> are by the default Java class loader.
-
-<p>In Java, for security reasons, a single class file may be loaded
-into the JVM by two distinct class loaders so that two different
-classes would be created. For example,
-
-<ul><pre>class Point {
- int x, y;
-}
-
-class Box {
- Point base;
- Point getBase() { return base; }
-}
-
-class Window {
- Point size;
- Point getSize() { return size; }
-}</pre></ul>
-
-<p>Suppose that a class <code>Box</code> is loaded by a class loader
-<code>L1</code> while a class <code>Window</code> is loaded by a class
-loader <code>L2</code>. Then, the obejcts returned by
-<code>getBase()</code> and <code>getSize()</code> are not instances of
-the same class <code>Point</code>.
-<code>getBase()</code> returns an instance of the class <code>Point</code>
-loaded by <code>L1</code> whereas <code>getSize()</code> returns an
-instance of <code>Point</code> loaded by <code>L2</code>. The two versions
-of the class <code>Point</code> are distinct. They belong to different
-name spaces. For more details, see the following paper:
-
-<ul>Sheng Liang and Gilad Bracha,
-"Dynamic Class Loading in the Java Virtual Machine",
-<br><i>ACM OOPSLA'98</i>, pp.36-44, 1998.</ul>
-
-<p>To avoid this problem, the two class loaders <code>L1</code> and
-<code>L2</code> must delegate the loading operation of the class
-<code>Point</code> to another class loader, <code>L3</code>, which is
-a parent class loader of <code>L1</code> and <code>L2</code>.
-<code>delegateLoadingOf()</code> in <code>javassist.Loader</code>
-is a method for specifying what classes should be loaded by the
-parent loader.
-
-<p>If <code>L1</code> is the parent class loader of <code>L2</code>,
-that is, if <code>L1</code> loads the class of <code>L2</code>,
-then <code>L2</code> can delegate the loading operation of
-<code>Point</code> to <code>L1</code> for avoiding the problem above.
-However, this technique does not work in the case below:
-
-<ul><pre>class Point { // loaded by L1
- Window win;
- int x, y;
-}
-
-class Box { // loaded by L1
- Point base;
- Point getBase() { return base; }
-}
-
-class Window { // loaded by L2
- Point size;
- Point getSize() { size.win = this; return size; }
-}</pre></ul>
-
-<p>Since all the classes included in a class definition loaded by
-a class loader <code>L1</code> are also loaded by <code>L1</code>,
-the class of the field <code>win</code> in <code>Point</code> is
-now the class <code>Window</code> loaded by <code>L1</code>.
-Thus <code>size.win = this</code> in <code>getSize()</code> raises
-a runtime exception because of type mismatch; the type of
-<code>size.win</code> is the class <code>Point</code> loaded by
-<code>L1</code> whereas the type of <code>this</code> is the class
-<code>Point</code> loaded by <code>L2</code>.
-
-<p><br>
-
-<h3>4.2 Writing a class loader</h3>
-
-<p>A simple class loader using Javassist is as follows:
-
-<ul><pre>import javassist.*;
-
-public class SimpleLoader extends ClassLoader {
- /* Call MyApp.main().
- */
- public static void main(String[] args) throws Throwable {
- SimpleLoader s = new SimpleLoader();
- Class c = s.loadClass("MyApp");
- c.getDeclaredMethod("main", new Class[] { String[].class })
- .invoke(null, new Object[] { args });
- }
-
- private ClassPool pool;
-
- public SimpleLoader() throws NotFoundException {
- pool = ClassPool.getDefault();
- pool.insertClassPath("./class"); // <em>MyApp.class must be there.</em>
- }
-
- /* Finds a specified class.
- * The bytecode for that class can be modified.
- */
- protected Class findClass(String name) throws ClassNotFoundException {
- try {
- CtClass cc = pool.get(name);
- // <em>modify the CtClass object here</em>
- byte[] b = pool.write(name);
- return defineClass(name, b, 0, b.length);
- } catch (NotFoundException e) {
- throw new ClassNotFoundException();
- } catch (IOException e) {
- throw new ClassNotFoundException();
- } catch (CannotCompileException e) {
- throw new ClassNotFoundException();
- }
- }
-}</pre></ul>
-
-<p>The class <code>MyApp</code> is an application program.
-To execute this program, first put the class file under the
-<code>./class</code> directory, which must <em>not</em> be included
-in the class search path. The directory name is specified by
-<code>insertClassPath()</code> in the constructor.
-You can choose a different name instead of <code>./class</code> if you want.
-Then do as follows:
-
-<ul><code>% java SimpleLoader</code></ul>
-
-<p>The class loader loads the class <code>MyApp</code>
-(<code>./class/MyApp.class</code>) and calls
-<code>MyApp.main()</code> with the command line parameters.
-Note that <code>MyApp.class</code> must not be under the directory
-that the system class loader searches. Otherwise, the system class
-loader, which is the parent loader of <code>SimpleLoader</code>,
-loads the class <code>MyApp</code>.
-
-<p>This is the simplest way of using Javassist. However, if you write
-a more complex class loader, you may need detailed knowledge of
-Java's class loading mechanism. For example, the program above puts the
-<code>MyApp</code> class in a name space separated from the name space
-that the class <code>SimpleLoader</code> belongs to because the two
-classes are loaded by different class loaders.
-Hence, the
-<code>MyApp</code> class cannot directly access the class
-<code>SimpleLoader</code>.
-
-<p><br>
-
-<h3>4.3 Modifying a system class</h3>
-
-<p>The system classes like <code>java.lang.String</code> cannot be
-loaded by a class loader other than the system class loader.
-Therefore, <code>SimpleLoader</code> or <code>javassist.Loader</code>
-shown above cannot modify the system classes at loading time.
-
-<p>If your application needs to do that, the system classes must be
-<em>statically</em> modified. For example, the following program
-adds a new field <code>hiddenValue</code> to <code>java.lang.String</code>:
-
-<ul><pre>ClassPool pool = ClassPool.getDefault();
-CtClass cc = pool.get("java.lang.String");
-cc.addField(new CtField(CtClass.intType, "hiddenValue", cc));
-pool.writeFile("java.lang.String", ".");</pre></ul>
-
-<p>This program produces a file <code>"./java/lang/String.class"</code>.
-
-<p>To run your program <code>MyApp</code>
-with this modified <code>String</code> class, do as follows:
-
-<ul><pre>
-% java -Xbootclasspath/p:. MyApp <i>arg1</i> <i>arg2</i>...
-</pre></ul>
-
-<p>Suppose that the definition of <code>MyApp</code> is as follows:
-
-<ul><pre>public class MyApp {
- public static void main(String[] args) throws Exception {
- System.out.println(String.class.getField("hiddenValue").getName());
- }
-}</pre></ul>
-
-<p>If the modified <code>String</code> class is correctly loaded,
-<code>MyApp</code> prints <code>hiddenValue</code>.
-
-<p><i>Note: Applications that use this technique for the purpose of
-overriding a system class in <code>rt.jar</code> should not be
-deployed as doing so would contravene the Java 2 Runtime Environment
-binary code license.</i>
-
-<p><br>
-
-<a href="tutorial2.html">Next page</a>
-
-<hr>
-Java(TM) is a trademark of Sun Microsystems, Inc.<br>
-Copyright (C) 2000-2002 by Shigeru Chiba, All rights reserved.
-</body>
-</html>
+<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>Javassist Tutorial</title> + <link rel="stylesheet" type="text/css" href="brown.css"> +</head> +<body> + +<b> +<font size="+3"> +Getting Started with Javassist +</font> + +<p><font size="+2"> +Shigeru Chiba +</font> +</b> + +<p><div align="right"><a href="tutorial2.html">Next page</a></div> + +<ul>1. <a href="#read">Reading bytecode</a> +<br>2. <a href="#def">Defining a new class</a> +<br>3. <a href="#mod">Modifying a class at load time</a> +<br>4. <a href="#load">Class loader</a> +<br>5. <a href="tutorial2.html#intro">Introspection and customization</a> +</ul> + +<p><br> + +<a name="read"> +<h2>1. Reading bytecode</h2> + +<p>Javassist is a class library for dealing with Java bytecode. +Java bytecode is stored in a binary file called a class file. +Each class file contains one Java class or interface. + +<p>The class <code>Javassist.CtClass</code> is an abstract representation +of a class file. A <code>CtClass</code> object is a handle for dealing +with a class file. The following program is a very simple example: + +<ul><pre> +ClassPool pool = ClassPool.getDefault(); +CtClass cc = pool.get("test.Rectangle"); +cc.setSuperclass(pool.get("test.Point")); +pool.writeFile("test.Rectangle"); // or simply, cc.writeFile() +</pre></ul> + +<p>This program first obtains a <code>ClassPool</code> object, +which controls bytecode modification with Javassist. +The <code>ClassPool</code> object is a container of <code>CtClass</code> +object representing a class file. +It reads a class file on demand for constructing a <code>CtClass</code> +object and contains the constructed object until it is written out +to a file or an output stream. + +<p>The <code>ClassPool</code> object is used to maintain one-to-one +mapping between classes and <code>CtClass</code> objects. Javassist +never allows two distinct <code>CtClass</code> objects to represent +the same class. This is a crucial feature to consistent program +transformaiton. + +<p>To modify the definition of a class, the users must first obtain a +reference to the <code>CtClass</code> object representing that class. +<code>ClassPool.get()</code> is used for this purpose. +In the case of the program above, the <code>CtClass</code> object +representing a class <code>test.Rectangle</code> is obtained from +the <code>ClassPool</code> object +and it is assigned +to a variable <code>cc</code>. Then it is modified so that +the superclass of <code>test.Rectangle</code> is changed into +a class <code>test.Point</code>. +This change is reflected on the original class file when +<code>ClassPool.writeFile()</code> is finally called. + +<p>Note that <code>writeFile()</code> is a method declared in not +<code>CtClass</code> but <code>ClassPool</code>. +If this method is called, the <code>ClassPool</code> +finds a <code>CtClass</code> object specified with a class name +among the objects that the <code>ClassPool</code> contains. +Then it translates that <code>CtClass</code> object into a class file +and writes it on a local disk. + +<p>There is also <code>writeFile()</code> defined in <code>CtClass</code>. +Thus, the last line in the program above can be rewritten into: + +<ul><pre>cc.writeFile();</pre></ul> + +<p>This method is a convenient method for invoking <code>writeFile()</code> +in <code>ClassPool</code> with the name of the class represented by +<code>cc</code>. + +<p>Javassist also provides a method for directly obtaining the +modified bytecode. To do this, call <code>write()</code>: + +<ul><pre> +byte[] b = pool.write("test.Rectangle"); +</pre></ul> + +<p>The contents of the class file for <code>test.Rectangle</code> are +assigned to a variable <code>b</code> in the form of byte array. +<code>writeFile()</code> also internally calls <code>write()</code> +to obtain the byte array written in a class file. + +<p>The default <code>ClassPool</code> returned +by a static method <code>ClassPool.getDefault()</code> +searches the same path as the underlying JVM. +The users can expand this class search path if needed. +For example, the following code adds a directory +<code>/usr/local/javalib</code> +to the search path: + +<ul><pre> +ClassPool pool = ClassPool.getDefault(); +pool.insertClassPath("/usr/local/javalib"); +</pre></ul> + +<p>The search path that the users can add is not only a directory but also +a URL: + +<ul><pre> +ClassPool pool = ClassPool.getDefault(); +ClassPath cp = new URLClassPath("www.foo.com", 80, "/java/", "com.foo."); +pool.insertClassPath(cp); +</pre></ul> + +<p>This program adds "http://www.foo.com:80/java/" to the class search +path. This URL is used only for searching classes belonging to a +package <code>com.foo</code>. + +<p>You can directly give a byte array to a <code>ClassPool</code> object +and construct a <code>CtClass</code> object from that array. To do this, +use <code>ByteArrayClassPath</code>. For example, + +<ul><pre> +ClassPool cp = ClassPool.getDefault(); +byte[] b = <em>a byte array</em>; +String name = <em>class name</em>; +cp.insertClassPath(new ByteArrayClassPath(name, b)); +CtClass cc = cp.get(name); +</pre></ul> + +<p>The obtained <code>CtClass</code> object represents +a class defined by the class file specified by <code>b</code>. + +<p>Since <code>ClassPath</code> is an interface, the users can define +a new class implementing this interface and they can add an instance +of that class so that a class file is obtained from a non-standard resource. + +<p>If you want to directly construct a <code>CtClass</code> object +from a class file but you do not know the fully-qualified name +of the class, then +you can use <code>makeClass()</code> in <code>CtClass</code>: + +<ul><pre> +ClassPool cp = ClassPool.getDefault(); +InputStream ins = <em>an input stream for reading a class file</em>; +CtClass cc = cp.makeClass(ins); +</pre></ul> + +<p><code>makeClass()</code> returns the <code>CtClass</code> object +constructed from the given input stream. You can use +<code>makeClass()</code> for eagerly feeding class files to +the <code>ClassPool</code> object. This might improve performance +if the search path includes a large jar file. Since +the <code>ClassPool</code> object reads a class file on demand, +it might repeatedly search the whole jar file for every class file. +<code>makeClass()</code> can be used for optimizing this search. +The <code>CtClass</code> constructed by <code>makeClass()</code> +is kept in the <code>ClassPool</code> object and the class file is never +read again. + +<p><br> + +<a name="def"> +<h2>2. Defining a new class</h2> + +<p>To define a new class from scratch, <code>makeClass()</code> +must be called on a <code>ClassPool</code>. + +<ul><pre> +ClassPool pool = ClassPool.getDefault(); +CtClass cc = pool.makeClass("Point"); +</pre></ul> + +<p>This program defines a class <code>Point</code> +including no members. + +<p>A new class can be also defined as a copy of an existing class. +The program below does that: + +<ul><pre> +ClassPool pool = ClassPool.getDefault(); +CtClass cc = pool.makeClass("Point"); +cc.setName("Pair"); +</pre></ul> + +<p>This program first obtains the <code>CtClass</code> object +for class <code>Point</code>. Then it gives a new name <code>Pair</code> +to that <code>CtClass</code> object. +If <code>get("Point")</code> is called on the <code>ClassPool</code> +object, then a class file <code>Point.class</code> is read again and +a new <code>CtClass</code> object for class <code>Point</code> is constructed +again. + +<ul><pre> +ClassPool pool = ClassPool.getDefault(); +CtClass cc = pool.makeClass("Point"); +CtClass cc1 = pool.get("Point"); // cc1 is identical to cc. +cc.setName("Pair"); +CtClass cc2 = pool.get("Pair"); // cc2 is identical to cc. +CtClass cc3 = pool.get("Point"); // cc3 is not identical to cc. +</pre></ul> + +<p><br> + +<a name="mod"> +<h2>3. Modifying a class at load time</h2> + +<p>If what classes are modified is known in advance, +the easiest way for modifying the classes is as follows: + +<ul><li>1. Get a <code>CtClass</code> object by calling + <code>ClassPool.get()</code>, + <li>2. Modify it, and + <li>3. Call <code>ClassPool.write()</code> or <code>writeFile()</code>. +</ul> + +<p>If whether a class is modified or not is determined at load time, +the users can write an event listener so that it is notified +when a class is loaded into the JVM. +A class loader (<code>java.lang.ClassLoader</code>) working with +Javassist must call <code>ClassPool.write()</code> for obtaining +a class file. The users can write an event listener so that it is +notified when the class loader calls <code>ClassPool.write()</code>. +The event-listener class must implement the following interface: + +<ul><pre>public interface Translator { + public void start(ClassPool pool) + throws NotFoundException, CannotCompileException; + public void onWrite(ClassPool pool, String classname) + throws NotFoundException, CannotCompileException; +}</pre></ul> + +<p>The method <code>start()</code> is called when this event listener +is registered to a <code>ClassPool</code> object. +The method <code>onWrite()</code> is called when <code>write()</code> +(or similar methods) is called on the <code>ClassPool</code> object. +The second parameter of <code>onWrite()</code> is the name of the class +to be written out. + +<p>Note that <code>start()</code> or <code>onWrite()</code> do not have +to call <code>write()</code> or <code>writeFile()</code>. For example, + +<ul><pre>public class MyAnotherTranslator implements Translator { + public void start(ClassPool pool) + throws NotFoundException, CannotCompileException {} + public void onWrite(ClassPool pool, String classname) + throws NotFoundException, CannotCompileException + { + CtClass cc = pool.get(classname); + cc.setModifiers(Modifier.PUBLIC); + } +}</pre></ul> + +<p>All the classes written out by <code>write()</code> are made public +just before their definitions are translated into an byte array. + +<p><center><img src="overview.gif" alt="overview"></center> + +<p>The two methods <code>start()</code> and <code>onWrite()</code> +can modify not only a <code>CtClass</code> object specified by +the given <code>classname</code> but also +<em>any</em> <code>CtClass</code> objects contained +in the given <code>ClassPool</code>. +They can call <code>ClassPool.get()</code> for obtaining any +<code>CtClass</code> object. +If a modified <code>CtClass</code> object is not written out immediately, +the modification is recorded until that object is written out. + +<p><center><img src="sequence.gif" alt="sequence diagram"></center> + +<p>To register an event listener to a <code>ClassPool</code>, +it must be passed to a constructor of <code>ClassPool</code>. +Only a single event listener can be registered. +If more than one event listeners are needed, multiple +<code>ClassPool</code>s should be connected to be a single +stream. For example, + +<ul><pre>Translator t1 = new MyTranslator(); +ClassPool c1 = new ClassPool(t1); +Translator t2 = new MyAnotherTranslator(); +ClassPool c2 = new ClassPool(c1, t2);</pre></ul> + +<p>This program connects two <code>ClassPool</code>s. +If a class loader calls <code>write()</code> on <code>c2</code>, +the specified class file is first modified by <code>t1</code> and +then by <code>t2</code>. <code>write()</code> returns the resulting +class file. + +First, <code>onWrite()</code> on <code>t1</code> is called since +<code>c2</code> obtains a class file by calling <code>write()</code> +on <code>c1</code>. Then <code>onWrite()</code> on <code>t2</code> +is called. If <code>onWrite()</code> called on <code>t2</code> +obtains a <code>CtClass</code> object from <code>c2</code>, that +<code>CtClass</code> object represents the class file that +<code>t1</code> has modified. + +<p><center><img src="two.gif" alt="two translators"></center> + +<p><br> + +<a name="load"> +<h2>4. Class loader</h2> + +<p>Javassist can be used with a class loader so that bytecode can be +modified at load time. The users of Javassist can define their own +version of class loader but they can also use a class loader provided +by Javassist. + +<p><br> + +<h3>4.1 Using <code>javassist.Loader</code></h3> + +<p>Javassist provides a class loader +<code>javassist.Loader</code>. This class loader uses a +<code>javassist.ClassPool</code> object for reading a class file. + +<p>For example, <code>javassist.Loader</code> can be used for loading +a particular class modified with Javassist. + +<ul><pre> +import javassist.*; +import test.Rectangle; + +public class Main { + public static void main(String[] args) throws Throwable { + ClassPool pool = ClassPool.getDefault(); + Loader cl = new Loader(pool); + + CtClass ct = pool.get("test.Rectangle"); + ct.setSuperclass(pool.get("test.Point")); + + Class c = cl.loadClass("test.Rectangle"); + Object rect = c.newInstance(); + : + } +} +</pre></ul> + +<p>This program modifies a class <code>test.Rectangle</code>. The +superclass of <code>test.Rectangle</code> is set to a +<code>test.Point</code> class. Then this program loads the modified +class into the JVM, and creates a new instance of the +<code>test.Rectangle</code> class. + +<p>The users can use a <code>javassist.Translator</code> object +for modifying class files. +Suppose that an instance of a class <code>MyTranslator</code>, +which implements +<code>javassist.Translator</code>, performs modification of class files. +To run an application class <code>MyApp</code> with the +<code>MyTranslator</code> object, write a main class: + +<ul><pre> +import javassist.*; + +public class Main2 { + public static void main(String[] args) throws Throwable { + Translator t = new MyTranslator(); + ClassPool pool = ClassPool.getDefault(t); + Loader cl = new Loader(pool); + cl.run("MyApp", args); + } +} +</pre></ul> + +<p>To run this program, do: + +<ul><pre> +% java Main <i>arg1</i> <i>arg2</i>... +</pre></ul> + +<p>The class <code>MyApp</code> and the other application classes +are translated by <code>MyTranslator</code>. + +<p>Note that <em>application</em> classes like <code>MyApp</code> cannot +access the <em>loader</em> classes such as <code>Main</code>, +<code>MyTranslator</code> and <code>ClassPool</code> because they +are loaded by different loaders. The application classes are loaded +by <code>javassist.Loader</code> whereas the loader classes such as +<code>Main</code> are by the default Java class loader. + +<p>In Java, for security reasons, a single class file may be loaded +into the JVM by two distinct class loaders so that two different +classes would be created. For example, + +<ul><pre>class Point { + int x, y; +} + +class Box { + Point base; + Point getBase() { return base; } +} + +class Window { + Point size; + Point getSize() { return size; } +}</pre></ul> + +<p>Suppose that a class <code>Box</code> is loaded by a class loader +<code>L1</code> while a class <code>Window</code> is loaded by a class +loader <code>L2</code>. Then, the obejcts returned by +<code>getBase()</code> and <code>getSize()</code> are not instances of +the same class <code>Point</code>. +<code>getBase()</code> returns an instance of the class <code>Point</code> +loaded by <code>L1</code> whereas <code>getSize()</code> returns an +instance of <code>Point</code> loaded by <code>L2</code>. The two versions +of the class <code>Point</code> are distinct. They belong to different +name spaces. For more details, see the following paper: + +<ul>Sheng Liang and Gilad Bracha, +"Dynamic Class Loading in the Java Virtual Machine", +<br><i>ACM OOPSLA'98</i>, pp.36-44, 1998.</ul> + +<p>To avoid this problem, the two class loaders <code>L1</code> and +<code>L2</code> must delegate the loading operation of the class +<code>Point</code> to another class loader, <code>L3</code>, which is +a parent class loader of <code>L1</code> and <code>L2</code>. +<code>delegateLoadingOf()</code> in <code>javassist.Loader</code> +is a method for specifying what classes should be loaded by the +parent loader. + +<p>If <code>L1</code> is the parent class loader of <code>L2</code>, +that is, if <code>L1</code> loads the class of <code>L2</code>, +then <code>L2</code> can delegate the loading operation of +<code>Point</code> to <code>L1</code> for avoiding the problem above. +However, this technique does not work in the case below: + +<ul><pre>class Point { // loaded by L1 + Window win; + int x, y; +} + +class Box { // loaded by L1 + Point base; + Point getBase() { return base; } +} + +class Window { // loaded by L2 + Point size; + Point getSize() { size.win = this; return size; } +}</pre></ul> + +<p>Since all the classes included in a class definition loaded by +a class loader <code>L1</code> are also loaded by <code>L1</code>, +the class of the field <code>win</code> in <code>Point</code> is +now the class <code>Window</code> loaded by <code>L1</code>. +Thus <code>size.win = this</code> in <code>getSize()</code> raises +a runtime exception because of type mismatch; the type of +<code>size.win</code> is the class <code>Point</code> loaded by +<code>L1</code> whereas the type of <code>this</code> is the class +<code>Point</code> loaded by <code>L2</code>. + +<p><br> + +<h3>4.2 Writing a class loader</h3> + +<p>A simple class loader using Javassist is as follows: + +<ul><pre>import javassist.*; + +public class SimpleLoader extends ClassLoader { + /* Call MyApp.main(). + */ + public static void main(String[] args) throws Throwable { + SimpleLoader s = new SimpleLoader(); + Class c = s.loadClass("MyApp"); + c.getDeclaredMethod("main", new Class[] { String[].class }) + .invoke(null, new Object[] { args }); + } + + private ClassPool pool; + + public SimpleLoader() throws NotFoundException { + pool = ClassPool.getDefault(); + pool.insertClassPath("./class"); // <em>MyApp.class must be there.</em> + } + + /* Finds a specified class. + * The bytecode for that class can be modified. + */ + protected Class findClass(String name) throws ClassNotFoundException { + try { + CtClass cc = pool.get(name); + // <em>modify the CtClass object here</em> + byte[] b = pool.write(name); + return defineClass(name, b, 0, b.length); + } catch (NotFoundException e) { + throw new ClassNotFoundException(); + } catch (IOException e) { + throw new ClassNotFoundException(); + } catch (CannotCompileException e) { + throw new ClassNotFoundException(); + } + } +}</pre></ul> + +<p>The class <code>MyApp</code> is an application program. +To execute this program, first put the class file under the +<code>./class</code> directory, which must <em>not</em> be included +in the class search path. The directory name is specified by +<code>insertClassPath()</code> in the constructor. +You can choose a different name instead of <code>./class</code> if you want. +Then do as follows: + +<ul><code>% java SimpleLoader</code></ul> + +<p>The class loader loads the class <code>MyApp</code> +(<code>./class/MyApp.class</code>) and calls +<code>MyApp.main()</code> with the command line parameters. +Note that <code>MyApp.class</code> must not be under the directory +that the system class loader searches. Otherwise, the system class +loader, which is the parent loader of <code>SimpleLoader</code>, +loads the class <code>MyApp</code>. + +<p>This is the simplest way of using Javassist. However, if you write +a more complex class loader, you may need detailed knowledge of +Java's class loading mechanism. For example, the program above puts the +<code>MyApp</code> class in a name space separated from the name space +that the class <code>SimpleLoader</code> belongs to because the two +classes are loaded by different class loaders. +Hence, the +<code>MyApp</code> class cannot directly access the class +<code>SimpleLoader</code>. + +<p><br> + +<h3>4.3 Modifying a system class</h3> + +<p>The system classes like <code>java.lang.String</code> cannot be +loaded by a class loader other than the system class loader. +Therefore, <code>SimpleLoader</code> or <code>javassist.Loader</code> +shown above cannot modify the system classes at loading time. + +<p>If your application needs to do that, the system classes must be +<em>statically</em> modified. For example, the following program +adds a new field <code>hiddenValue</code> to <code>java.lang.String</code>: + +<ul><pre>ClassPool pool = ClassPool.getDefault(); +CtClass cc = pool.get("java.lang.String"); +cc.addField(new CtField(CtClass.intType, "hiddenValue", cc)); +pool.writeFile("java.lang.String", ".");</pre></ul> + +<p>This program produces a file <code>"./java/lang/String.class"</code>. + +<p>To run your program <code>MyApp</code> +with this modified <code>String</code> class, do as follows: + +<ul><pre> +% java -Xbootclasspath/p:. MyApp <i>arg1</i> <i>arg2</i>... +</pre></ul> + +<p>Suppose that the definition of <code>MyApp</code> is as follows: + +<ul><pre>public class MyApp { + public static void main(String[] args) throws Exception { + System.out.println(String.class.getField("hiddenValue").getName()); + } +}</pre></ul> + +<p>If the modified <code>String</code> class is correctly loaded, +<code>MyApp</code> prints <code>hiddenValue</code>. + +<p><i>Note: Applications that use this technique for the purpose of +overriding a system class in <code>rt.jar</code> should not be +deployed as doing so would contravene the Java 2 Runtime Environment +binary code license.</i> + +<p><br> + +<a href="tutorial2.html">Next page</a> + +<hr> +Java(TM) is a trademark of Sun Microsystems, Inc.<br> +Copyright (C) 2000-2003 by Shigeru Chiba, All rights reserved. +</body> +</html> |