Mercurial > hg > blitz_condensed
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; + } +}