169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpackage sample.evolve;
269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.util.Hashtable;
469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.lang.reflect.*;
569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/**
769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Runtime system for class evolution
869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpublic class VersionManager {
1069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static Hashtable versionNo = new Hashtable();
1169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final static String latestVersionField = "_version";
1369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
1569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * For updating the definition of class my.X, say:
1669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
1769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * VersionManager.update("my.X");
1869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
1969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static void update(String qualifiedClassname)
2069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throws CannotUpdateException {
2169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
2269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            Class c = getUpdatedClass(qualifiedClassname);
2369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            Field f = c.getField(latestVersionField);
2469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            f.set(null, c);
2569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
2669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (ClassNotFoundException e) {
2769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new CannotUpdateException("cannot update class: "
2869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    + qualifiedClassname);
2969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
3069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (Exception e) {
3169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new CannotUpdateException(e);
3269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
3369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
3469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
3569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static Class getUpdatedClass(String qualifiedClassname)
3669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throws ClassNotFoundException {
3769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int version;
3869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Object found = versionNo.get(qualifiedClassname);
3969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (found == null)
4069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            version = 0;
4169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        else
4269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            version = ((Integer)found).intValue() + 1;
4369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
4469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Class c = Class.forName(qualifiedClassname + "$$" + version);
4569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        versionNo.put(qualifiedClassname, new Integer(version));
4669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return c;
4769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
4869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
4969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /*
5069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * initiaVersion() is used to initialize the _version field of the updatable
5169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * classes.
5269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
5369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static Class initialVersion(String[] params) {
5469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
5569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return getUpdatedClass(params[0]);
5669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
5769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (ClassNotFoundException e) {
5869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException("cannot initialize " + params[0]);
5969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
6069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
6169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
6269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
6369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * make() performs the object creation of the updatable classes. The
6469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * expression "new <updatable class>" is replaced with a call to this
6569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * method.
6669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
6769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static Object make(Class clazz, Object[] args) {
6869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Constructor[] constructors = clazz.getConstructors();
6969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int n = constructors.length;
7069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (int i = 0; i < n; ++i) {
7169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            try {
7269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                return constructors[i].newInstance(args);
7369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
7469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            catch (IllegalArgumentException e) {
7569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                // try again
7669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
7769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            catch (InstantiationException e) {
7869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                throw new CannotCreateException(e);
7969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
8069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            catch (IllegalAccessException e) {
8169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                throw new CannotCreateException(e);
8269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
8369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            catch (InvocationTargetException e) {
8469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                throw new CannotCreateException(e);
8569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
8669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
8769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        throw new CannotCreateException("no constructor matches");
8969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
9069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal}
91