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 | |
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')
31 files changed, 1494 insertions, 0 deletions
diff --git a/sample/Test.java b/sample/Test.java new file mode 100644 index 00000000..0692943f --- /dev/null +++ b/sample/Test.java @@ -0,0 +1,44 @@ +package sample;
+
+import javassist.*;
+
+/*
+ A very simple sample program
+
+ This program overwrites sample/Test.class (the class file of this
+ class itself) for adding a method g(). If the method g() is not
+ defined in class Test, then this program adds a copy of
+ f() to the class Test with name g(). Otherwise, this program does
+ not modify sample/Test.class at all.
+
+ To see the modified class definition, execute:
+
+ % javap sample.Test
+
+ after running this program.
+*/
+public class Test {
+ public int f(int i) {
+ return i + 1;
+ }
+
+ public static void main(String[] args) throws Exception {
+ ClassPool pool = ClassPool.getDefault(null);
+
+ CtClass cc = pool.get("sample.Test");
+ try {
+ cc.getDeclaredMethod("g");
+ System.out.println("g() is already defined in sample.Test.");
+ }
+ catch (NotFoundException e) {
+ /* getDeclaredMethod() throws an exception if g()
+ * is not defined in sample.Test.
+ */
+ CtMethod fMethod = cc.getDeclaredMethod("f");
+ CtMethod gMethod = CtNewMethod.copy(fMethod, "g", cc, null);
+ cc.addMethod(gMethod);
+ pool.writeFile("sample.Test"); // update the class file
+ System.out.println("g() was added.");
+ }
+ }
+}
diff --git a/sample/duplicate/Ball.java b/sample/duplicate/Ball.java new file mode 100644 index 00000000..21d6e1cf --- /dev/null +++ b/sample/duplicate/Ball.java @@ -0,0 +1,44 @@ +package sample.duplicate;
+
+import java.awt.Graphics;
+import java.awt.Color;
+
+public class Ball {
+ private int x, y;
+ private Color color;
+ private int radius = 30;
+ private boolean isBackup = false;
+
+ public Ball(int x, int y) {
+ move(x, y);
+ changeColor(Color.orange);
+ }
+
+ // This constructor is for a backup object.
+ public Ball(Ball b) {
+ isBackup = true;
+ }
+
+ // Adjust the position so that the backup object is visible.
+ private void adjust() {
+ if (isBackup) {
+ this.x += 50;
+ this.y += 50;
+ }
+ }
+
+ public void paint(Graphics g) {
+ g.setColor(color);
+ g.fillOval(x, y, radius, radius);
+ }
+
+ public void move(int x, int y) {
+ this.x = x;
+ this.y = y;
+ adjust();
+ }
+
+ public void changeColor(Color color) {
+ this.color = color;
+ }
+}
diff --git a/sample/duplicate/DuplicatedObject.java b/sample/duplicate/DuplicatedObject.java new file mode 100644 index 00000000..5995abcc --- /dev/null +++ b/sample/duplicate/DuplicatedObject.java @@ -0,0 +1,38 @@ +package sample.duplicate;
+
+import javassist.reflect.*;
+
+public class DuplicatedObject extends Metaobject {
+ private DuplicatedObject backup;
+
+ // if a base-level object is created, this metaobject creates
+ // a copy of the base-level object.
+
+ public DuplicatedObject(Object self, Object[] args) {
+ super(self, args);
+ ClassMetaobject clazz = getClassMetaobject();
+ if (clazz.isInstance(args[0]))
+ backup = null; // self is a backup object.
+ else {
+ Object[] args2 = new Object[1];
+ args2[0] = self;
+ try {
+ Metalevel m = (Metalevel)clazz.newInstance(args2);
+ backup = (DuplicatedObject)m._getMetaobject();
+ }
+ catch (CannotCreateException e) {
+ backup = null;
+ }
+ }
+ }
+
+ public Object trapMethodcall(int identifier, Object[] args)
+ throws Throwable
+ {
+ Object obj = super.trapMethodcall(identifier, args);
+ if (backup != null)
+ backup.trapMethodcall(identifier, args);
+
+ return obj;
+ }
+}
diff --git a/sample/duplicate/Main.java b/sample/duplicate/Main.java new file mode 100644 index 00000000..e152a23e --- /dev/null +++ b/sample/duplicate/Main.java @@ -0,0 +1,44 @@ +package sample.duplicate;
+
+/*
+ Runtime metaobject (JDK 1.2 or later only).
+
+ With the javassist.reflect package, the users can attach a metaobject
+ to an object. The metaobject can control the behavior of the object.
+ For example, you can implement fault tolerancy with this ability. One
+ of the implementation techniques of fault tolernacy is to make a copy
+ of every object containing important data and maintain it as a backup.
+ If the machine running the object becomes down, the backup object on a
+ different machine is used to continue the execution.
+
+ To make the copy of the object a real backup, all the method calls to
+ the object must be also sent to that copy. The metaobject is needed
+ for this duplication of the method calls. It traps every method call
+ and invoke the same method on the copy of the object so that the
+ internal state of the copy is kept equivalent to that of the original
+ object.
+
+ First, run sample.duplicate.Viewer without a metaobject.
+
+ % java sample.duplicate.Viewer
+
+ This program shows a ball in a window.
+
+ Then, run the same program with a metaobject, which is an instance
+ of sample.duplicate.DuplicatedObject.
+
+ % java sample.duplicate.Main
+
+ You would see two balls in a window. This is because
+ sample.duplicate.Viewer is loaded by javassist.reflect.Loader so that
+ a metaobject would be attached.
+*/
+public class Main {
+ public static void main(String[] args) throws Throwable {
+ javassist.reflect.Loader cl = new javassist.reflect.Loader();
+ cl.makeReflective("sample.duplicate.Ball",
+ "sample.duplicate.DuplicatedObject",
+ "javassist.reflect.ClassMetaobject");
+ cl.run("sample.duplicate.Viewer", args);
+ }
+}
diff --git a/sample/duplicate/Viewer.java b/sample/duplicate/Viewer.java new file mode 100644 index 00000000..aec13f6b --- /dev/null +++ b/sample/duplicate/Viewer.java @@ -0,0 +1,78 @@ +package sample.duplicate;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+
+public class Viewer extends Applet
+ implements MouseListener, ActionListener, WindowListener
+{
+ private static final Color[] colorList = {
+ Color.orange, Color.pink, Color.green, Color.blue };
+
+ private Ball ball;
+ private int colorNo;
+
+ public void init() {
+ colorNo = 0;
+ Button b = new Button("change");
+ b.addActionListener(this);
+ add(b);
+
+ addMouseListener(this);
+ }
+
+ public void start() {
+ ball = new Ball(50, 50);
+ ball.changeColor(colorList[0]);
+ }
+
+ public void paint(Graphics g) {
+ ball.paint(g);
+ }
+
+ public void mouseClicked(MouseEvent ev) {
+ ball.move(ev.getX(), ev.getY());
+ repaint();
+ }
+
+ public void mouseEntered(MouseEvent ev) {}
+
+ public void mouseExited(MouseEvent ev) {}
+
+ public void mousePressed(MouseEvent ev) {}
+
+ public void mouseReleased(MouseEvent ev) {}
+
+ public void actionPerformed(ActionEvent e) {
+ ball.changeColor(colorList[++colorNo % colorList.length]);
+ repaint();
+ }
+
+ public void windowOpened(WindowEvent e) {}
+
+ public void windowClosing(WindowEvent e) {
+ System.exit(0);
+ }
+
+ public void windowClosed(WindowEvent e) {}
+
+ public void windowIconified(WindowEvent e) {}
+
+ public void windowDeiconified(WindowEvent e) {}
+
+ public void windowActivated(WindowEvent e) {}
+
+ public void windowDeactivated(WindowEvent e) {}
+
+ public static void main(String[] args) {
+ Frame f = new Frame("Viewer");
+ Viewer view = new Viewer();
+ f.addWindowListener(view);
+ f.add(view);
+ f.setSize(300, 300);
+ view.init();
+ view.start();
+ f.setVisible(true);
+ }
+}
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>
diff --git a/sample/reflect/Main.java b/sample/reflect/Main.java new file mode 100644 index 00000000..6086d9f8 --- /dev/null +++ b/sample/reflect/Main.java @@ -0,0 +1,33 @@ +package sample.reflect;
+
+import javassist.reflect.ClassMetaobject;
+import javassist.reflect.Loader;
+
+/*
+ The "verbose metaobject" example (JDK 1.2 or later only).
+
+ Since this program registers class Person as a reflective class
+ (in a more realistic demonstration, what classes are reflective
+ would be specified by some configuration file), the class loader
+ modifies Person.class when loading into the JVM so that the class
+ Person is changed into a reflective class and a Person object is
+ controlled by a VerboseMetaobj.
+
+ To run,
+
+ % java javassist.reflect.Loader sample.reflect.Main Joe
+
+ Compare this result with that of the regular execution without reflection:
+
+ % java sample.reflect.Person Joe
+*/
+public class Main {
+ public static void main(String[] args) throws Throwable {
+ Loader cl = (Loader)Main.class.getClassLoader();
+ cl.makeReflective("sample.reflect.Person",
+ "sample.reflect.VerboseMetaobj",
+ "javassist.reflect.ClassMetaobject");
+
+ cl.run("sample.reflect.Person", args);
+ }
+}
diff --git a/sample/reflect/Person.java b/sample/reflect/Person.java new file mode 100644 index 00000000..5e0e2ad8 --- /dev/null +++ b/sample/reflect/Person.java @@ -0,0 +1,51 @@ +/*
+ A base-level class controlled by VerboseMetaobj.
+*/
+
+package sample.reflect;
+
+import javassist.reflect.Metalevel;
+import javassist.reflect.Metaobject;
+
+public class Person {
+ public String name;
+ public static int birth = 3;
+ public static final String defaultName = "John";
+
+ public Person(String name, int birthYear) {
+ if (name == null)
+ this.name = defaultName;
+ else
+ this.name = name;
+
+ this.birth = birthYear;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge(int year) {
+ return year - birth;
+ }
+
+ public static void main(String[] args) {
+ String name;
+ if (args.length > 0)
+ name = args[0];
+ else
+ name = "Bill";
+
+ Person p = new Person(name, 1960);
+ System.out.println("name: " + p.getName());
+ System.out.println("object: " + p.toString());
+
+ // change the metaobject of p.
+ if (p instanceof Metalevel) {
+ ((Metalevel)p)._setMetaobject(new Metaobject(p, null));
+ System.out.println("<< the metaobject was changed.>>");
+ }
+
+ System.out.println("age: " + p.getAge(1999));
+ }
+}
diff --git a/sample/reflect/VerboseMetaobj.java b/sample/reflect/VerboseMetaobj.java new file mode 100644 index 00000000..a9f75dd1 --- /dev/null +++ b/sample/reflect/VerboseMetaobj.java @@ -0,0 +1,29 @@ +package sample.reflect;
+
+import javassist.*;
+import javassist.reflect.*;
+
+public class VerboseMetaobj extends Metaobject {
+ public VerboseMetaobj(Object self, Object[] args) {
+ super(self, args);
+ System.out.println("** constructed: " + self.getClass().getName());
+ }
+
+ public Object trapFieldRead(String name) {
+ System.out.println("** field read: " + name);
+ return super.trapFieldRead(name);
+ }
+
+ public void trapFieldWrite(String name, Object value) {
+ System.out.println("** field write: " + name);
+ super.trapFieldWrite(name, value);
+ }
+
+ public Object trapMethodcall(int identifier, Object[] args)
+ throws Throwable
+ {
+ System.out.println("** trap: " + getMethodName(identifier)
+ + "() in " + getClassMetaobject().getName());
+ return super.trapMethodcall(identifier, args);
+ }
+}
diff --git a/sample/rmi/AlertDialog.java b/sample/rmi/AlertDialog.java new file mode 100644 index 00000000..99fae5c2 --- /dev/null +++ b/sample/rmi/AlertDialog.java @@ -0,0 +1,30 @@ +package sample.rmi;
+
+import java.awt.*;
+import java.awt.event.*;
+
+public class AlertDialog extends Frame implements ActionListener {
+ private Label label;
+
+ public AlertDialog() {
+ super("Alert");
+ setSize(200, 100);
+ setLocation(100, 100);
+ label = new Label();
+ Button b = new Button("OK");
+ b.addActionListener(this);
+ Panel p = new Panel();
+ p.add(b);
+ add("North", label);
+ add("South", p);
+ }
+
+ public void show(String message) {
+ label.setText(message);
+ setVisible(true);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ setVisible(false);
+ }
+}
diff --git a/sample/rmi/CountApplet.java b/sample/rmi/CountApplet.java new file mode 100644 index 00000000..0bebdaf9 --- /dev/null +++ b/sample/rmi/CountApplet.java @@ -0,0 +1,83 @@ +package sample.rmi;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import javassist.rmi.ObjectImporter;
+import javassist.rmi.ObjectNotFoundException;
+import javassist.web.Viewer;
+
+public class CountApplet extends Applet implements ActionListener {
+ private Font font;
+ private ObjectImporter importer;
+ private Counter counter;
+ private AlertDialog dialog;
+ private String message;
+
+ private String paramButton;
+ private String paramName;
+
+ public void init() {
+ paramButton = getParameter("button");
+ paramName = getParameter("name");
+ importer = new ObjectImporter(this);
+ commonInit();
+ }
+
+ /* call this method instead of init() if this program is not run
+ * as an applet.
+ */
+ public void applicationInit() {
+ paramButton = "OK";
+ paramName = "counter";
+ Viewer cl = (Viewer)getClass().getClassLoader();
+ importer = new ObjectImporter(cl.getServer(), cl.getPort());
+ commonInit();
+ }
+
+ private void commonInit() {
+ font = new Font("SansSerif", Font.ITALIC, 40);
+ Button b = new Button(paramButton);
+ b.addActionListener(this);
+ add(b);
+ dialog = new AlertDialog();
+ message = "???";
+ }
+
+ public void destroy() {
+ dialog.dispose();
+ }
+
+ public void start() {
+ try {
+ counter = (Counter)importer.lookupObject(paramName);
+ message = Integer.toString(counter.get());
+ }
+ catch (ObjectNotFoundException e) {
+ dialog.show(e.toString());
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ counter.increase();
+ message = Integer.toString(counter.get());
+ repaint();
+ }
+
+ public void paint(Graphics g) {
+ g.setFont(font);
+ g.drawRect(50, 50, 100, 100);
+ g.setColor(Color.blue);
+ g.drawString(message, 60, 120);
+ }
+
+ public static void main(String[] args) {
+ Frame f = new Frame("CountApplet");
+ CountApplet ca = new CountApplet();
+ f.add(ca);
+ f.setSize(300, 300);
+ ca.applicationInit();
+ ca.start();
+ f.setVisible(true);
+ }
+}
diff --git a/sample/rmi/Counter.java b/sample/rmi/Counter.java new file mode 100644 index 00000000..f8a0fcf5 --- /dev/null +++ b/sample/rmi/Counter.java @@ -0,0 +1,32 @@ +package sample.rmi;
+
+import javassist.rmi.AppletServer;
+import java.io.IOException;
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
+
+public class Counter {
+ private int count = 0;
+
+ public int get() {
+ return count;
+ }
+
+ synchronized public int increase() {
+ count += 1;
+ return count;
+ }
+
+ public static void main(String[] args)
+ throws IOException, NotFoundException, CannotCompileException
+ {
+ if (args.length == 1) {
+ AppletServer web = new AppletServer(args[0]);
+ web.exportObject("counter", new Counter());
+ web.run();
+ }
+ else
+ System.err.println(
+ "Usage: java sample.rmi.Counter <port number>");
+ }
+}
diff --git a/sample/rmi/inside.gif b/sample/rmi/inside.gif Binary files differnew file mode 100644 index 00000000..c69c8ee8 --- /dev/null +++ b/sample/rmi/inside.gif diff --git a/sample/rmi/start.html b/sample/rmi/start.html new file mode 100644 index 00000000..696b629c --- /dev/null +++ b/sample/rmi/start.html @@ -0,0 +1,15 @@ +<h2>Instructions</h2>
+
+<p>1. Run the server on the local host (where your web browser is running):
+
+<ul>% java sample.rmi.Counter 5001</ul>
+
+<p>2. Click below:
+
+<h2><a href="webdemo.html">
+Start!
+</a></h2>
+
+<p>If you don't want to use a web browser, do as follows:
+
+<ul><pre>% java javassist.web.Viewer localhost 5001 sample.rmi.CountApplet</pre></ul>
diff --git a/sample/rmi/webdemo.html b/sample/rmi/webdemo.html new file mode 100644 index 00000000..d313ec3c --- /dev/null +++ b/sample/rmi/webdemo.html @@ -0,0 +1,203 @@ +<html>
+<body>
+<h2>Remote Method Invocation</h2>
+
+<P>Javassist enables an applet to access a remote object as if it is a
+local object. The applet can communicate through a socket with the
+host that executes the web server distributing that applet. However,
+the applet cannot directly call a method on an object if the object is
+on a remote host. The <code>javassist.rmi</code> package provides
+a mechanism for the applet to transparently access the remote object.
+The rules that the applet must be subject to are simpler than the
+standard Java RMI.
+
+<h3>1. Sample applet</h3>
+
+<P>The applet showing below is a simple number counter.
+If you press the button, the number is increased by one.
+An interesting feature of this applet is that the object
+recording the current number is contained by the web server
+written in Java. The applet must access the object through a socket
+to obtain the current number.
+
+<p><center>
+<applet codebase="http://localhost:5001"
+code="sample.rmi.CountApplet" width=200 height=200>
+<param name=name value="counter">
+<param name=button value="+1">
+</applet>
+</center>
+
+<p>However, the program of the applet does not need to directly handle
+a socket. The <code>ObjectImporter</code> provided by Javassist deals
+with all the awkward programming.
+Look at the lines shown with red:
+
+<p><b>Figure 1: Applet</b>
+
+<pre>
+<font color="red">import javassist.rmi.ObjectImporter;</font>
+
+public class CountApplet extends Applet implements ActionListener {
+ private Font font;
+ <font color="red">private ObjectImporter importer;
+ private Counter counter;</font>
+ private AlertDialog dialog;
+ private String message;
+
+ public void init() {
+ font = new Font("SansSerif", Font.ITALIC, 40);
+ Button b = new Button(getParameter("button"));
+ b.addActionListener(this);
+ add(b);
+ <font color="red">importer = new ObjectImporter(this);</font>
+ dialog = new AlertDialog();
+ message = "???";
+ }
+
+ public void start() {
+ String counterName = getParameter("name");
+ <font color="red">counter = (Counter)importer.getObject(counterName);</font>
+ message = Integer.toString(<font color="red">counter.get()</font>);
+ }
+
+ /* The method called when the button is pressed.
+ */
+ public void actionPerformed(ActionEvent e) {
+ message = Integer.toString(<font color="red">counter.increase()</font>);
+ repaint();
+ }
+
+ public void paint(Graphics g) {
+ g.setFont(font);
+ g.drawRect(50, 50, 100, 100);
+ g.setColor(Color.blue);
+ g.drawString(message, 60, 120);
+ }
+}
+</pre>
+
+<p>A <code>Counter</code> object running on a remote host
+maintains the counter number. To access this object, the applet first
+calls <code>getObject()</code> on an <code>ObjectImporter</code>
+to obtain a reference to the object. The parameter is the name associated
+with the object by the web server. Once the reference is obtained, it
+is delt with as if it is a reference to a local object.
+For example, <code>counter.get()</code> and <code>counter.increase()</code>
+call methods on the remote object.
+
+<p>The definition of the <code>Counter</code> class is also
+straightforward:
+
+<p><b>Figure 2: Remote object</b>
+
+<pre>
+public class Counter {
+ private int count = 0;
+
+ public int get() {
+ return count;
+ }
+
+ public int increase() {
+ count += 1;
+ return count;
+ }
+}
+</pre>
+
+<p>Note that the <code>javassist.rmi</code> package does not require
+the <code>Counter</code> class to be an interface unlike the Java RMI,
+with which <code>Counter</code> must be an interface and it must be
+implemented by another class.
+
+<p>To make the <code>Counter</code> object available from the applet,
+it must be registered with the web server. A <code>AppletServer</code>
+object is a simple webserver that can distribute <code>.html</code> files
+and <code>.class</code> files (Java applets).
+
+<p><b>Figure 3: Server-side program</b>
+
+<pre>
+public class MyWebServer {
+ public static void main(String[] args) throws IOException, CannotCompileException
+ {
+ AppletServer web = new AppletServer(args[0]);
+ <font color="red">web.exportObject("counter", new Counter());</font>
+ web.run();
+ }
+}
+</pre>
+
+<p>The <code>exportObject()</code> method registers a remote object
+with the <code>AppletServer</code> object. In the example above,
+a <code>Counter</code> object is registered. The applet can access
+the object with the name "counter". The web server starts the service
+if the <code>run()</code> method is called.
+
+<p><br>
+
+<h3>2. Features</h3>
+
+The remote method invocation mechanism provided by Javassist has the
+following features:
+
+<ul>
+<li><b>Regular Java syntax:</b><br>
+ The applet can call a method on a remote object with regular
+ Java syntax.
+<p>
+
+<li><b>No special naming convention:</b><br>
+ The applet can use the same class name as the server-side program.
+ The reference object to a remote <code>Foo</code> object is
+ also represented by the class <code>Foo</code>.
+ Unlike other similar
+ systems, it is not represented by a different class such as
+ <code>ProxyFoo</code> or an interface implemented by
+ <code>Foo</code>.
+<p>
+
+<li><b>No extra compiler:</b><br>
+ All the programs, both the applet and the server-side program,
+ are compiled by the regular Java compiler. No external compiler
+ is needed.
+</ul>
+
+<p> With the Java RMI or Voyager, the applet programmer must define
+an interface for every remote object class and access the remote object
+through that interface.
+On the other hand, the <code>javassist.rmi</code> package does not
+require the programmer to follow that programming convention.
+It is suitable for writing simple distributed programs like applets.
+
+<p><br>
+
+<h3>3. Inside of the system</h3>
+
+<p>A key idea of the implementation is that the applet and the server-side
+program must use different versions of the class <code>Counter</code>.
+The <code>Counter</code> object in the applet must work as a proxy
+object, which transfers the method invocations to the <code>Counter</code>
+object in the server-side program.
+
+<p>With other systems like the Java RMI, the class of this proxy object is
+produced by a special compiler such as <code>rmic</code>.
+It must be manually maintained by the programmer.
+
+<center><img src="inside.gif"></center>
+
+<p>However, Javassist automatically generates the proxy class at
+runtime so that the programmer does not have to be concerned about the
+maintenance of the proxy class.
+If the web browser running the applet
+requests to load the <code>Counter</code> class, which is the class
+of an exported object,
+then the web server
+transfers the version of <code>Counter</code> that Javassist generates
+as a proxy class.
+
+<p><br>
+
+</body>
+</html>
diff --git a/sample/vector/Sample.java b/sample/vector/Sample.java new file mode 100644 index 00000000..7a47aadf --- /dev/null +++ b/sample/vector/Sample.java @@ -0,0 +1,14 @@ +package sample.vector;
+
+public class Sample extends java.util.Vector {
+ public void add(X e) {
+ super.addElement(e);
+ }
+
+ public X at(int i) {
+ return (X)super.elementAt(i);
+ }
+}
+
+class X {
+}
diff --git a/sample/vector/Sample2.java b/sample/vector/Sample2.java new file mode 100644 index 00000000..dd5c965e --- /dev/null +++ b/sample/vector/Sample2.java @@ -0,0 +1,13 @@ +package sample.vector;
+
+public class Sample2 extends java.util.Vector {
+ public Object add(Object[] args) {
+ super.addElement(args[0]);
+ return null;
+ }
+
+ public Object at(Object[] args) {
+ int i = ((Integer)args[0]).intValue();
+ return super.elementAt(i);
+ }
+}
diff --git a/sample/vector/Test.j b/sample/vector/Test.j new file mode 100644 index 00000000..6f524c93 --- /dev/null +++ b/sample/vector/Test.j @@ -0,0 +1,38 @@ +/*
+ A sample program using sample.vector.VectorAssistant
+ and the javassist.preproc package.
+
+ This automatically produces the classes representing vectors of integer
+ and vectors of java.lang.String.
+
+ To compile and run this program, do as follows:
+
+ % java javassist.tool.Compiler sample/vector/Test.j
+ % javac sample/vector/Test.java
+ % java sample.vector.Test
+
+ The first line produces one source file (sample/Test.java) and
+ two class files (sample/vector/intVector.class and
+ sample/vector/StringVector.class).
+*/
+
+package sample.vector;
+
+import java.util.Vector by sample.vector.VectorAssistant(java.lang.String);
+import java.util.Vector by sample.vector.VectorAssistant(int);
+
+public class Test {
+ public static void main(String[] args) {
+ intVector iv = new intVector();
+ iv.add(3);
+ iv.add(4);
+ for (int i = 0; i < iv.size(); ++i)
+ System.out.println(iv.at(i));
+
+ StringVector sv = new StringVector();
+ sv.add("foo");
+ sv.add("bar");
+ for (int i = 0; i < sv.size(); ++i)
+ System.out.println(sv.at(i));
+ }
+}
diff --git a/sample/vector/VectorAssistant.java b/sample/vector/VectorAssistant.java new file mode 100644 index 00000000..44fdd41c --- /dev/null +++ b/sample/vector/VectorAssistant.java @@ -0,0 +1,135 @@ +package sample.vector;
+
+import java.io.IOException;
+import javassist.*;
+import javassist.preproc.Assistant;
+
+/**
+ * This is a Javassist program which produce a new class representing
+ * vectors of a given type. For example,
+ *
+ * <ul>import java.util.Vector by sample.vector.VectorAssistant(int)</ul>
+ *
+ * <p>requests the Javassist preprocessor to substitute the following
+ * lines for the original import declaration:
+ *
+ * <ul><pre>
+ * import java.util.Vector;
+ * import sample.vector.intVector;
+ * </pre></ul>
+ *
+ * <p>The Javassist preprocessor calls <code>VectorAssistant.assist()</code>
+ * and produces class <code>intVector</code> equivalent to:
+ *
+ * <ul><pre>
+ * package sample.vector;
+ *
+ * public class intVector extends Vector {
+ * pubilc void add(int value) {
+ * addElement(new Integer(value));
+ * }
+ *
+ * public int at(int index) {
+ * return elementAt(index).intValue();
+ * }
+ * }
+ * </pre></ul>
+ *
+ * <p><code>VectorAssistant.assist()</code> uses
+ * <code>sample.vector.Sample</code> and <code>sample.vector.Sample2</code>
+ * as a template to produce the methods <code>add()</code> and
+ * <code>at()</code>.
+ */
+public class VectorAssistant implements Assistant {
+ public final String packageName = "sample.vector.";
+
+ /**
+ * Calls <code>makeSubclass()</code> and produces a new vector class.
+ * This method is called by a <code>javassist.preproc.Compiler</code>.
+ *
+ * @see javassist.preproc.Compiler
+ */
+ public CtClass[] assist(ClassPool pool, String vec, String[] args)
+ throws CannotCompileException
+ {
+ if (args.length != 1)
+ throw new CannotCompileException(
+ "VectorAssistant receives a single argument.");
+
+ try {
+ CtClass subclass;
+ CtClass elementType = pool.get(args[0]);
+ if (elementType.isPrimitive())
+ subclass = makeSubclass2(pool, elementType);
+ else
+ subclass = makeSubclass(pool, elementType);
+
+ CtClass[] results = { subclass, pool.get(vec) };
+ return results;
+ }
+ catch (NotFoundException e) {
+ throw new CannotCompileException(e);
+ }
+ catch (IOException e) {
+ throw new CannotCompileException(e);
+ }
+ }
+
+ /**
+ * Produces a new vector class. This method does not work if
+ * the element type is a primitive type.
+ *
+ * @param type the type of elements
+ */
+ public CtClass makeSubclass(ClassPool pool, CtClass type)
+ throws CannotCompileException, NotFoundException, IOException
+ {
+ CtClass vec = pool.makeClass(makeClassName(type));
+ vec.setSuperclass(pool.get("java.util.Vector"));
+
+ CtClass c = pool.get("sample.vector.Sample");
+ CtMethod addmethod = c.getDeclaredMethod("add");
+ CtMethod atmethod = c.getDeclaredMethod("at");
+
+ ClassMap map = new ClassMap();
+ map.put("sample.vector.X", type.getName());
+
+ vec.addMethod(CtNewMethod.copy(addmethod, "add", vec, map));
+ vec.addMethod(CtNewMethod.copy(atmethod, "at", vec, map));
+ pool.writeFile(vec.getName());
+ return vec;
+ }
+
+ /**
+ * Produces a new vector class. This uses wrapped methods so that
+ * the element type can be a primitive type.
+ *
+ * @param type the type of elements
+ */
+ public CtClass makeSubclass2(ClassPool pool, CtClass type)
+ throws CannotCompileException, NotFoundException, IOException
+ {
+ CtClass vec = pool.makeClass(makeClassName(type));
+ vec.setSuperclass(pool.get("java.util.Vector"));
+
+ CtClass c = pool.get("sample.vector.Sample2");
+ CtMethod addmethod = c.getDeclaredMethod("add");
+ CtMethod atmethod = c.getDeclaredMethod("at");
+
+ CtClass[] args1 = { type };
+ CtClass[] args2 = { CtClass.intType };
+ CtMethod m
+ = CtNewMethod.wrapped(CtClass.voidType, "add", args1,
+ null, addmethod, null, vec);
+ vec.addMethod(m);
+ m = CtNewMethod.wrapped(type, "at", args2,
+ null, atmethod, null, vec);
+ vec.addMethod(m);
+ pool.writeFile(vec.getName());
+ return vec;
+ }
+
+ private String makeClassName(CtClass type) {
+ return packageName + type.getSimpleName() + "Vector";
+ }
+}
|