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