1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.river.reggie.proxy;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.DataOutputStream;
22 import java.io.IOException;
23 import java.io.InvalidObjectException;
24 import java.io.ObjectInputStream;
25 import java.io.ObjectOutputStream;
26 import java.io.ObjectStreamField;
27 import java.io.Serializable;
28 import java.rmi.MarshalException;
29 import java.rmi.UnmarshalException;
30 import java.security.DigestOutputStream;
31 import java.security.MessageDigest;
32 import org.apache.river.api.io.AtomicSerial;
33 import org.apache.river.api.io.AtomicSerial.GetArg;
34 import org.apache.river.proxy.CodebaseProvider;
35 import org.apache.river.proxy.MarshalledWrapper;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 @AtomicSerial
54 public class EntryClass implements Serializable {
55
56 private static final long serialVersionUID = 2L;
57 private static final ObjectStreamField[] serialPersistentFields =
58 {
59
60 new ObjectStreamField("name", String.class),
61
62 new ObjectStreamField("hash", Long.TYPE),
63
64 new ObjectStreamField("superclass", EntryClass.class),
65
66 new ObjectStreamField("numFields", Integer.TYPE)
67 };
68
69
70
71
72
73 protected final String name;
74
75
76
77
78
79 protected final long hash;
80
81
82
83
84
85 protected final EntryClass superclass;
86
87
88
89
90
91 protected final int numFields;
92
93 private static boolean check(GetArg arg) throws IOException{
94 String name = (String) arg.get("name", null);
95 if (name == null) throw new InvalidObjectException("name cannot be null");
96 long hash = arg.get("hash", 0L);
97 if (hash == 0) throw new InvalidObjectException("hash cannot be zero");
98 Object superclass = arg.get("superclass", null);
99 if (superclass != null) {
100 if (!(superclass instanceof EntryClass))
101 throw new InvalidObjectException(
102 "superclass must be an instance of EntryClass");
103 } else {
104 if (!Object.class.getName().equals(name))
105 throw new InvalidObjectException(
106 "superclass may only be null if name is an instance of java.lang.Object");
107 }
108 int numFields = arg.get("numFields", 0);
109 return true;
110 }
111
112 public EntryClass(GetArg arg) throws IOException{
113 this(arg, check(arg));
114 }
115
116 private EntryClass(GetArg arg, boolean check) throws IOException {
117 name = (String) arg.get("name", null);
118 hash = arg.get("hash", 0L);
119 superclass = (EntryClass) arg.get("superclass", null);
120 numFields = arg.get("numFields", 0);
121 integrity = MarshalledWrapper.integrityEnforced(arg);
122 }
123
124
125 private transient int numInstances;
126
127 private transient int numTemplates;
128
129
130
131
132
133 private transient EntryClass replacement;
134
135
136
137
138 private transient boolean integrity = false;
139
140
141 public EntryClass(Class clazz, EntryClass superclass)
142 throws MarshalException
143 {
144 name = clazz.getName();
145 this.superclass = superclass;
146 ClassMapper.EntryField[] fields = ClassMapper.getFields(clazz);
147 numFields = fields.length;
148 hash = computeHash(fields);
149 }
150
151
152
153
154
155 private EntryClass(EntryClass orig) {
156 name = orig.name;
157 hash = orig.hash;
158 superclass = orig.superclass == null ? null : orig.superclass.getReplacement();
159 numFields = orig.numFields;
160 }
161
162
163 public EntryClass getSuperclass() {
164 return superclass;
165 }
166
167
168 public int getNumFields() {
169 return numFields;
170 }
171
172
173 public void setNumInstances(int numInstances) {
174 this.numInstances = numInstances;
175 }
176
177
178 public void setNumTemplates(int numTemplates) {
179 this.numTemplates = numTemplates;
180 }
181
182
183 public synchronized EntryClass getReplacement() {
184 if (replacement == null)
185 replacement = new EntryClass(this);
186 return replacement;
187 }
188
189
190
191
192
193
194 public boolean isAssignableFrom(EntryClass cls) {
195 for (EntryClass sup = cls; sup != null; sup = sup.superclass) {
196 if (hash == sup.hash)
197 return true;
198 }
199 return false;
200 }
201
202
203
204
205
206
207
208 public int getNumInstances() {
209 return numInstances;
210 }
211
212
213
214
215
216
217 public int getNumTemplates() {
218 return numTemplates;
219 }
220
221
222 public Class toClass(String codebase)
223 throws IOException, ClassNotFoundException
224 {
225 Class cls =
226 CodebaseProvider.loadClass(codebase, name, null, integrity, null);
227 EntryClass local;
228 try {
229 local = ClassMapper.toEntryClassBase(cls).eclass;
230 } catch (MarshalException e) {
231 throw new UnmarshalException("problem obtaining local version of "
232 + toString(), e);
233 }
234 if (hash != local.hash)
235 throw new UnmarshalException("incoming entry type: " + toString()
236 + " is not assignable to the local"
237 + " version of the type: " + local);
238 return cls;
239 }
240
241
242
243
244
245 public String getName() {
246 return name;
247 }
248
249
250
251
252
253
254
255
256 public boolean equals(Object o) {
257 if (this == o) return true;
258 if (!(o instanceof EntryClass))
259 return false;
260 EntryClass t = (EntryClass) o;
261 return hash == t.hash;
262 }
263
264
265
266
267
268 public int hashCode() {
269 return (int) (hash ^ (hash >>> 32));
270 }
271
272
273 public String toString() {
274 return getClass() + "[name=" + getName() + ", hash=" + hash + "]";
275 }
276
277
278
279
280
281
282
283
284 private long computeHash(ClassMapper.EntryField[] fields)
285 throws MarshalException
286 {
287 long hash = 0;
288 try {
289 MessageDigest md = MessageDigest.getInstance("SHA");
290 DataOutputStream out = new DataOutputStream(
291 new DigestOutputStream(new ByteArrayOutputStream(127),md));
292 if (superclass != null)
293 out.writeLong(superclass.hash);
294 out.writeUTF(name);
295 int startDeclaredFields = superclass != null ?
296 superclass.numFields : 0;
297 for (int i = startDeclaredFields; i < fields.length; i++) {
298 out.writeUTF(fields[i].field.getName());
299 out.writeUTF(fields[i].field.getType().getName());
300 }
301 out.flush();
302 byte[] digest = md.digest();
303 for (int i = Math.min(8, digest.length); --i >= 0; ) {
304 hash += ((long) (digest[i] & 0xFF)) << (i * 8);
305 }
306 } catch (Exception e) {
307 throw new MarshalException("Unable to calculate type hash for "
308 + name, e);
309 }
310 return hash;
311 }
312
313 private void writeObject(ObjectOutputStream out) throws IOException {
314 out.defaultWriteObject();
315 }
316
317
318
319
320
321
322
323 private void readObject(ObjectInputStream in)
324 throws IOException, ClassNotFoundException
325 {
326 in.defaultReadObject();
327 if (name == null)
328 throw new InvalidObjectException("name cannot be null");
329 if (hash == 0)
330 throw new InvalidObjectException("hash cannot be zero");
331 integrity = MarshalledWrapper.integrityEnforced(in);
332 }
333
334
335
336
337 private void readObjectNoData() throws InvalidObjectException {
338 throw new InvalidObjectException("no data");
339 }
340
341 }