comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:3dc0c5604566
1 package org.dancres.blitz.mangler;
2
3 import java.io.IOException;
4
5 import java.rmi.server.RMIClassLoader;
6
7 import java.lang.reflect.Field;
8 import java.lang.reflect.Modifier;
9
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Comparator;
13
14 import java.util.WeakHashMap;
15 import java.util.Map;
16
17 import java.util.logging.Logger;
18 import java.util.logging.Level;
19
20 import net.jini.core.entry.Entry;
21 import net.jini.core.entry.UnusableEntryException;
22
23 import net.jini.loader.ClassLoading;
24
25 /**
26 This class takes an Entry instance and reduces it down into it's
27 constituent parts (MangledEntry) which can be stored in the server-side.
28
29 @todo Hints about the kind of indexing for an Entry type are an
30 interesting idea - sometimes we're dealing with a queue, sometimes
31 hashing and sometimes neither. We could achieve it with a simple
32 static field with a fixed name and a possible set of values. Non-standard
33 but interesting......We could even set a default of indexed so we can
34 implement it and do something sane by default but possibly more optimal
35 with a hint.
36
37 @todo Consider adding test for Entry being a public class with a public
38 no-args constructor
39 */
40 public class EntryMangler {
41 private static final Logger theLogger =
42 Logger.getLogger("org.dancres.blitz.mangler.EntryMangler");
43
44 private static final int INVALID_MODIFIERS = Modifier.TRANSIENT |
45 Modifier.STATIC | Modifier.FINAL;
46
47 private static final Comparator theFieldSorter = new FieldSorter();
48
49 private static EntryMangler theMangler = new EntryMangler();
50
51 public static EntryMangler getMangler() {
52 return theMangler;
53 }
54
55 /**
56 DEBUG AND TESTING ONLY
57 */
58 public EntryMangler() {
59 }
60
61 public MangledEntry mangle(Entry anEntry) {
62 return mangle(anEntry, false);
63 }
64
65 /**
66 * @param isSnapshot indicates whether this mangle is for a snapshot operation on
67 * a template Entry
68 */
69 public MangledEntry mangle(Entry anEntry, boolean isSnapshot) {
70 Class myEntryClass = anEntry.getClass();
71 String myClassName = myEntryClass.getName();
72 String myCodebase = RMIClassLoader.getClassAnnotation(myEntryClass);
73
74 MangledField[] myEntryFields = getFields(anEntry);
75 String[] myParents = getParents(myEntryClass);
76
77 // Is it wildcard - i.e. all fields are null
78 boolean isWildcard = true;
79 for (int i = 0; i < myEntryFields.length; i++) {
80 if (! myEntryFields[i].isNull()) {
81 isWildcard = false;
82 break;
83 }
84 }
85
86 return new MangledEntry(myClassName, myCodebase, myEntryFields,
87 myParents, isWildcard, isSnapshot);
88 }
89
90 public Entry unMangle(MangledEntry anME) throws UnusableEntryException {
91 try {
92 Class myEntryClass =
93 ClassLoading.loadClass(anME.getCodebase(), anME.getType(),
94 null, anME.needsIntegrityCheck(), null);
95
96 Entry myEntry = (Entry) myEntryClass.newInstance();
97
98 Field[] myFields = getFields(myEntryClass);
99
100 MangledField[] myMangledFields = anME.getFields();
101 int myNextField = 0;
102
103 for (int i = 0; i < myFields.length; i++) {
104 if (theLogger.isLoggable(Level.FINEST)) {
105 theLogger.finest("Looking at field: " +
106 myFields[i].getName());
107 theLogger.finest("Valid:" +
108 myMangledFields[myNextField]);
109 theLogger.finest("Hash: " +
110 myMangledFields[myNextField].hashCode());
111 }
112 if (! myMangledFields[myNextField].isNull()) {
113 if (theLogger.isLoggable(Level.FINEST)) {
114 theLogger.finest("Not null");
115 theLogger.finest("Copy from: " +
116 myMangledFields[myNextField].getName());
117 theLogger.finest("Value will be: " +
118 myMangledFields[myNextField].unMangle(null,
119 anME.needsIntegrityCheck()));
120 }
121 myFields[i].set(myEntry,
122 myMangledFields[myNextField++].unMangle(null,
123 anME.needsIntegrityCheck()));
124 } else
125 myNextField++;
126 }
127
128 return myEntry;
129 } catch (Exception anE) {
130 theLogger.log(Level.SEVERE, "Error during unpack: ", anE);
131 throw new UnusableEntryException(anE);
132 }
133 }
134
135 private static Map theParentsCache = new WeakHashMap();
136
137 private String[] getParents(Class aClass) {
138 String[] myParents = null;
139
140 synchronized(theParentsCache) {
141 myParents = (String[]) theParentsCache.get(aClass);
142
143 if (myParents == null) {
144 ArrayList mySuperClasses = new ArrayList();
145
146 for(Class myCurrentClass = aClass.getSuperclass();
147 myCurrentClass != null;
148 myCurrentClass = myCurrentClass.getSuperclass()) {
149
150 mySuperClasses.add(myCurrentClass.getName());
151 }
152
153 myParents = new String[mySuperClasses.size()];
154 Object[] myClasses = mySuperClasses.toArray();
155 System.arraycopy(myClasses, 0, myParents, 0, myParents.length);
156
157 theParentsCache.put(aClass, myParents);
158 }
159 }
160
161 return myParents;
162 }
163
164 /**
165 We wish to sort fields into order with those of super-class before
166 subclass. We're using alphabetical order for fields within one
167 particular class but, in reality, any predicatable ordering would do.
168 */
169 private static class FieldSorter implements Comparator {
170 public int compare(Object anObject, Object anotherObject) {
171 Field myFirst = (Field) anObject;
172 Field mySecond = (Field) anotherObject;
173
174 if (myFirst == mySecond)
175 return 0;
176 else if (myFirst.getDeclaringClass() ==
177 mySecond.getDeclaringClass()) {
178 return myFirst.getName().compareTo(mySecond.getName());
179 } else if (myFirst.getDeclaringClass().isAssignableFrom(mySecond.getDeclaringClass())) {
180 return -1;
181 } else {
182 return 1;
183 }
184 }
185 }
186
187 private static Map theFieldCache = new WeakHashMap();
188
189 private Field[] getFields(Class aClass) {
190 synchronized(theFieldCache) {
191 Field[] myFields = (Field[]) theFieldCache.get(aClass);
192
193 if (myFields == null) {
194 myFields = aClass.getFields();
195 Arrays.sort(myFields, theFieldSorter);
196
197 ArrayList myRelevant = new ArrayList();
198
199 for (int i = 0; i < myFields.length; i++) {
200 if (isValid(myFields[i]))
201 myRelevant.add(myFields[i]);
202 }
203
204 myFields = new Field[myRelevant.size()];
205 myFields = (Field[]) myRelevant.toArray(myFields);
206 theFieldCache.put(aClass, myFields);
207 }
208
209 return myFields;
210 }
211 }
212
213 private MangledField[] getFields(Entry anEntry) {
214 Field[] myFields = getFields(anEntry.getClass());
215
216 MangledField[] myValidFields =
217 new MangledField[myFields.length];
218
219 int i = 0;
220 try {
221 for (i = 0; i < myFields.length; i++) {
222 Object myFieldValue = myFields[i].get(anEntry);
223 String myFieldName = myFields[i].getName();
224
225 if (myFieldValue == null)
226 myValidFields[i] =
227 new MangledField(myFieldName);
228 else
229 myValidFields[i] =
230 new MangledField(myFieldName, myFieldValue);
231 }
232 } catch (IllegalAccessException anIAE) {
233 theLogger.log(Level.SEVERE, "Problem accessing field", anIAE);
234 throw new InternalError("Couldn't access field: " + myFields[i]);
235 } catch (IOException anIOE) {
236 theLogger.log(Level.SEVERE, "Fatal error", anIOE);
237 throw new InternalError("Fatal error: " + anIOE);
238 }
239
240 return myValidFields;
241 }
242
243 private boolean isValid(Field aField) {
244 if ((aField.getModifiers() & INVALID_MODIFIERS) != 0)
245 return false;
246
247 if (aField.getType().isPrimitive())
248 return false;
249
250 return true;
251 }
252 }