1 /* |
|
2 * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
21 * have any questions. |
|
22 */ |
|
23 |
|
24 /* @test %M% %I% |
|
25 * @bug 6562936 6750935 |
|
26 * @run compile customtypes/package-info.java |
|
27 * @run main CustomTypeTest |
|
28 */ |
|
29 |
|
30 import java.io.InvalidObjectException; |
|
31 import java.lang.management.ManagementFactory; |
|
32 import java.lang.reflect.Array; |
|
33 import java.lang.reflect.InvocationHandler; |
|
34 import java.lang.reflect.Method; |
|
35 import java.lang.reflect.Proxy; |
|
36 import java.lang.reflect.Type; |
|
37 import java.util.ArrayList; |
|
38 import java.util.Arrays; |
|
39 import java.util.Collection; |
|
40 import java.util.Date; |
|
41 import java.util.Iterator; |
|
42 import java.util.List; |
|
43 import javax.management.JMX; |
|
44 import javax.management.MBeanServer; |
|
45 import javax.management.ObjectName; |
|
46 import javax.management.StandardMBean; |
|
47 import javax.management.Descriptor; |
|
48 import javax.management.MBeanServerInvocationHandler; |
|
49 import javax.management.NotCompliantMBeanException; |
|
50 import javax.management.openmbean.ArrayType; |
|
51 import javax.management.openmbean.CompositeData; |
|
52 import javax.management.openmbean.CompositeDataSupport; |
|
53 import javax.management.openmbean.CompositeType; |
|
54 import javax.management.openmbean.MXBeanMapping; |
|
55 import javax.management.openmbean.MXBeanMappingClass; |
|
56 import javax.management.openmbean.MXBeanMappingFactory; |
|
57 import javax.management.openmbean.MXBeanMappingFactoryClass; |
|
58 import javax.management.openmbean.OpenDataException; |
|
59 import javax.management.openmbean.OpenType; |
|
60 import javax.management.openmbean.SimpleType; |
|
61 import javax.management.openmbean.TabularData; |
|
62 |
|
63 import static javax.management.JMX.MBeanOptions; |
|
64 |
|
65 import customtypes.*; |
|
66 |
|
67 public class CustomTypeTest { |
|
68 @MXBeanMappingClass(LinkedListMapping.class) |
|
69 public static class LinkedList { |
|
70 private final String name; |
|
71 private final LinkedList next; |
|
72 |
|
73 public LinkedList(String name, LinkedList next) { |
|
74 this.name = name; |
|
75 this.next = next; |
|
76 } |
|
77 |
|
78 public String getName() { |
|
79 return name; |
|
80 } |
|
81 |
|
82 public LinkedList getNext() { |
|
83 return next; |
|
84 } |
|
85 |
|
86 public String toString() { |
|
87 if (next == null) |
|
88 return "(" + name + ")"; |
|
89 else |
|
90 return "(" + name + " " + next + ")"; |
|
91 } |
|
92 |
|
93 public boolean equals(Object x) { |
|
94 if (!(x instanceof LinkedList)) |
|
95 return false; |
|
96 LinkedList other = (LinkedList) x; |
|
97 return (this.name.equals(other.name) && |
|
98 (this.next == null ? other.next == null : |
|
99 this.next.equals(other.next))); |
|
100 } |
|
101 } |
|
102 |
|
103 public static class LinkedListMapping extends MXBeanMapping { |
|
104 public LinkedListMapping(Type type) throws OpenDataException { |
|
105 super(LinkedList.class, ArrayType.getArrayType(SimpleType.STRING)); |
|
106 if (type != LinkedList.class) { |
|
107 throw new OpenDataException("Mapping only valid for " + |
|
108 LinkedList.class); |
|
109 } |
|
110 } |
|
111 |
|
112 public Object fromOpenValue(Object openValue) throws InvalidObjectException { |
|
113 String[] array = (String[]) openValue; |
|
114 LinkedList list = null; |
|
115 for (int i = array.length - 1; i >= 0; i--) |
|
116 list = new LinkedList(array[i], list); |
|
117 return list; |
|
118 } |
|
119 |
|
120 public Object toOpenValue(Object javaValue) throws OpenDataException { |
|
121 ArrayList<String> array = new ArrayList<String>(); |
|
122 for (LinkedList list = (LinkedList) javaValue; list != null; |
|
123 list = list.getNext()) |
|
124 array.add(list.getName()); |
|
125 return array.toArray(new String[0]); |
|
126 } |
|
127 } |
|
128 |
|
129 public static interface LinkedListMXBean { |
|
130 public LinkedList getLinkedList(); |
|
131 } |
|
132 |
|
133 public static class LinkedListImpl implements LinkedListMXBean { |
|
134 public LinkedList getLinkedList() { |
|
135 return new LinkedList("car", new LinkedList("cdr", null)); |
|
136 } |
|
137 } |
|
138 |
|
139 public static class ObjectMXBeanMapping extends MXBeanMapping { |
|
140 private static final CompositeType wildcardType; |
|
141 |
|
142 static { |
|
143 try { |
|
144 wildcardType = |
|
145 new CompositeType(Object.class.getName(), |
|
146 "Wildcard type for Object", |
|
147 new String[0], // itemNames |
|
148 new String[0], // itemDescriptions |
|
149 new OpenType<?>[0]); // itemTypes |
|
150 } catch (OpenDataException e) { |
|
151 throw new RuntimeException(e); |
|
152 } |
|
153 } |
|
154 |
|
155 public ObjectMXBeanMapping() { |
|
156 super(Object.class, wildcardType); |
|
157 } |
|
158 |
|
159 public Object fromOpenValue(Object openValue) throws InvalidObjectException { |
|
160 if (!(openValue instanceof CompositeData)) { |
|
161 throw new InvalidObjectException("Not a CompositeData: " + |
|
162 openValue.getClass()); |
|
163 } |
|
164 CompositeData cd = (CompositeData) openValue; |
|
165 if (!cd.containsKey("value")) { |
|
166 throw new InvalidObjectException("CompositeData does not " + |
|
167 "contain a \"value\" item: " + cd); |
|
168 } |
|
169 Object x = cd.get("value"); |
|
170 if (!(x instanceof CompositeData || x instanceof TabularData || |
|
171 x instanceof Object[])) |
|
172 return x; |
|
173 |
|
174 String typeName = (String) cd.get("type"); |
|
175 if (typeName == null) { |
|
176 throw new InvalidObjectException("CompositeData does not " + |
|
177 "contain a \"type\" item: " + cd); |
|
178 } |
|
179 Class<?> c; |
|
180 try { |
|
181 c = Class.forName(typeName); |
|
182 } catch (ClassNotFoundException e) { |
|
183 InvalidObjectException ioe = |
|
184 new InvalidObjectException("Could not find type"); |
|
185 ioe.initCause(e); |
|
186 throw ioe; |
|
187 } |
|
188 MXBeanMapping mapping; |
|
189 try { |
|
190 mapping = objectMappingFactory.mappingForType(c, objectMappingFactory); |
|
191 } catch (OpenDataException e) { |
|
192 InvalidObjectException ioe = |
|
193 new InvalidObjectException("Could not map object's " + |
|
194 "type " + c.getName()); |
|
195 ioe.initCause(e); |
|
196 throw ioe; |
|
197 } |
|
198 return mapping.fromOpenValue(x); |
|
199 } |
|
200 |
|
201 public Object toOpenValue(Object javaValue) throws OpenDataException { |
|
202 OpenType<?> openType; |
|
203 Object openValue; |
|
204 String typeName; |
|
205 if (javaValue == null) { |
|
206 openType = SimpleType.VOID; |
|
207 openValue = null; |
|
208 typeName = null; |
|
209 } else { |
|
210 Class<?> c = javaValue.getClass(); |
|
211 if (c.equals(Object.class)) |
|
212 throw new OpenDataException("Cannot map Object to an open value"); |
|
213 MXBeanMapping mapping = |
|
214 objectMappingFactory.mappingForType(c, objectMappingFactory); |
|
215 openType = mapping.getOpenType(); |
|
216 openValue = mapping.toOpenValue(javaValue); |
|
217 typeName = c.getName(); |
|
218 } |
|
219 CompositeType ct = new CompositeType( |
|
220 (javaValue == null) ? "null" : openType.getClassName(), |
|
221 "Open Mapping for Object", |
|
222 new String[] {"type", "value"}, |
|
223 new String[] {"type", "value"}, |
|
224 new OpenType<?>[] {SimpleType.STRING, openType}); |
|
225 return new CompositeDataSupport( |
|
226 ct, |
|
227 new String[] {"type", "value"}, |
|
228 new Object[] {typeName, openValue}); |
|
229 } |
|
230 } |
|
231 |
|
232 public static class ObjectMappingFactory extends MXBeanMappingFactory { |
|
233 private static MXBeanMapping objectMapping = |
|
234 new ObjectMXBeanMapping(); |
|
235 |
|
236 @Override |
|
237 public MXBeanMapping mappingForType(Type t, MXBeanMappingFactory f) |
|
238 throws OpenDataException { |
|
239 if (t.equals(Object.class)) |
|
240 return objectMapping; |
|
241 else |
|
242 return MXBeanMappingFactory.DEFAULT.mappingForType(t, f); |
|
243 } |
|
244 } |
|
245 |
|
246 private static MXBeanMappingFactory objectMappingFactory = |
|
247 new ObjectMappingFactory(); |
|
248 |
|
249 public static interface ObjectMXBean { |
|
250 public Object getObject(); |
|
251 public Object[] getObjects(); |
|
252 public List<Object> getObjectList(); |
|
253 public Object[][] getMoreObjects(); |
|
254 } |
|
255 |
|
256 public static class ObjectImpl implements ObjectMXBean { |
|
257 public Object getObject() { |
|
258 return 123; |
|
259 } |
|
260 |
|
261 private static Object[] objects = { |
|
262 "foo", 3, 3.14f, 3.14, 3L, new Date(), ObjectName.WILDCARD, |
|
263 new byte[3], new char[3], new int[3][3], |
|
264 new LinkedListImpl().getLinkedList(), |
|
265 }; |
|
266 |
|
267 public Object[] getObjects() { |
|
268 return objects; |
|
269 } |
|
270 |
|
271 public List<Object> getObjectList() { |
|
272 return Arrays.asList(getObjects()); |
|
273 } |
|
274 |
|
275 public Object[][] getMoreObjects() { |
|
276 return new Object[][] {{getObjects()}}; |
|
277 } |
|
278 } |
|
279 |
|
280 @MXBeanMappingFactoryClass(ObjectMappingFactory.class) |
|
281 public static interface AnnotatedObjectMXBean extends ObjectMXBean {} |
|
282 |
|
283 public static class AnnotatedObjectImpl extends ObjectImpl |
|
284 implements AnnotatedObjectMXBean {} |
|
285 |
|
286 public static class BrokenMappingFactory extends MXBeanMappingFactory { |
|
287 public MXBeanMapping mappingForType(Type t, MXBeanMappingFactory f) |
|
288 throws OpenDataException { |
|
289 throw new OpenDataException(t.toString()); |
|
290 } |
|
291 } |
|
292 |
|
293 public static class ReallyBrokenMappingFactory extends BrokenMappingFactory { |
|
294 public ReallyBrokenMappingFactory() { |
|
295 throw new RuntimeException("Oops"); |
|
296 } |
|
297 } |
|
298 |
|
299 @MXBeanMappingFactoryClass(BrokenMappingFactory.class) |
|
300 public static interface BrokenMXBean { |
|
301 public int getX(); |
|
302 } |
|
303 |
|
304 public static class BrokenImpl implements BrokenMXBean { |
|
305 public int getX() {return 0;} |
|
306 } |
|
307 |
|
308 @MXBeanMappingFactoryClass(ReallyBrokenMappingFactory.class) |
|
309 public static interface ReallyBrokenMXBean { |
|
310 public int getX(); |
|
311 } |
|
312 |
|
313 public static class ReallyBrokenImpl implements ReallyBrokenMXBean { |
|
314 public int getX() {return 0;} |
|
315 } |
|
316 |
|
317 public static class BrokenMapping extends MXBeanMapping { |
|
318 public BrokenMapping(Type t) { |
|
319 super(t, SimpleType.STRING); |
|
320 throw new RuntimeException("Oops"); |
|
321 } |
|
322 |
|
323 public Object fromOpenValue(Object openValue) throws InvalidObjectException { |
|
324 throw new AssertionError(); |
|
325 } |
|
326 |
|
327 public Object toOpenValue(Object javaValue) throws OpenDataException { |
|
328 throw new AssertionError(); |
|
329 } |
|
330 } |
|
331 |
|
332 @MXBeanMappingClass(BrokenMapping.class) |
|
333 public static class BrokenType {} |
|
334 |
|
335 public static interface BrokenTypeMXBean { |
|
336 BrokenType getBroken(); |
|
337 } |
|
338 |
|
339 public static class BrokenTypeImpl implements BrokenTypeMXBean { |
|
340 public BrokenType getBroken() { |
|
341 throw new AssertionError(); |
|
342 } |
|
343 } |
|
344 |
|
345 public static class BadConstructorMXBeanMappingFactory1 extends |
|
346 MXBeanMappingFactory { |
|
347 private BadConstructorMXBeanMappingFactory1() {} |
|
348 |
|
349 @Override |
|
350 public MXBeanMapping mappingForType(Type arg0, MXBeanMappingFactory arg1) |
|
351 throws OpenDataException { |
|
352 throw new UnsupportedOperationException("Should not be called"); |
|
353 } |
|
354 } |
|
355 |
|
356 public static class BadConstructorMXBeanMappingFactory2 extends |
|
357 MXBeanMappingFactory { |
|
358 public BadConstructorMXBeanMappingFactory2(boolean oops) {} |
|
359 |
|
360 @Override |
|
361 public MXBeanMapping mappingForType(Type arg0, MXBeanMappingFactory arg1) |
|
362 throws OpenDataException { |
|
363 throw new UnsupportedOperationException("Should not be called"); |
|
364 } |
|
365 } |
|
366 |
|
367 @MXBeanMappingFactoryClass(BadConstructorMXBeanMappingFactory1.class) |
|
368 public static interface BadConstructor1MXBean {} |
|
369 |
|
370 public static class BadConstructor1 implements BadConstructor1MXBean {} |
|
371 |
|
372 @MXBeanMappingFactoryClass(BadConstructorMXBeanMappingFactory2.class) |
|
373 public static interface BadConstructor2MXBean {} |
|
374 |
|
375 public static class BadConstructor2 implements BadConstructor2MXBean {} |
|
376 |
|
377 public static void main(String[] args) throws Exception { |
|
378 MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); |
|
379 |
|
380 System.out.println("Test @MXBeanMappingClass"); |
|
381 ObjectName linkedName = new ObjectName("d:type=LinkedList"); |
|
382 LinkedListMXBean linkedListMXBean = new LinkedListImpl(); |
|
383 LinkedList list1 = linkedListMXBean.getLinkedList(); |
|
384 mbs.registerMBean(linkedListMXBean, linkedName); |
|
385 LinkedListMXBean linkedProxy = |
|
386 JMX.newMXBeanProxy(mbs, linkedName, LinkedListMXBean.class); |
|
387 MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler) |
|
388 Proxy.getInvocationHandler(linkedProxy); |
|
389 if (!mbsih.isMXBean()) |
|
390 fail("not MXBean proxy"); |
|
391 LinkedList list2 = linkedProxy.getLinkedList(); |
|
392 if (list1 == list2) |
|
393 fail("lists identical!"); |
|
394 // They should have gone through the mapping and back, |
|
395 // and the mapping doesn't do anything that would allow it |
|
396 // to restore the identical object. |
|
397 if (!list1.equals(list2)) |
|
398 fail("lists different: " + list1 + " vs " + list2); |
|
399 System.out.println("...success"); |
|
400 |
|
401 System.out.println("Test StandardMBean with MXBeanMappingFactory"); |
|
402 ObjectMXBean wildcardMBean = new ObjectImpl(); |
|
403 MBeanOptions options = new MBeanOptions(); |
|
404 options.setMXBeanMappingFactory(objectMappingFactory); |
|
405 if (!options.isMXBean()) |
|
406 fail("Setting MXBeanMappingFactory should imply MXBean"); |
|
407 StandardMBean wildcardStandardMBean = |
|
408 new StandardMBean(wildcardMBean, ObjectMXBean.class, options); |
|
409 testWildcardMBean(mbs, wildcardMBean, wildcardStandardMBean, |
|
410 options, ObjectMXBean.class); |
|
411 |
|
412 System.out.println("Test @MXBeanMappingFactoryClass on interface"); |
|
413 ObjectMXBean annotatedWildcardMBean = new AnnotatedObjectImpl(); |
|
414 testWildcardMBean(mbs, annotatedWildcardMBean, annotatedWildcardMBean, |
|
415 null, AnnotatedObjectMXBean.class); |
|
416 |
|
417 System.out.println("Test @MXBeanMappingFactoryClass on package"); |
|
418 CustomMXBean custom = zeroProxy(CustomMXBean.class); |
|
419 ObjectName customName = new ObjectName("d:type=Custom"); |
|
420 mbs.registerMBean(custom, customName); |
|
421 Object x = mbs.getAttribute(customName, "X"); |
|
422 if (!(x instanceof String)) |
|
423 fail("Should be String: " + x + " (a " + x.getClass().getName() + ")"); |
|
424 CustomMXBean customProxy = |
|
425 JMX.newMXBeanProxy(mbs, customName, CustomMXBean.class); |
|
426 x = customProxy.getX(); |
|
427 if (!(x instanceof Integer) || (Integer) x != 0) |
|
428 fail("Wrong return from proxy: " + x + " (a " + x.getClass().getName() + ")"); |
|
429 |
|
430 System.out.println("Test MXBeanMappingFactory exception"); |
|
431 try { |
|
432 mbs.registerMBean(new BrokenImpl(), new ObjectName("d:type=Broken")); |
|
433 fail("Register did not throw exception"); |
|
434 } catch (NotCompliantMBeanException e) { |
|
435 System.out.println("...OK: threw: " + e); |
|
436 } |
|
437 |
|
438 System.out.println("Test MXBeanMappingFactory constructor exception"); |
|
439 try { |
|
440 mbs.registerMBean(new ReallyBrokenImpl(), new ObjectName("d:type=Broken")); |
|
441 fail("Register did not throw exception"); |
|
442 } catch (NotCompliantMBeanException e) { |
|
443 System.out.println("...OK: threw: " + e); |
|
444 } catch (Exception e) { |
|
445 fail("Register threw wrong exception: " + e); |
|
446 } |
|
447 |
|
448 System.out.println("Test MXBeanMappingFactory exception with StandardMBean"); |
|
449 MXBeanMappingFactory brokenF = new BrokenMappingFactory(); |
|
450 MBeanOptions brokenO = new MBeanOptions(); |
|
451 brokenO.setMXBeanMappingFactory(brokenF); |
|
452 try { |
|
453 new StandardMBean(wildcardMBean, ObjectMXBean.class, brokenO); |
|
454 fail("StandardMBean with broken factory did not throw exception"); |
|
455 } catch (IllegalArgumentException e) { |
|
456 if (!(e.getCause() instanceof NotCompliantMBeanException)) { |
|
457 fail("StandardMBean with broken factory threw wrong exception: " |
|
458 + e.getCause()); |
|
459 } |
|
460 } |
|
461 |
|
462 System.out.println("Test MXBeanMappingClass exception"); |
|
463 try { |
|
464 mbs.registerMBean(new BrokenTypeImpl(), new ObjectName("d:type=Broken")); |
|
465 fail("Broken MXBeanMappingClass did not throw exception"); |
|
466 } catch (NotCompliantMBeanException e) { |
|
467 System.out.println("...OK: threw: " + e); |
|
468 } |
|
469 |
|
470 System.out.println("Test MXBeanMappingFactoryClass constructor exception"); |
|
471 for (Object mbean : new Object[] { |
|
472 new BadConstructor1(), new BadConstructor2(), |
|
473 }) { |
|
474 String testName = mbean.getClass().getSimpleName(); |
|
475 try { |
|
476 ObjectName name = new ObjectName("d:type=" + testName); |
|
477 mbs.registerMBean(mbean, name); |
|
478 fail("Broken MXBeanMappingFactoryClass did not throw exception" + |
|
479 " (" + testName + ")"); |
|
480 } catch (NotCompliantMBeanException e) { |
|
481 System.out.println("...OK: " + testName + " threw: " + e); |
|
482 } catch (Exception e) { |
|
483 fail("Broken MXBeanMappingFactoryClass " + testName + " threw " + |
|
484 "wrong exception: " + e); |
|
485 } |
|
486 } |
|
487 |
|
488 if (failure == null) |
|
489 System.out.println("TEST PASSED"); |
|
490 else |
|
491 throw new Exception("TEST FAILED: " + failure); |
|
492 } |
|
493 |
|
494 private static void testWildcardMBean(MBeanServer mbs, ObjectMXBean impl, |
|
495 Object mbean, |
|
496 MBeanOptions proxyOptions, |
|
497 Class<? extends ObjectMXBean> intf) |
|
498 throws Exception { |
|
499 ObjectName wildcardName = new ObjectName("d:type=Object"); |
|
500 mbs.registerMBean(mbean, wildcardName); |
|
501 try { |
|
502 testWildcardMBean2(mbs, impl, wildcardName, proxyOptions, intf); |
|
503 } finally { |
|
504 mbs.unregisterMBean(wildcardName); |
|
505 } |
|
506 } |
|
507 |
|
508 private static void testWildcardMBean2(MBeanServer mbs, ObjectMXBean impl, |
|
509 ObjectName wildcardName, |
|
510 MBeanOptions proxyOptions, |
|
511 Class<? extends ObjectMXBean> intf) |
|
512 throws Exception { |
|
513 if (proxyOptions == null) { |
|
514 proxyOptions = new MBeanOptions(); |
|
515 MXBeanMappingFactory f = MXBeanMappingFactory.forInterface(intf); |
|
516 proxyOptions.setMXBeanMappingFactory(f); |
|
517 } |
|
518 Descriptor d = mbs.getMBeanInfo(wildcardName).getDescriptor(); |
|
519 String factoryName = (String) |
|
520 d.getFieldValue(JMX.MXBEAN_MAPPING_FACTORY_CLASS_FIELD); |
|
521 if (!ObjectMappingFactory.class.getName().equals(factoryName)) { |
|
522 fail("Descriptor has wrong MXBeanMappingFactory: " + factoryName + |
|
523 " should be " + ObjectMappingFactory.class.getName()); |
|
524 } |
|
525 ObjectMXBean wildcardProxy = |
|
526 JMX.newMBeanProxy(mbs, wildcardName, intf, proxyOptions); |
|
527 MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler) |
|
528 Proxy.getInvocationHandler(wildcardProxy); |
|
529 MBeanOptions opts = mbsih.getMBeanOptions(); |
|
530 if (!opts.equals(proxyOptions)) { |
|
531 fail("Proxy options differ from request: " + opts + " vs " + |
|
532 proxyOptions); |
|
533 } |
|
534 Method[] wildcardMethods = ObjectMXBean.class.getMethods(); |
|
535 for (Method m : wildcardMethods) { |
|
536 System.out.println("..." + m.getName()); |
|
537 Object orig = m.invoke(impl); |
|
538 Object copy = m.invoke(wildcardProxy); |
|
539 if (!deepEquals(orig, copy)) { |
|
540 fail("objects differ: " + deepToString(orig) + " vs " + |
|
541 deepToString(copy)); |
|
542 } |
|
543 } |
|
544 } |
|
545 |
|
546 private static <T> T zeroProxy(Class<T> intf) { |
|
547 return intf.cast(Proxy.newProxyInstance(intf.getClassLoader(), |
|
548 new Class<?>[] {intf}, |
|
549 new ZeroInvocationHandler())); |
|
550 } |
|
551 |
|
552 private static class ZeroInvocationHandler implements InvocationHandler { |
|
553 public Object invoke(Object proxy, Method method, Object[] args) |
|
554 throws Throwable { |
|
555 return 0; |
|
556 } |
|
557 } |
|
558 |
|
559 private static boolean deepEquals(Object x, Object y) { |
|
560 if (x == y) |
|
561 return true; |
|
562 if (x == null || y == null) |
|
563 return false; |
|
564 |
|
565 if (x instanceof Collection<?>) { |
|
566 if (!(y instanceof Collection<?>)) |
|
567 return false; |
|
568 Collection<?> xcoll = (Collection<?>) x; |
|
569 Collection<?> ycoll = (Collection<?>) y; |
|
570 if (xcoll.size() != ycoll.size()) |
|
571 return false; |
|
572 Iterator<?> xit = xcoll.iterator(); |
|
573 Iterator<?> yit = ycoll.iterator(); |
|
574 while (xit.hasNext()) { |
|
575 if (!deepEquals(xit.next(), yit.next())) |
|
576 return false; |
|
577 } |
|
578 return true; |
|
579 } |
|
580 |
|
581 Class<?> xclass = x.getClass(); |
|
582 Class<?> yclass = y.getClass(); |
|
583 if (xclass.isArray()) { |
|
584 if (!yclass.isArray()) |
|
585 return false; |
|
586 if (!xclass.getComponentType().equals(yclass.getComponentType())) |
|
587 return false; |
|
588 int len = Array.getLength(x); |
|
589 if (Array.getLength(y) != len) |
|
590 return false; |
|
591 for (int i = 0; i < len; i++) { |
|
592 if (!deepEquals(Array.get(x, i), Array.get(y, i))) |
|
593 return false; |
|
594 } |
|
595 return true; |
|
596 } |
|
597 |
|
598 // return x.equals(y); |
|
599 if (x.equals(y)) |
|
600 return true; |
|
601 System.out.println("Not equal: <" + x + "> and <" + y + ">"); |
|
602 return false; |
|
603 } |
|
604 |
|
605 private static String deepToString(Object x) { |
|
606 if (x == null) |
|
607 return "null"; |
|
608 |
|
609 if (x instanceof Collection<?>) { |
|
610 Collection<?> xcoll = (Collection<?>) x; |
|
611 StringBuilder sb = new StringBuilder("["); |
|
612 for (Object e : xcoll) { |
|
613 if (sb.length() > 1) |
|
614 sb.append(", "); |
|
615 sb.append(deepToString(e)); |
|
616 } |
|
617 sb.append("]"); |
|
618 return sb.toString(); |
|
619 } |
|
620 |
|
621 if (x instanceof Object[]) { |
|
622 Object[] xarr = (Object[]) x; |
|
623 return deepToString(Arrays.asList(xarr)); |
|
624 } |
|
625 |
|
626 if (x.getClass().isArray()) { // primitive array |
|
627 String s = Arrays.deepToString(new Object[] {x}); |
|
628 return s.substring(1, s.length() - 1); |
|
629 } |
|
630 |
|
631 return x.toString(); |
|
632 } |
|
633 |
|
634 private static void fail(String msg) { |
|
635 System.out.println("TEST FAILED: " + msg); |
|
636 if (msg.length() > 100) |
|
637 msg = msg.substring(0, 100) + "..."; |
|
638 failure = msg; |
|
639 } |
|
640 |
|
641 private static String failure; |
|
642 } |
|