001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package test.javax.management.compliance.signature.support;
010:
011: import java.io.Serializable;
012: import java.lang.reflect.Field;
013: import java.lang.reflect.Modifier;
014: import java.util.ArrayList;
015: import java.util.HashSet;
016: import java.util.Iterator;
017: import java.util.Set;
018:
019: /**
020: * @version $Revision: 1.5 $
021: */
022: public class SignatureVerifier {
023: public void verifySignature(String className,
024: ClassLoader jmxriLoader, ClassLoader mx4jLoader)
025: throws Exception {
026: Class jmxriClass = jmxriLoader.loadClass(className);
027: Class mx4jClass = mx4jLoader.loadClass(className);
028:
029: int modifiers = jmxriClass.getModifiers();
030: boolean isPublic = Modifier.isPublic(modifiers);
031: boolean isProtected = Modifier.isProtected(modifiers);
032: boolean isPackage = !Modifier.isPrivate(modifiers)
033: && !isProtected && !isPublic;
034: boolean isSerializable = Serializable.class
035: .isAssignableFrom(jmxriClass);
036:
037: NotCompliantWarningException warning = null;
038:
039: try {
040: checkSameClassModifiers(jmxriClass, mx4jClass);
041: } catch (NotCompliantWarningException x) {
042: warning = x;
043: }
044:
045: try {
046: checkSameInheritance(jmxriClass, mx4jClass);
047: } catch (NotCompliantWarningException x) {
048: warning = x;
049: }
050:
051: if (!isPackage) {
052: try {
053: checkSameConstructors(jmxriClass, mx4jClass);
054: } catch (NotCompliantWarningException x) {
055: warning = x;
056: }
057: try {
058: checkSameMethods(jmxriClass, mx4jClass);
059: } catch (NotCompliantWarningException x) {
060: warning = x;
061: }
062: try {
063: checkSameFields(jmxriClass, mx4jClass);
064: } catch (NotCompliantWarningException x) {
065: warning = x;
066: }
067: }
068:
069: if (isSerializable) {
070: try {
071: checkSameSerialVersionUID(jmxriClass, mx4jClass);
072: } catch (NotCompliantWarningException x) {
073: warning = x;
074: }
075: }
076:
077: if (warning != null)
078: throw warning;
079: }
080:
081: private void checkSameClassModifiers(Class jmxri, Class mx4j)
082: throws NotCompliantException {
083: int jmxriModifiers = jmxri.getModifiers();
084: int mx4jModifers = mx4j.getModifiers();
085: if (jmxriModifiers != mx4jModifers) {
086: int modifier = jmxriModifiers ^ mx4jModifers;
087: if ((modifier & jmxriModifiers) != 0) {
088: throw new NotCompliantException("JMX class "
089: + jmxri.getName()
090: + " in MX4J implementation is not declared "
091: + Modifier.toString(modifier)
092: + " as it should be");
093: }
094: if ((modifier & mx4jModifers) != 0) {
095: throw new NotCompliantWarningException("JMX class "
096: + jmxri.getName()
097: + " in MX4J implementation is declared "
098: + Modifier.toString(modifier)
099: + ", it is not in JMXRI");
100: }
101: }
102: }
103:
104: private void checkSameInheritance(Class jmxri, Class mx4j)
105: throws NotCompliantException {
106: // I have to walk the inheritance hierarchy
107:
108: Set jmxriInterfaces = new HashSet();
109: Set mx4jInterfaces = new HashSet();
110: for (Class jmxriParent = jmxri, mx4jParent = mx4j; jmxriParent != null; jmxriParent = jmxriParent
111: .getSuperclass(), mx4jParent = mx4jParent
112: .getSuperclass()) {
113: findInterfaces(jmxriParent, jmxriInterfaces);
114:
115: findInterfaces(mx4jParent, mx4jInterfaces);
116:
117: if (!jmxriParent.getName().equals(mx4jParent.getName())) {
118: throw new NotCompliantException(
119: "JMX class "
120: + jmxri.getName()
121: + " in MX4J implementation does not have the same hierarchy as JMXRI: "
122: + mx4jParent.getName() + ", should be "
123: + jmxriParent.getName());
124: }
125: }
126:
127: if (!jmxriInterfaces.containsAll(mx4jInterfaces)) {
128: mx4jInterfaces.removeAll(jmxriInterfaces);
129: checkInterfacesHaveMethods(jmxri, mx4jInterfaces);
130: }
131: if (!mx4jInterfaces.containsAll(jmxriInterfaces)) {
132: jmxriInterfaces.removeAll(mx4jInterfaces);
133: throw new NotCompliantException(
134: "JMX class "
135: + jmxri.getName()
136: + " in MX4J implementation does not implement the required interfaces: "
137: + jmxriInterfaces);
138: }
139: }
140:
141: private void findInterfaces(Class cls, Set interfaces) {
142: Class[] intfs = cls.getInterfaces();
143: for (int i = 0; i < intfs.length; ++i) {
144: Class intf = intfs[i];
145: boolean added = interfaces.add(intf.getName());
146: if (added)
147: findInterfaces(intf, interfaces);
148: }
149: }
150:
151: private void checkInterfacesHaveMethods(Class cls, Set interfaces)
152: throws NotCompliantException {
153: boolean warning = false;
154: for (Iterator i = interfaces.iterator(); i.hasNext();) {
155: String name = (String) i.next();
156: if (name.equals("java.lang.Cloneable"))
157: warning = true;
158: else
159: warning = false;
160: }
161:
162: if (warning)
163: throw new NotCompliantWarningException(
164: "JMX class "
165: + cls.getName()
166: + " in MX4J implementation implements too many tag interfaces: "
167: + interfaces);
168: else
169: throw new NotCompliantException(
170: "JMX class "
171: + cls.getName()
172: + " in MX4J implementation implements too many interfaces: "
173: + interfaces);
174: }
175:
176: private void checkSameConstructors(final Class jmxri, Class mx4j)
177: throws NotCompliantException {
178: checkSameObjectMethod(new ObjectClass.Constructor(jmxri),
179: new ObjectClass.Constructor(mx4j));
180: }
181:
182: private void checkSameMethods(Class jmxri, Class mx4j)
183: throws NotCompliantException {
184: checkSameObjectMethod(new ObjectClass.Method(jmxri),
185: new ObjectClass.Method(mx4j));
186: }
187:
188: private void checkSameObjectMethod(ObjectClass jmxri,
189: ObjectClass mx4j) throws NotCompliantException {
190: // Public methods first
191: Set jmxriMethods = wrapMethods(jmxri.getMethods());
192: Set mx4jMethods = wrapMethods(mx4j.getMethods());
193: checkSameMethods(jmxri.getName(), jmxriMethods, mx4jMethods);
194:
195: // Protected methods now. I should walk the inheritance hierarchy.
196: jmxriMethods.clear();
197: mx4jMethods.clear();
198: for (ObjectClass jmxriParent = jmxri, mx4jParent = mx4j; jmxriParent != null; jmxriParent = jmxriParent
199: .getSuperclass(), mx4jParent = mx4jParent
200: .getSuperclass()) {
201: ObjectMethod[] methods = jmxriParent.getDeclaredMethods();
202: for (int i = 0; i < methods.length; ++i) {
203: if (Modifier.isProtected(methods[i].getModifiers())) {
204: jmxriMethods.add(wrapMethod(methods[i]));
205: }
206: }
207:
208: methods = mx4jParent.getDeclaredMethods();
209: for (int i = 0; i < methods.length; ++i) {
210: if (Modifier.isProtected(methods[i].getModifiers())) {
211: mx4jMethods.add(wrapMethod(methods[i]));
212: }
213: }
214: }
215: checkSameMethods(jmxri.getName(), jmxriMethods, mx4jMethods);
216: }
217:
218: private void checkSameFields(Class jmxri, Class mx4j)
219: throws NotCompliantException {
220: // Public fields first
221: Set jmxriFields = wrapFields(jmxri.getFields());
222: Set mx4jFields = wrapFields(mx4j.getFields());
223: checkSameFields(jmxri.getName(), jmxriFields, mx4jFields);
224:
225: // Protected fields now. I should walk the inheritance hierarchy.
226: jmxriFields.clear();
227: mx4jFields.clear();
228: for (Class jmxriParent = jmxri, mx4jParent = mx4j; jmxriParent != null; jmxriParent = jmxriParent
229: .getSuperclass(), mx4jParent = mx4jParent
230: .getSuperclass()) {
231: Field[] fields = jmxriParent.getDeclaredFields();
232: for (int i = 0; i < fields.length; ++i) {
233: if (Modifier.isProtected(fields[i].getModifiers())) {
234: jmxriFields.add(wrapField(fields[i]));
235: }
236: }
237:
238: fields = mx4jParent.getDeclaredFields();
239: for (int i = 0; i < fields.length; ++i) {
240: if (Modifier.isProtected(fields[i].getModifiers())) {
241: mx4jFields.add(wrapField(fields[i]));
242: }
243: }
244: }
245: checkSameFields(jmxri.getName(), jmxriFields, mx4jFields);
246: }
247:
248: private void checkSameSerialVersionUID(Class jmxriClass,
249: Class mx4jClass) throws NotCompliantException {
250: try {
251: Field jmxriField = jmxriClass.getField("serialVersionUID");
252: jmxriField.setAccessible(true);
253: Field mx4jField = mx4jClass.getField("serialVersionUID");
254: mx4jField.setAccessible(true);
255: long jmxriValue = jmxriField.getLong(null);
256: long mx4jValue = jmxriField.getLong(null);
257: if (jmxriValue != mx4jValue)
258: throw new NotCompliantException(
259: "JMX class "
260: + jmxriClass.getName()
261: + " in MX4J implementation does not have the same serialVersionUID: expecting "
262: + jmxriValue + ", found " + mx4jValue);
263: } catch (NoSuchFieldException ignored) {
264: // If the class did not change between JMX 1.0 and JMX 1.1, then the serialVersionUID is not present
265: } catch (NotCompliantException x) {
266: throw x;
267: } catch (Exception x) {
268: x.printStackTrace();
269: throw new NotCompliantException(
270: "Unknown problems in checking serialVersionUID: "
271: + x);
272: }
273: }
274:
275: private Set wrapMethods(ObjectMethod[] methods) {
276: Set set = new HashSet();
277: for (int i = 0; i < methods.length; ++i) {
278: set.add(wrapMethod(methods[i]));
279: }
280: return set;
281: }
282:
283: private MethodWrapper wrapMethod(ObjectMethod method) {
284: return new MethodWrapper(method);
285: }
286:
287: private Set wrapFields(Field[] fields) {
288: HashSet set = new HashSet();
289: for (int i = 0; i < fields.length; ++i) {
290: set.add(wrapField(fields[i]));
291: }
292: return set;
293: }
294:
295: private FieldWrapper wrapField(Field field) {
296: return new FieldWrapper(field);
297: }
298:
299: private void checkSameMethods(String name, Set jmxri, Set mx4j)
300: throws NotCompliantException {
301: if (!jmxri.containsAll(mx4j)) {
302: checkDifferentMethods(name, mx4j, jmxri);
303: }
304:
305: if (!mx4j.containsAll(jmxri)) {
306: checkDifferentMethods(name, jmxri, mx4j);
307: }
308: }
309:
310: private void checkDifferentMethods(String name, Set set1, Set set2)
311: throws NotCompliantException {
312: set1.removeAll(set2);
313:
314: boolean warning = false;
315: boolean error = false;
316: ArrayList warnings = new ArrayList();
317: ArrayList errors = new ArrayList();
318: for (Iterator i = set1.iterator(); i.hasNext();) {
319: MethodWrapper method1 = (MethodWrapper) i.next();
320: boolean found = false;
321: for (Iterator j = set2.iterator(); j.hasNext();) {
322: MethodWrapper method2 = (MethodWrapper) j.next();
323: if (method1.isSameMethod(method2)) {
324: if (!method1.sameSignatureModifiers(method2)) {
325: warning = true;
326: warnings.add(method1);
327: warnings.add(method2);
328: } else {
329: if (method1
330: .throwsClauseDifferForRuntimeExceptionsOnly(method2)) {
331: warning = true;
332: warnings.add(method1);
333: warnings.add(method2);
334: } else {
335: error = true;
336: errors.add(method1);
337: errors.add(method2);
338: }
339: }
340: found = true;
341: break;
342: }
343: }
344: if (!found)
345: throw new NotCompliantException(
346: "JMX class "
347: + name
348: + " in MX4J implementation has different interface: "
349: + set1);
350: }
351:
352: if (error)
353: throw new NotCompliantException(
354: "JMX class "
355: + name
356: + " in MX4J implementation has different signature: "
357: + errors);
358: if (warning)
359: throw new NotCompliantWarningException(
360: "JMX class "
361: + name
362: + " in MX4J implementation has different signature: "
363: + warnings);
364: throw new IllegalStateException();
365: }
366:
367: private void checkSameFields(String name, Set jmxri, Set mx4j)
368: throws NotCompliantException {
369: if (!jmxri.containsAll(mx4j)) {
370: mx4j.removeAll(jmxri);
371: throw new NotCompliantException("JMX class " + name
372: + " in MX4J implementation has too many fields: "
373: + mx4j);
374: }
375: if (!mx4j.containsAll(jmxri)) {
376: jmxri.removeAll(mx4j);
377: throw new NotCompliantException(
378: "JMX class "
379: + name
380: + " in MX4J implementation does not have the required fields: "
381: + jmxri);
382: }
383: }
384: }
|