001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.util.io;
017:
018: import java.io.Externalizable;
019: import java.io.IOException;
020: import java.io.ObjectStreamClass;
021: import java.io.ObjectStreamConstants;
022: import java.io.ObjectStreamField;
023: import java.io.Serializable;
024: import java.lang.reflect.AccessibleObject;
025: import java.lang.reflect.Field;
026: import java.lang.reflect.Method;
027: import java.lang.reflect.Modifier;
028: import java.security.AccessController;
029: import java.security.PrivilegedAction;
030: import java.util.Arrays;
031:
032: public class ClassDescriptor implements java.io.Serializable,
033: ObjectStreamConstants {
034:
035: private static final ObjectStreamField[] serialPersistentFields = ObjectStreamClass.NO_FIELDS;
036:
037: protected ClassDescriptor(Class clazz, ClassDescriptor super desc,
038: boolean serializable, boolean externalizable) {
039:
040: if (externalizable)
041: serializable = false;
042:
043: this .forClass = clazz;
044: this .super desc = super desc;
045: this .serializable = serializable;
046: this .externalizable = externalizable;
047: this .name = forClass.getName();
048:
049: /*
050: * Enter this class in the table of known descriptors.
051: * Otherwise, when the fields are read it may recurse
052: * trying to find the descriptor for itself.
053: */
054: insertDescriptorFor(this );
055:
056: if (externalizable)
057: fields = NO_FIELDS;
058: else if (serializable)
059: AccessController
060: .doPrivileged(new AccessibleFieldInitializer(this ));
061:
062: AccessController
063: .doPrivileged(new SerializationPropertiesReflector(this ));
064:
065: if (hasWriteObjectMethod)
066: flags |= ObjectStreamConstants.SC_WRITE_METHOD;
067: if (serializable)
068: flags |= ObjectStreamConstants.SC_SERIALIZABLE;
069: if (externalizable)
070: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
071: }
072:
073: protected int flags = 0;
074:
075: protected void writeClassInfo(ObjectOutputStream out)
076: throws IOException {
077: out.writeByte(flags);
078: out.writeShort(fields.length);
079:
080: for (int i = 0; i < fields.length; i++)
081: fields[i].writeDesc(out);
082: }
083:
084: private boolean serializable;
085: private boolean externalizable;
086:
087: /*
088: * Get the Serializability of the class.
089: */
090: protected boolean isSerializable() {
091: return serializable;
092: }
093:
094: /*
095: * Get the externalizability of the class.
096: */
097: protected boolean isExternalizable() {
098: return externalizable;
099: }
100:
101: protected boolean isNonSerializable() {
102: return !(externalizable || serializable);
103: }
104:
105: /*
106: * Array of persistent fields of this class, sorted by
107: * type and name.
108: */
109:
110: public static final FieldDescriptor[] NO_FIELDS = new FieldDescriptor[0];
111: protected FieldDescriptor[] fields;
112:
113: public FieldDescriptor[] getFields() {
114: return fields;
115: }
116:
117: public void setFields(FieldDescriptor[] fields) {
118: this .fields = fields;
119: }
120:
121: private boolean hasWriteObjectMethod;
122: private boolean hasReadObjectMethod;
123: private Method writeObjectMethod;
124: private Method readObjectMethod;
125:
126: public boolean hasWriteObjectMethod() {
127: return hasWriteObjectMethod;
128: }
129:
130: public void hasWriteObjectMethod(boolean b) {
131: hasWriteObjectMethod = b;
132: }
133:
134: public Method getWriteObjectMethod() {
135: return writeObjectMethod;
136: }
137:
138: protected void setWriteObjectMethod(Method method) {
139: writeObjectMethod = method;
140: hasWriteObjectMethod = (method != null);
141: }
142:
143: public boolean hasReadObjectMethod() {
144: return hasReadObjectMethod;
145: }
146:
147: public void hasReadObjectMethod(boolean b) {
148: hasReadObjectMethod = b;
149: }
150:
151: public Method getReadObjectMethod() {
152: return readObjectMethod;
153: }
154:
155: protected void setReadObjectMethod(Method method) {
156: readObjectMethod = method;
157: hasReadObjectMethod = (method != null);
158: }
159:
160: /*
161: * SerialVersionUID for the class this instance represents.
162: */
163: private long suid;
164:
165: public long getSerialVersionUID() {
166: return suid;
167: }
168:
169: protected void setSerialVersionUID(long suid) {
170: this .suid = suid;
171: }
172:
173: /*
174: * The name of this descriptor
175: */
176: private String name;
177:
178: public String getName() {
179: return name;
180: }
181:
182: /*
183: * Class that is a descriptor for in this virtual machine.
184: */
185: private Class forClass;
186:
187: public Class forClass() {
188: return forClass;
189: }
190:
191: /*
192: * The descriptor of the supertype.
193: */
194: private ClassDescriptor super desc;
195:
196: /*
197: * Return the superclass descriptor of this descriptor.
198: */
199: protected ClassDescriptor getSuperclass() {
200: return super desc;
201: }
202:
203: /*
204: * Return the superclass descriptor of this descriptor.
205: */
206: protected void setSuperclass(ClassDescriptor s) {
207: super desc = s;
208: }
209:
210: public String toString() {
211: StringBuffer sb = new StringBuffer();
212:
213: sb.append(name);
214: sb.append(": static final long serialVersionUID = ");
215: sb.append(Long.toString(suid));
216: sb.append("L;");
217: return sb.toString();
218: }
219:
220: public static StringBuffer getSignature(Class clazz) {
221: StringBuffer buf = new StringBuffer();
222: return getSignature(clazz, buf);
223: }
224:
225: public static StringBuffer getSignature(Class clazz,
226: StringBuffer buf) {
227: if (clazz.isPrimitive()) {
228: if (clazz == Integer.TYPE)
229: buf.append('I');
230: else if (clazz == Byte.TYPE)
231: buf.append('B');
232: else if (clazz == Long.TYPE)
233: buf.append('J');
234: else if (clazz == Float.TYPE)
235: buf.append('F');
236: else if (clazz == Double.TYPE)
237: buf.append('D');
238: else if (clazz == Short.TYPE)
239: buf.append('S');
240: else if (clazz == Character.TYPE)
241: buf.append('C');
242: else if (clazz == Boolean.TYPE)
243: buf.append('Z');
244: else if (clazz == Void.TYPE)
245: buf.append('V');
246: } else if (clazz.isArray()) {
247: Class cl = clazz;
248: while (cl.isArray()) {
249: buf.append('[');
250: cl = cl.getComponentType();
251: }
252: buf.append(getSignature(cl).toString());
253: } else {
254: buf.append('L');
255: buf.append(clazz.getName().replace('.', '/'));
256: buf.append(';');
257: }
258: return buf;
259: }
260:
261: public static ClassDescriptor lookup(Class clazz) {
262: ClassDescriptor desc = lookupInternal(clazz);
263: if (desc.isSerializable() || desc.isExternalizable())
264: return desc;
265: return null;
266: }
267:
268: /*
269: * Find the class descriptor for the specified class.
270: * Package access only so it can be called from ObjectIn/OutStream.
271: */
272: static ClassDescriptor lookupInternal(Class clazz) {
273: /* Synchronize on the hashtable so no two threads will do
274: * this at the same time.
275: */
276: ClassDescriptor desc = null;
277: synchronized (descriptorFor) {
278: /* Find the matching descriptor if it already known */
279: desc = findDescriptorFor(clazz);
280: if (desc != null) {
281: return desc;
282: }
283:
284: /* Check if it's serializable */
285: boolean serializable = Serializable.class
286: .isAssignableFrom(clazz);
287:
288: /* If the class is only Serializable,
289: * lookup the descriptor for the superclass.
290: */
291: ClassDescriptor super desc = null;
292: if (serializable) {
293: Class super class = clazz.getSuperclass();
294: if (super class != null)
295: super desc = lookup(super class);
296: }
297:
298: /* Check if its' externalizable.
299: * If it's Externalizable, clear the serializable flag.
300: * Only one or the other may be set in the protocol.
301: */
302: boolean externalizable = false;
303: if (serializable) {
304: externalizable = ((super desc != null) && super desc
305: .isExternalizable())
306: || Externalizable.class.isAssignableFrom(clazz);
307: if (externalizable) {
308: serializable = false;
309: }
310: }
311:
312: /* Create a new version descriptor,
313: * it put itself in the known table.
314: */
315: desc = new ClassDescriptor(clazz, super desc, serializable,
316: externalizable);
317: }
318: return desc;
319: }
320:
321: /*
322: * findDescriptorFor a Class. This looks in the cache for a
323: * mapping from Class -> ObjectStreamClass mappings. The hashCode
324: * of the Class is used for the lookup since the Class is the key.
325: * The entries are extended from java.lang.ref.SoftReference so the
326: * gc will be able to free them if needed.
327: */
328: private static ClassDescriptor findDescriptorFor(Class clazz) {
329:
330: int hash = clazz.hashCode();
331: int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
332: ClassDescriptorEntry e;
333: ClassDescriptorEntry prev;
334:
335: /* Free any initial entries whose refs have been cleared */
336: while ((e = descriptorFor[index]) != null && e.get() == null) {
337: descriptorFor[index] = e.next;
338: }
339:
340: /* Traverse the chain looking for a descriptor with forClass == clazz.
341: * unlink entries that are unresolved.
342: */
343: prev = e;
344: while (e != null) {
345: ClassDescriptor desc = (ClassDescriptor) (e.get());
346: if (desc == null) {
347: // This entry has been cleared, unlink it
348: prev.next = e.next;
349: } else {
350: if (desc.forClass == clazz)
351: return desc;
352: prev = e;
353: }
354: e = e.next;
355: }
356: return null;
357: }
358:
359: /*
360: * insertDescriptorFor a Class -> ClassDescriptor mapping.
361: */
362: private static void insertDescriptorFor(ClassDescriptor desc) {
363: // Make sure not already present
364: if (findDescriptorFor(desc.forClass) != null) {
365: return;
366: }
367:
368: int hash = desc.forClass.hashCode();
369: int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
370: ClassDescriptorEntry e = new ClassDescriptorEntry(desc);
371: e.next = descriptorFor[index];
372: descriptorFor[index] = e;
373: }
374:
375: /*
376: * Entries held in the Cache of known ClassDescriptor objects.
377: * Entries are chained together with the same hash value (modulo array size).
378: */
379: private static class ClassDescriptorEntry extends
380: java.lang.ref.SoftReference {
381: ClassDescriptorEntry next;
382:
383: ClassDescriptorEntry(ClassDescriptor c) {
384: super (c);
385: }
386: }
387:
388: static private ClassDescriptorEntry[] descriptorFor = new ClassDescriptorEntry[61];
389:
390: static final Class[] OIS_ARGS = { java.io.ObjectInputStream.class };
391: static final Class[] OOS_ARGS = { java.io.ObjectOutputStream.class };
392:
393: private class SerializationPropertiesReflector implements
394: PrivilegedAction {
395:
396: ClassDescriptor desc;
397:
398: public SerializationPropertiesReflector(ClassDescriptor desc) {
399: this .desc = desc;
400: }
401:
402: public Object run() {
403: try {
404: Field field = null;
405: field = desc.forClass().getDeclaredField(
406: "serialVersionUID");
407: int modifiers = field.getModifiers();
408: if (Modifier.isStatic(modifiers)
409: && Modifier.isFinal(modifiers)) {
410: field.setAccessible(true);
411: desc.setSerialVersionUID(field.getLong(desc
412: .forClass()));
413: } else
414: desc
415: .setSerialVersionUID(computeSerialVersionUID(desc
416: .forClass()));
417:
418: } catch (NoSuchFieldException ex) {
419: desc.setSerialVersionUID(computeSerialVersionUID(desc
420: .forClass()));
421: } catch (IllegalAccessException ex) {
422: desc.setSerialVersionUID(computeSerialVersionUID(desc
423: .forClass()));
424: }
425:
426: if (serializable) {
427: desc.setWriteObjectMethod(getDeclaredMethod(
428: "writeObject", ClassDescriptor.OOS_ARGS,
429: Modifier.PRIVATE, Modifier.STATIC));
430:
431: desc.setWriteObjectMethod(getDeclaredMethod(
432: "readObject", ClassDescriptor.OIS_ARGS,
433: Modifier.PRIVATE, Modifier.STATIC));
434: }
435: return null;
436: }
437:
438: private Method getDeclaredMethod(String methodName,
439: Class[] args, int requiredModifierMask,
440: int disallowedModifierMask) {
441: Method method = null;
442: try {
443: method = forClass.getDeclaredMethod(methodName, args);
444: if (method != null) {
445: int mods = method.getModifiers();
446: if ((mods & disallowedModifierMask) != 0
447: || (mods & requiredModifierMask) != requiredModifierMask)
448: method = null;
449: else
450: method.setAccessible(true);
451:
452: }
453: } catch (NoSuchMethodException e) { /* This is not a problem. */
454: }
455: return method;
456: }
457:
458: private long computeSerialVersionUID(Class clazz) {
459: /* In this implementation we are not using the
460: * serialVersionID. The classes on both ends must be compatable.
461: */
462: return 42L;
463: }
464:
465: }
466:
467: private static class AccessibleFieldInitializer implements
468: PrivilegedAction {
469:
470: ClassDescriptor desc;
471:
472: public AccessibleFieldInitializer(ClassDescriptor desc) {
473: this .desc = desc;
474: }
475:
476: public Object run() {
477: FieldDescriptor[] fields = getDeclaredSerialPersistentFields(desc
478: .forClass());
479: if (fields == null)
480: fields = reflectForFields(desc.forClass());
481:
482: if (fields.length > 1)
483: Arrays.sort(fields);
484:
485: desc.setFields(fields);
486: return null;
487: }
488:
489: private FieldDescriptor[] getDeclaredSerialPersistentFields(
490: Class clazz) throws SecurityException {
491: java.lang.reflect.Field[] tmpFields = null;
492: ObjectStreamField[] outputStreamFields;
493:
494: try {
495: Field serialPersistentFields = clazz
496: .getDeclaredField("serialPersistentFields");
497: /* This field must be private according to the spec */
498: if (!Modifier.isPrivate(serialPersistentFields
499: .getModifiers()))
500: return null;
501:
502: serialPersistentFields.setAccessible(true);
503:
504: /* Safely retreive the value of the class' serialPersistentFields.
505: * These fields are of the wrong type they are of the type
506: * java.io.ObjectStreamField and are nothing more than wrappers
507: * for a methods name and type to anyone outside the java.io package
508: * For this reason we must create our own more accessible FieldDescriptors
509: * that encompas the functionality we need.
510: */
511:
512: outputStreamFields = (ObjectStreamField[]) serialPersistentFields
513: .get(clazz);
514: tmpFields = new Field[outputStreamFields.length];
515:
516: } catch (NoSuchFieldException e) {
517: return null;
518: } catch (IllegalAccessException e) {
519: return null;
520: } catch (IllegalArgumentException e) {
521: return null;
522: }
523:
524: /* Validate the existence of the fields the class indicated
525: * and instaniate a FieldDescriptor for it.
526: */
527: Field reflectedField;
528: int validFields = 0;
529: for (int i = outputStreamFields.length - 1; i >= 0; i--) {
530: try {
531: reflectedField = clazz
532: .getDeclaredField(outputStreamFields[i]
533: .getName());
534: if (outputStreamFields[i].getType() == reflectedField
535: .getType()) {
536: reflectedField.setAccessible(true);
537: tmpFields[validFields++] = reflectedField;
538: }
539: } catch (NoSuchFieldException e) { /* unimportant. keep going */
540: }
541: }
542:
543: FieldDescriptor[] validFieldDescriptors = new FieldDescriptor[validFields];
544:
545: for (validFields--; validFields >= 0; validFields--) {
546: validFieldDescriptors[validFields] = new FieldDescriptor(
547: tmpFields[validFields]);
548: }
549:
550: return validFieldDescriptors;
551: }
552:
553: private FieldDescriptor[] reflectForFields(Class clazz) {
554:
555: Field[] allFields = clazz.getDeclaredFields();
556: AccessibleObject.setAccessible(allFields, true);
557: Field[] tmpFields = new Field[allFields.length];
558:
559: int validFields = 0, modifiers;
560:
561: for (int i = 0; i < allFields.length; i++) {
562: modifiers = allFields[i].getModifiers();
563: if (!Modifier.isStatic(modifiers)
564: && !Modifier.isTransient(modifiers))
565: tmpFields[validFields++] = allFields[i];
566: }
567:
568: FieldDescriptor[] validFieldDescriptors = new FieldDescriptor[validFields];
569:
570: for (validFields--; validFields >= 0; validFields--) {
571: validFieldDescriptors[validFields] = new FieldDescriptor(
572: tmpFields[validFields]);
573: }
574:
575: return validFieldDescriptors;
576: }
577: }
578:
579: }
|