diff options
author | patriot1burke <patriot1burke@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2003-04-22 13:47:06 +0000 |
---|---|---|
committer | patriot1burke <patriot1burke@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2003-04-22 13:47:06 +0000 |
commit | 069bceaf72fd0d6ffad14ce4e3e00ca91a280bde (patch) | |
tree | b8230a15d3061c64d5a64bf9efa654d0d6311ff2 /sample/evolve | |
parent | f610083ba0adbcb3fe92a504015fb26fb5a42530 (diff) | |
download | javassist-069bceaf72fd0d6ffad14ce4e3e00ca91a280bde.tar.gz javassist-069bceaf72fd0d6ffad14ce4e3e00ca91a280bde.zip |
This commit was generated by cvs2svn to compensate for changes in r2, which
included commits to RCS files with non-trunk default branches.
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@6 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
Diffstat (limited to 'sample/evolve')
-rw-r--r-- | sample/evolve/CannotCreateException.java | 14 | ||||
-rw-r--r-- | sample/evolve/CannotUpdateException.java | 14 | ||||
-rw-r--r-- | sample/evolve/DemoLoader.java | 40 | ||||
-rw-r--r-- | sample/evolve/DemoServer.java | 102 | ||||
-rw-r--r-- | sample/evolve/Evolution.java | 203 | ||||
-rw-r--r-- | sample/evolve/Sample.java | 12 | ||||
-rw-r--r-- | sample/evolve/VersionManager.java | 90 | ||||
-rw-r--r-- | sample/evolve/WebPage.class.0 | bin | 0 -> 686 bytes | |||
-rw-r--r-- | sample/evolve/WebPage.class.1 | bin | 0 -> 812 bytes | |||
-rw-r--r-- | sample/evolve/WebPage.java | 31 | ||||
-rw-r--r-- | sample/evolve/demo.html | 38 | ||||
-rw-r--r-- | sample/evolve/start.html | 23 | ||||
-rw-r--r-- | sample/evolve/update.html | 3 |
13 files changed, 570 insertions, 0 deletions
diff --git a/sample/evolve/CannotCreateException.java b/sample/evolve/CannotCreateException.java new file mode 100644 index 00000000..828afb06 --- /dev/null +++ b/sample/evolve/CannotCreateException.java @@ -0,0 +1,14 @@ +package sample.evolve;
+
+/**
+ * Signals that VersionManager.newInstance() fails.
+ */
+public class CannotCreateException extends RuntimeException {
+ public CannotCreateException(String s) {
+ super(s);
+ }
+
+ public CannotCreateException(Exception e) {
+ super("by " + e.toString());
+ }
+}
diff --git a/sample/evolve/CannotUpdateException.java b/sample/evolve/CannotUpdateException.java new file mode 100644 index 00000000..bd28bba6 --- /dev/null +++ b/sample/evolve/CannotUpdateException.java @@ -0,0 +1,14 @@ +package sample.evolve;
+
+/**
+ * Signals that VersionManager.update() fails.
+ */
+public class CannotUpdateException extends Exception {
+ public CannotUpdateException(String msg) {
+ super(msg);
+ }
+
+ public CannotUpdateException(Exception e) {
+ super("by " + e.toString());
+ }
+}
diff --git a/sample/evolve/DemoLoader.java b/sample/evolve/DemoLoader.java new file mode 100644 index 00000000..5fa950da --- /dev/null +++ b/sample/evolve/DemoLoader.java @@ -0,0 +1,40 @@ +package sample.evolve;
+
+import javassist.*;
+import java.io.*;
+import java.util.Hashtable;
+
+/**
+ * DemoLoader is a class loader for running a program including
+ * an updatable class. This simple loader allows only a single
+ * class to be updatable. (Extending it for supporting multiple
+ * updatable classes is easy.)
+ *
+ * To run, type as follows:
+ *
+ * % java sample.evolve.DemoLoader <port number>
+ *
+ * Then DemoLoader launches sample.evolve.DemoServer with <port number>.
+ * It also translates sample.evolve.WebPage, which sample.evolve.DemoServer
+ * uses, so that it is an updable class.
+ *
+ * Note: JDK 1.2 or later only.
+ */
+public class DemoLoader {
+ private static final int initialVersion = 0;
+ private String updatableClassName = null;
+ private CtClass updatableClass = null;
+
+ /* Creates a <code>DemoLoader</code> for making class WebPage
+ * updatable. Then it runs main() in sample.evolve.DemoServer.
+ */
+ public static void main(String[] args) throws Throwable {
+ Evolution translator = new Evolution();
+ ClassPool cp = ClassPool.getDefault(translator);
+ Loader cl = new Loader();
+ cl.setClassPool(cp);
+
+ translator.makeUpdatable("sample.evolve.WebPage");
+ cl.run("sample.evolve.DemoServer", args);
+ }
+}
diff --git a/sample/evolve/DemoServer.java b/sample/evolve/DemoServer.java new file mode 100644 index 00000000..943c509c --- /dev/null +++ b/sample/evolve/DemoServer.java @@ -0,0 +1,102 @@ +package sample.evolve;
+
+import javassist.*;
+import javassist.web.*;
+import java.io.*;
+
+/**
+ * A web server for demonstrating class evolution. It must be
+ * run with a DemoLoader.
+ *
+ * If a html file /java.html is requested, this web server calls
+ * WebPage.show() for constructing the contents of that html file
+ * So if a DemoLoader changes the definition of WebPage, then
+ * the image of /java.html is also changed.
+ * Note that WebPage is not an applet. It is rather
+ * similar to a CGI script or a servlet. The web server never
+ * sends the class file of WebPage to web browsers.
+ *
+ * Furthermore, if a html file /update.html is requested, this web
+ * server overwrites WebPage.class (class file) and calls update()
+ * in VersionManager so that WebPage.class is loaded into the JVM
+ * again. The new contents of WebPage.class are copied from
+ * either WebPage.class.0 or WebPage.class.1.
+ */
+public class DemoServer extends Webserver {
+
+ public static void main(String[] args) throws IOException
+ {
+ if (args.length == 1) {
+ DemoServer web = new DemoServer(Integer.parseInt(args[0]));
+ web.run();
+ }
+ else
+ System.err.println(
+ "Usage: java sample.evolve.DemoServer <port number>");
+ }
+
+ public DemoServer(int port) throws IOException {
+ super(port);
+ htmlfileBase = "sample/evolve/";
+ }
+
+ private static final String ver0 = "sample/evolve/WebPage.class.0";
+ private static final String ver1 = "sample/evolve/WebPage.class.1";
+ private String currentVersion = ver0;
+
+ public void doReply(InputStream in, OutputStream out, String cmd)
+ throws IOException, BadHttpRequest
+ {
+ if (cmd.startsWith("GET /java.html ")) {
+ runJava(out);
+ return;
+ }
+ else if (cmd.startsWith("GET /update.html ")) {
+ try {
+ if (currentVersion == ver0)
+ currentVersion = ver1;
+ else
+ currentVersion = ver0;
+
+ updateClassfile(currentVersion);
+ VersionManager.update("sample.evolve.WebPage");
+ }
+ catch (CannotUpdateException e) {
+ logging(e.toString());
+ }
+ catch (FileNotFoundException e) {
+ logging(e.toString());
+ }
+ }
+
+ super.doReply(in, out, cmd);
+ }
+
+ private void runJava(OutputStream outs) throws IOException {
+ OutputStreamWriter out = new OutputStreamWriter(outs);
+ out.write("HTTP/1.0 200 OK\r\n\r\n");
+ WebPage page = new WebPage();
+ page.show(out);
+ out.close();
+ }
+
+ /* updateClassfile() copies the specified file to WebPage.class.
+ */
+ private void updateClassfile(String filename)
+ throws IOException, FileNotFoundException
+ {
+ byte[] buf = new byte[1024];
+
+ FileInputStream fin
+ = new FileInputStream(filename);
+ FileOutputStream fout
+ = new FileOutputStream("sample/evolve/WebPage.class");
+ for (;;) {
+ int len = fin.read(buf);
+ if (len >= 0)
+ fout.write(buf, 0, len);
+ else
+ break;
+ }
+ }
+}
diff --git a/sample/evolve/Evolution.java b/sample/evolve/Evolution.java new file mode 100644 index 00000000..810f1986 --- /dev/null +++ b/sample/evolve/Evolution.java @@ -0,0 +1,203 @@ +package sample.evolve;
+
+import javassist.*;
+import java.io.IOException;
+
+/**
+ * Evolution provides a set of methods for instrumenting bytecodes.
+ *
+ * For class evolution, updatable class A is renamed to B. Then an
+ * abstract class named A is produced as the super class of B. If the
+ * original class A has a public method m(), then the abstract class A
+ * has an abstract method m().
+ *
+ * abstract class A
+ * abstract m()
+ * _makeInstance()
+ * |
+ * class A --------> class B
+ * m() m()
+ *
+ * Also, all the other classes are translated so that "new A(i)"
+ * in the methods is replaced with "_makeInstance(i)". This makes
+ * it possible to change the behavior of the instantiation of
+ * the class A.
+ */
+public class Evolution implements Translator {
+ public final static String handlerMethod = "_makeInstance";
+ public final static String latestVersionField
+ = VersionManager.latestVersionField;
+ public final static String versionManagerMethod = "initialVersion";
+
+ private static CtMethod trapMethod;
+ private static final int initialVersion = 0;
+ private ClassPool pool;
+ private String updatableClassName = null;
+ private CtClass updatableClass = null;
+
+ public void start(ClassPool _pool) throws NotFoundException {
+ pool = _pool;
+
+ // Get the definition of Sample.make() and store it into trapMethod
+ // for later use.
+ trapMethod = _pool.getMethod("sample.evolve.Sample", "make");
+ }
+
+ public void onWrite(ClassPool _pool, String classname)
+ throws NotFoundException, CannotCompileException
+ {
+ onWriteUpdatable(classname);
+
+ /*
+ * Replaces all the occurrences of the new operator with a call
+ * to _makeInstance().
+ */
+ CtClass clazz = _pool.get(classname);
+ CtClass absClass = updatableClass;
+ CodeConverter converter = new CodeConverter();
+ converter.replaceNew(absClass, absClass, handlerMethod);
+ clazz.instrument(converter);
+ }
+
+ private void onWriteUpdatable(String classname)
+ throws NotFoundException, CannotCompileException
+ {
+ // if the class is a concrete class,
+ // classname is <updatableClassName>$<version>.
+
+ int i = classname.lastIndexOf('$');
+ if (i <= 0)
+ return;
+
+ String orgname = classname.substring(0, i);
+ if (!orgname.equals(updatableClassName))
+ return;
+
+ int version;
+ try {
+ version = Integer.parseInt(classname.substring(i + 1));
+ }
+ catch (NumberFormatException e) {
+ throw new NotFoundException(classname, e);
+ }
+
+
+ CtClass clazz = pool.getAndRename(orgname, classname);
+ makeConcreteClass(clazz, updatableClass, version);
+ }
+
+ /* Register an updatable class.
+ */
+ public void makeUpdatable(String classname)
+ throws NotFoundException, CannotCompileException
+ {
+ if (pool == null)
+ throw new RuntimeException(
+ "Evolution has not been linked to ClassPool.");
+
+ CtClass c = pool.get(classname);
+ updatableClassName = classname;
+ updatableClass = makeAbstractClass(c);
+ }
+
+ /**
+ * Produces an abstract class.
+ */
+ protected CtClass makeAbstractClass(CtClass clazz)
+ throws CannotCompileException, NotFoundException
+ {
+ int i;
+
+ CtClass absClass = pool.makeClass(clazz.getName());
+ absClass.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);
+ absClass.setSuperclass(clazz.getSuperclass());
+ absClass.setInterfaces(clazz.getInterfaces());
+
+ // absClass.inheritAllConstructors();
+
+ CtField fld = new CtField(pool.get("java.lang.Class"),
+ latestVersionField, absClass);
+ fld.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
+
+ CtField.Initializer finit
+ = CtField.Initializer.byCall(
+ pool.get("sample.evolve.VersionManager"),
+ versionManagerMethod,
+ new String[] { clazz.getName() });
+ absClass.addField(fld, finit);
+
+ CtField[] fs = clazz.getDeclaredFields();
+ for (i = 0; i < fs.length; ++i) {
+ CtField f = fs[i];
+ if (Modifier.isPublic(f.getModifiers()))
+ absClass.addField(new CtField(f.getType(), f.getName(),
+ absClass));
+ }
+
+ CtConstructor[] cs = clazz.getDeclaredConstructors();
+ for (i = 0; i < cs.length; ++i) {
+ CtConstructor c = cs[i];
+ int mod = c.getModifiers();
+ if (Modifier.isPublic(mod)) {
+ CtMethod wm
+ = CtNewMethod.wrapped(absClass, handlerMethod,
+ c.getParameterTypes(),
+ c.getExceptionTypes(),
+ trapMethod, null, absClass);
+ wm.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
+ absClass.addMethod(wm);
+ }
+ }
+
+ CtMethod[] ms = clazz.getDeclaredMethods();
+ for (i = 0; i < ms.length; ++i) {
+ CtMethod m = ms[i];
+ int mod = m.getModifiers();
+ if (Modifier.isPublic(mod))
+ if (Modifier.isStatic(mod))
+ throw new CannotCompileException(
+ "static methods are not supported.");
+ else {
+ CtMethod m2
+ = CtNewMethod.abstractMethod(m.getReturnType(),
+ m.getName(),
+ m.getParameterTypes(),
+ m.getExceptionTypes(),
+ absClass);
+ absClass.addMethod(m2);
+ }
+ }
+
+ return absClass;
+ }
+
+ /**
+ * Modifies the given class file so that it is a subclass of the
+ * abstract class produced by makeAbstractClass().
+ *
+ * Note: the naming convention must be consistent with
+ * VersionManager.update().
+ */
+ protected void makeConcreteClass(CtClass clazz,
+ CtClass abstractClass, int version)
+ throws CannotCompileException, NotFoundException
+ {
+ int i;
+ clazz.setSuperclass(abstractClass);
+ CodeConverter converter = new CodeConverter();
+ CtField[] fs = clazz.getDeclaredFields();
+ for (i = 0; i < fs.length; ++i) {
+ CtField f = fs[i];
+ if (Modifier.isPublic(f.getModifiers()))
+ converter.redirectFieldAccess(f, abstractClass, f.getName());
+ }
+
+ CtConstructor[] cs = clazz.getDeclaredConstructors();
+ for (i = 0; i < cs.length; ++i)
+ cs[i].instrument(converter);
+
+ CtMethod[] ms = clazz.getDeclaredMethods();
+ for (i = 0; i < ms.length; ++i)
+ ms[i].instrument(converter);
+ }
+}
diff --git a/sample/evolve/Sample.java b/sample/evolve/Sample.java new file mode 100644 index 00000000..c147c965 --- /dev/null +++ b/sample/evolve/Sample.java @@ -0,0 +1,12 @@ +package sample.evolve;
+
+/**
+ * This is a sample class used by Transformer.
+ */
+public class Sample {
+ public static Class _version;
+
+ public static Object make(Object[] args) {
+ return VersionManager.make(_version, args);
+ }
+}
diff --git a/sample/evolve/VersionManager.java b/sample/evolve/VersionManager.java new file mode 100644 index 00000000..d95268b8 --- /dev/null +++ b/sample/evolve/VersionManager.java @@ -0,0 +1,90 @@ +package sample.evolve;
+
+import java.util.Hashtable;
+import java.lang.reflect.*;
+import javassist.CtClass;
+
+/**
+ * Runtime system for class evolution
+ */
+public class VersionManager {
+ private static Hashtable versionNo = new Hashtable();
+ public final static String latestVersionField = "_version";
+
+ /**
+ * For updating the definition of class my.X, say:
+ *
+ * VersionManager.update("my.X");
+ */
+ public static void update(String qualifiedClassname)
+ throws CannotUpdateException
+ {
+ try {
+ Class c = getUpdatedClass(qualifiedClassname);
+ Field f = c.getField(latestVersionField);
+ f.set(null, c);
+ }
+ catch (ClassNotFoundException e) {
+ throw new CannotUpdateException("cannot update class: "
+ + qualifiedClassname);
+ }
+ catch (Exception e) {
+ throw new CannotUpdateException(e);
+ }
+ }
+
+ private static Class getUpdatedClass(String qualifiedClassname)
+ throws ClassNotFoundException
+ {
+ int version;
+ Object found = versionNo.get(qualifiedClassname);
+ if (found == null)
+ version = 0;
+ else
+ version = ((Integer)found).intValue() + 1;
+
+ Class c = Class.forName(qualifiedClassname + '$' + version);
+ versionNo.put(qualifiedClassname, new Integer(version));
+ return c;
+ }
+
+ /* initiaVersion() is used to initialize the _version field of
+ * the updatable classes.
+ */
+ public static Class initialVersion(String[] params) {
+ try {
+ return getUpdatedClass(params[0]);
+ }
+ catch (ClassNotFoundException e) {
+ throw new RuntimeException("cannot initialize " + params[0]);
+ }
+ }
+
+ /** make() performs the object creation of the updatable classes.
+ * The expression "new <updatable class>" is replaced with a call
+ * to this method.
+ */
+ public static Object make(Class clazz, Object[] args) {
+ Constructor[] constructors = clazz.getConstructors();
+ int n = constructors.length;
+ for (int i = 0; i < n; ++i) {
+ try {
+ return constructors[i].newInstance(args);
+ }
+ catch (IllegalArgumentException e) {
+ // try again
+ }
+ catch (InstantiationException e) {
+ throw new CannotCreateException(e);
+ }
+ catch (IllegalAccessException e) {
+ throw new CannotCreateException(e);
+ }
+ catch (InvocationTargetException e) {
+ throw new CannotCreateException(e);
+ }
+ }
+
+ throw new CannotCreateException("no constructor matches");
+ }
+}
diff --git a/sample/evolve/WebPage.class.0 b/sample/evolve/WebPage.class.0 Binary files differnew file mode 100644 index 00000000..3cc1d743 --- /dev/null +++ b/sample/evolve/WebPage.class.0 diff --git a/sample/evolve/WebPage.class.1 b/sample/evolve/WebPage.class.1 Binary files differnew file mode 100644 index 00000000..fe49380e --- /dev/null +++ b/sample/evolve/WebPage.class.1 diff --git a/sample/evolve/WebPage.java b/sample/evolve/WebPage.java new file mode 100644 index 00000000..7c2b7cfb --- /dev/null +++ b/sample/evolve/WebPage.java @@ -0,0 +1,31 @@ +package sample.evolve;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Updatable class. DemoServer instantiates this class and calls
+ * show() on the created object.
+ */
+
+// WebPage.class.0
+public class WebPage {
+ public void show(OutputStreamWriter out) throws IOException {
+ Calendar c = new GregorianCalendar();
+ out.write(c.getTime().toString());
+ out.write("<P><A HREF=\"demo.html\">Return to the home page.</A>");
+ }
+}
+/*
+// WebPage.class.1
+public class WebPage {
+ public void show(OutputStreamWriter out) throws IOException {
+ out.write("<H2>Current Time:</H2>");
+ Calendar c = new GregorianCalendar();
+ out.write("<CENTER><H3><FONT color=\"blue\">");
+ out.write(c.getTime().toString());
+ out.write("</FONT></H3></CENTER><HR>");
+ out.write("<P><A HREF=\"demo.html\">Return to the home page.</A>");
+ }
+}
+*/
diff --git a/sample/evolve/demo.html b/sample/evolve/demo.html new file mode 100644 index 00000000..6be4a2c3 --- /dev/null +++ b/sample/evolve/demo.html @@ -0,0 +1,38 @@ +<H2>Class Evolution</H2>
+
+<P>This is a demonstration of the class evolution mechanism
+implemented with Javassist. This mechanism enables a Java program to
+reload an existing class file. Although the reloaded class file is
+not applied to exiting objects (the old class file is used for those
+objects), it is effective in newly created objects.
+
+<P>Since the reloading is transparently executed, no programming
+convention is needed. However, there are some limitations on possible
+changes of the class definition. For example, the new class definition
+must have the same set of methods as the old one. These limitations are
+necessary for keeping the type system consistent.
+
+
+<H3><a href="java.html">Run WebPage.show()</a></H3>
+
+<P>The web server creates a new <code>WebPage</code> object and
+calls <code>show()</code> on that object. This method works as
+if it is a CGI script or a servlet and you will see the html file
+produced by this method on your browser.
+
+<H3><a href="update.html">Change WebPage.class</a></H3>
+
+<P>The web server overwrites class file <code>WebPage.class</code>
+on the local disk. Then it signals that <code>WebPage.class</code>
+must be reloaded into the JVM. If you run <code>WebPage.show()</code>
+again, you will see a different page on your browser.
+
+<H3>Source files</H3>
+
+<P>Web server: <A HREF="DemoServer.java"><code>DemoServer.java</code></A>
+
+<P>WebPage: <A HREF="WebPage.java"><code>WebPage.java</code></A>
+
+<P>Class loader: <A HREF="DemoLoader.java"><code>DemoLoader.java</code></A>,
+ <A HREF="Evolution.java"><code>Evolution.java</code></A>, and
+ <A HREF="VersionManager.java"><code>VersionManager.java</code></A>.
diff --git a/sample/evolve/start.html b/sample/evolve/start.html new file mode 100644 index 00000000..d31d9d08 --- /dev/null +++ b/sample/evolve/start.html @@ -0,0 +1,23 @@ +<h2>Instructions</h2>
+
+<p>1. Compile <code>sample/evolve/*.java</code>.
+ Copy <code>WebPage.class</code> to <code>WebPage.class.0</code>.
+
+<p>2. Edit <code>Webpage.java</code>, compile it,
+ and copy <code>WebPage.class</code> to <code>WebPage.class.1</code>.
+<br><code>WebPage.class.0</code> and
+ <code>WebPage.class.1</code> are used
+ for changing the contents of <code>WebPage.class</code> during
+ the demo.
+
+<p>3. Run the server on the local host (where your web browser is running):
+
+<ul>
+<code>% java sample.evolve.DemoLoader 5003
+</code></ul>
+
+<p>4. Click below:
+
+<ul><h2><a href="http://localhost:5003/demo.html">
+Start!
+</a></h2></ul>
diff --git a/sample/evolve/update.html b/sample/evolve/update.html new file mode 100644 index 00000000..82551653 --- /dev/null +++ b/sample/evolve/update.html @@ -0,0 +1,3 @@ +<h2><code>WebPage.class</code> has been changed.</h2>
+
+<a href="demo.html">Return to the home page.</a>
|