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