diff src/org/dancres/blitz/mangler/EntryMangler.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/dancres/blitz/mangler/EntryMangler.java	Sat Mar 21 11:00:06 2009 +0000
@@ -0,0 +1,252 @@
+package org.dancres.blitz.mangler;
+
+import java.io.IOException;
+
+import java.rmi.server.RMIClassLoader;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import java.util.WeakHashMap;
+import java.util.Map;
+
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+import net.jini.core.entry.Entry;
+import net.jini.core.entry.UnusableEntryException;
+
+import net.jini.loader.ClassLoading;
+
+/**
+   This class takes an Entry instance and reduces it down into it's
+   constituent parts (MangledEntry) which can be stored in the server-side.
+
+   @todo Hints about the kind of indexing for an Entry type are an
+   interesting idea - sometimes we're dealing with a queue, sometimes
+   hashing and sometimes neither.  We could achieve it with a simple
+   static field with a fixed name and a possible set of values.  Non-standard
+   but interesting......We could even set a default of indexed so we can
+   implement it and do something sane by default but possibly more optimal
+   with a hint.
+
+   @todo Consider adding test for Entry being a public class with a public
+   no-args constructor
+ */
+public class EntryMangler {
+    private static final Logger theLogger =
+            Logger.getLogger("org.dancres.blitz.mangler.EntryMangler");
+
+    private static final int INVALID_MODIFIERS = Modifier.TRANSIENT |
+        Modifier.STATIC | Modifier.FINAL;
+
+    private static final Comparator theFieldSorter = new FieldSorter();
+
+    private static EntryMangler theMangler = new EntryMangler();
+
+    public static EntryMangler getMangler() {
+        return theMangler;
+    }
+
+    /**
+       DEBUG AND TESTING ONLY
+     */
+    public EntryMangler() {
+    }
+
+    public MangledEntry mangle(Entry anEntry) {
+        return mangle(anEntry, false);
+    }
+
+    /**
+     * @param isSnapshot indicates whether this mangle is for a snapshot operation on
+     * a template Entry 
+     */
+    public MangledEntry mangle(Entry anEntry, boolean isSnapshot) {
+        Class myEntryClass = anEntry.getClass();
+        String myClassName = myEntryClass.getName();
+        String myCodebase = RMIClassLoader.getClassAnnotation(myEntryClass);
+
+        MangledField[] myEntryFields = getFields(anEntry);
+        String[] myParents = getParents(myEntryClass);
+
+        // Is it wildcard - i.e. all fields are null
+        boolean isWildcard = true;
+        for (int i = 0; i < myEntryFields.length; i++) {
+            if (! myEntryFields[i].isNull()) {
+                isWildcard = false;
+                break;
+            }
+        }
+
+        return new MangledEntry(myClassName, myCodebase, myEntryFields,
+                                myParents, isWildcard, isSnapshot);
+    }
+
+    public Entry unMangle(MangledEntry anME) throws UnusableEntryException {
+        try {
+            Class myEntryClass =
+                ClassLoading.loadClass(anME.getCodebase(), anME.getType(),
+                                       null, anME.needsIntegrityCheck(), null);
+
+            Entry myEntry = (Entry) myEntryClass.newInstance();
+
+            Field[] myFields = getFields(myEntryClass);
+
+            MangledField[] myMangledFields = anME.getFields();
+            int myNextField = 0;
+
+            for (int i = 0; i < myFields.length; i++) {
+                if (theLogger.isLoggable(Level.FINEST)) {
+                    theLogger.finest("Looking at field: " +
+                            myFields[i].getName());
+                    theLogger.finest("Valid:" +
+                            myMangledFields[myNextField]);
+                    theLogger.finest("Hash: " +
+                            myMangledFields[myNextField].hashCode());
+                }
+                if (! myMangledFields[myNextField].isNull()) {
+                    if (theLogger.isLoggable(Level.FINEST)) {
+                        theLogger.finest("Not null");
+                        theLogger.finest("Copy from: " +
+                                myMangledFields[myNextField].getName());
+                        theLogger.finest("Value will be: " +
+                                myMangledFields[myNextField].unMangle(null,
+                                        anME.needsIntegrityCheck()));
+                    }
+                    myFields[i].set(myEntry,
+                                    myMangledFields[myNextField++].unMangle(null,
+                                            anME.needsIntegrityCheck()));
+                } else
+                    myNextField++;
+            }
+
+            return myEntry;
+        } catch (Exception anE) {
+            theLogger.log(Level.SEVERE, "Error during unpack: ", anE);
+            throw new UnusableEntryException(anE);
+        }
+    }
+
+    private static Map theParentsCache = new WeakHashMap();
+
+    private String[] getParents(Class aClass) {
+        String[] myParents = null;
+
+        synchronized(theParentsCache) {
+            myParents = (String[]) theParentsCache.get(aClass);
+
+            if (myParents == null) {
+                ArrayList mySuperClasses = new ArrayList();
+
+                for(Class myCurrentClass = aClass.getSuperclass();
+                    myCurrentClass != null;
+                    myCurrentClass = myCurrentClass.getSuperclass()) {
+
+                    mySuperClasses.add(myCurrentClass.getName());
+                }
+
+                myParents = new String[mySuperClasses.size()];
+                Object[] myClasses = mySuperClasses.toArray();
+                System.arraycopy(myClasses, 0, myParents, 0, myParents.length);
+
+                theParentsCache.put(aClass, myParents);
+            }
+        }
+
+        return myParents;
+    }
+
+    /**
+       We wish to sort fields into order with those of super-class before
+       subclass.  We're using alphabetical order for fields within one
+       particular class but, in reality, any predicatable ordering would do.
+     */
+    private static class FieldSorter implements Comparator {
+        public int compare(Object anObject, Object anotherObject) {
+            Field myFirst = (Field) anObject;
+            Field mySecond = (Field) anotherObject;
+
+            if (myFirst == mySecond)
+                return 0;
+            else if (myFirst.getDeclaringClass() ==
+                     mySecond.getDeclaringClass()) {
+                return myFirst.getName().compareTo(mySecond.getName());
+            } else if (myFirst.getDeclaringClass().isAssignableFrom(mySecond.getDeclaringClass())) {
+                return -1;
+            } else {
+                return 1;
+            }
+        }
+    }
+
+    private static Map theFieldCache = new WeakHashMap();
+
+    private Field[] getFields(Class aClass) {
+        synchronized(theFieldCache) {
+            Field[] myFields = (Field[]) theFieldCache.get(aClass);
+
+            if (myFields == null) {
+                myFields = aClass.getFields();
+                Arrays.sort(myFields, theFieldSorter);
+
+                ArrayList myRelevant = new ArrayList();
+
+                for (int i = 0; i < myFields.length; i++) {
+                    if (isValid(myFields[i]))
+                        myRelevant.add(myFields[i]);
+                }
+
+                myFields = new Field[myRelevant.size()];
+                myFields = (Field[]) myRelevant.toArray(myFields);
+                theFieldCache.put(aClass, myFields);
+            }
+
+            return myFields;
+        }
+    }
+
+    private MangledField[] getFields(Entry anEntry) {
+        Field[] myFields = getFields(anEntry.getClass());
+
+        MangledField[] myValidFields =
+            new MangledField[myFields.length];
+
+        int i = 0;
+        try {
+            for (i = 0; i < myFields.length; i++) {
+                Object myFieldValue = myFields[i].get(anEntry);
+                String myFieldName = myFields[i].getName();
+
+                if (myFieldValue == null)
+                    myValidFields[i] =
+                        new MangledField(myFieldName);
+                else
+                    myValidFields[i] =
+                        new MangledField(myFieldName, myFieldValue);
+            }
+        } catch (IllegalAccessException anIAE) {
+            theLogger.log(Level.SEVERE, "Problem accessing field", anIAE);
+            throw new InternalError("Couldn't access field: " + myFields[i]);
+        } catch (IOException anIOE) {
+            theLogger.log(Level.SEVERE, "Fatal error", anIOE);
+            throw new InternalError("Fatal error: " + anIOE);
+        }
+
+        return myValidFields;
+    }
+
+    private boolean isValid(Field aField) {
+        if ((aField.getModifiers() & INVALID_MODIFIERS) != 0)
+            return false;
+
+        if (aField.getType().isPrimitive())
+            return false;
+
+        return true;
+    }
+}