1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.beanutils;
18
19 import java.util.*;
20
21 import java.lang.ref.WeakReference;
22 import java.lang.ref.ReferenceQueue;
23
24 import junit.framework.TestCase;
25 import junit.framework.Test;
26 import junit.framework.TestSuite;
27
28 import org.apache.commons.collections.ReferenceMap;
29 import org.apache.commons.logging.LogFactory;
30
31 /**
32 * <p>
33 * Test Case for changes made during Beanutils Beanification
34 * </p>
35 *
36 * @author Robert Burrell Donkin
37 * @author Juozas Baliuka
38 * @version $Revision: 1.5 $ $Date: 2004/02/28 13:18:36 $
39 */
40
41 public class BeanificationTestCase extends TestCase {
42
43
44
45 /** Maximum number of iterations before our test fails */
46 public static final int MAX_GC_ITERATIONS = 50;
47
48
49
50
51
52
53
54 /**
55 * Construct a new instance of this test case.
56 *
57 * @param name Name of the test case
58 */
59 public BeanificationTestCase(String name) {
60 super(name);
61 }
62
63
64
65
66
67 /**
68 * Set up instance variables required by this test case.
69 */
70 public void setUp() {
71
72 ConvertUtils.deregister();
73
74 }
75
76
77 /**
78 * Return the tests included in this test suite.
79 */
80 public static Test suite() {
81 return (new TestSuite(BeanificationTestCase.class));
82 }
83
84
85 /**
86 * Tear down instance variables required by this test case.
87 */
88 public void tearDown() {
89 ;
90 }
91
92
93
94
95 /** Test of the methodology we'll use for some of the later tests */
96 public void testMemoryTestMethodology() throws Exception {
97
98
99 ClassLoader loader = new ClassLoader(this.getClass().getClassLoader()) {};
100 WeakReference reference = new WeakReference(loader);
101 Class myClass = loader.loadClass("org.apache.commons.beanutils.BetaBean");
102
103 assertNotNull("Weak reference released early", reference.get());
104
105
106 loader = null;
107 myClass = null;
108
109 int iterations = 0;
110 int bytz = 2;
111 while(true) {
112 System.gc();
113 if(iterations++ > MAX_GC_ITERATIONS){
114 fail("Max iterations reached before resource released.");
115 }
116 if( reference.get() == null ) {
117 break;
118
119 } else {
120
121 byte[] b = new byte[bytz];
122 bytz = bytz * 2;
123 }
124 }
125 }
126
127 /** Tests whether classloaders and beans are released from memory by the map used by beanutils */
128 public void testMemoryLeak2() throws Exception {
129
130
131 if (isPre14JVM()) {
132 System.out.println("WARNING: CANNOT TEST MEMORY LEAK ON PRE1.4 JVM");
133 return;
134 }
135
136
137 TestClassLoader loader = new TestClassLoader();
138 ReferenceQueue queue = new ReferenceQueue();
139 WeakReference loaderReference = new WeakReference(loader, queue);
140 Integer test = new Integer(1);
141
142 WeakReference testReference = new WeakReference(test, queue);
143
144 Map map = new WeakHashMap();
145 map.put(loader, test);
146
147 assertEquals("In map", test, map.get(loader));
148 assertNotNull("Weak reference released early (1)", loaderReference.get());
149 assertNotNull("Weak reference released early (2)", testReference.get());
150
151
152 loader = null;
153 test = null;
154
155 int iterations = 0;
156 int bytz = 2;
157 while(true) {
158 System.gc();
159 if(iterations++ > MAX_GC_ITERATIONS){
160 fail("Max iterations reached before resource released.");
161 }
162 map.isEmpty();
163
164 if(
165 loaderReference.get() == null &&
166 testReference.get() == null) {
167 break;
168
169 } else {
170
171 byte[] b = new byte[bytz];
172 bytz = bytz * 2;
173 }
174 }
175 }
176
177 /** Tests whether classloaders and beans are released from memory */
178 public void testMemoryLeak() throws Exception {
179 if (isPre14JVM()) {
180 System.out.println("WARNING: CANNOT TEST MEMORY LEAK ON PRE1.4 JVM");
181 return;
182 }
183
184
185 TestClassLoader loader = new TestClassLoader();
186 WeakReference loaderReference = new WeakReference(loader);
187 BeanUtilsBean.getInstance();
188
189 class GetBeanUtilsBeanThread extends Thread {
190
191 BeanUtilsBean beanUtils;
192 ConvertUtilsBean convertUtils;
193 PropertyUtilsBean propertyUtils;
194
195 GetBeanUtilsBeanThread() {}
196
197 public void run() {
198 beanUtils = BeanUtilsBean.getInstance();
199 convertUtils = ConvertUtilsBean.getInstance();
200 propertyUtils = PropertyUtilsBean.getInstance();
201
202 LogFactory.releaseAll();
203 }
204
205 public String toString() {
206 return "GetBeanUtilsBeanThread";
207 }
208 }
209
210
211 GetBeanUtilsBeanThread thread = new GetBeanUtilsBeanThread();
212 WeakReference threadWeakReference = new WeakReference(thread);
213 thread.setContextClassLoader(loader);
214
215 thread.start();
216 thread.join();
217
218 WeakReference beanUtilsReference = new WeakReference(thread.beanUtils);
219 WeakReference propertyUtilsReference = new WeakReference(thread.propertyUtils);
220 WeakReference convertUtilsReference = new WeakReference(thread.convertUtils);
221
222 assertNotNull("Weak reference released early (1)", loaderReference.get());
223 assertNotNull("Weak reference released early (2)", beanUtilsReference.get());
224 assertNotNull("Weak reference released early (3)", propertyUtilsReference.get());
225 assertNotNull("Weak reference released early (4)", convertUtilsReference.get());
226
227
228 loader = null;
229 thread.setContextClassLoader(null);
230 thread = null;
231
232 int iterations = 0;
233 int bytz = 2;
234 while(true) {
235 BeanUtilsBean.getInstance();
236 System.gc();
237 if(iterations++ > MAX_GC_ITERATIONS){
238 fail("Max iterations reached before resource released.");
239 }
240
241 if(
242 loaderReference.get() == null &&
243 beanUtilsReference.get() == null &&
244 propertyUtilsReference.get() == null &&
245 convertUtilsReference.get() == null) {
246 break;
247
248 } else {
249
250 byte[] b = new byte[bytz];
251 bytz = bytz * 2;
252 }
253 }
254 }
255
256 /**
257 * Tests whether difference instances are loaded by different
258 * context classloaders.
259 */
260 public void testGetByContextClassLoader() throws Exception {
261
262 class GetBeanUtilsBeanThread extends Thread {
263
264 private Signal signal;
265
266 GetBeanUtilsBeanThread(Signal signal) {
267 this.signal = signal;
268 }
269
270 public void run() {
271 signal.setSignal(2);
272 signal.setBean(BeanUtilsBean.getInstance());
273 signal.setConvertUtils(ConvertUtilsBean.getInstance());
274 signal.setPropertyUtils(PropertyUtilsBean.getInstance());
275 }
276
277 public String toString() {
278 return "GetBeanUtilsBeanThread";
279 }
280 }
281
282 Signal signal = new Signal();
283 signal.setSignal(1);
284
285 GetBeanUtilsBeanThread thread = new GetBeanUtilsBeanThread(signal);
286 thread.setContextClassLoader(new TestClassLoader());
287
288 thread.start();
289 thread.join();
290
291 assertEquals("Signal not set by test thread", 2, signal.getSignal());
292 assertTrue(
293 "Different BeanUtilsBean instances per context classloader",
294 BeanUtilsBean.getInstance() != signal.getBean());
295 assertTrue(
296 "Different ConvertUtilsBean instances per context classloader",
297 ConvertUtilsBean.getInstance() != signal.getConvertUtils());
298 assertTrue(
299 "Different PropertyUtilsBean instances per context classloader",
300 PropertyUtilsBean.getInstance() != signal.getPropertyUtils());
301 }
302
303
304 /**
305 * Tests whether difference instances are loaded by different
306 * context classloaders.
307 */
308 public void testContextClassLoaderLocal() throws Exception {
309
310 class CCLLTesterThread extends Thread {
311
312 private Signal signal;
313 private ContextClassLoaderLocal ccll;
314
315 CCLLTesterThread(Signal signal, ContextClassLoaderLocal ccll) {
316 this.signal = signal;
317 this.ccll = ccll;
318 }
319
320 public void run() {
321 ccll.set(new Integer(1789));
322 signal.setSignal(2);
323 signal.setMarkerObject(ccll.get());
324 }
325
326 public String toString() {
327 return "CCLLTesterThread";
328 }
329 }
330
331 ContextClassLoaderLocal ccll = new ContextClassLoaderLocal();
332 ccll.set(new Integer(1776));
333 assertEquals("Start thread sets value", new Integer(1776), ccll.get());
334
335 Signal signal = new Signal();
336 signal.setSignal(1);
337
338 CCLLTesterThread thread = new CCLLTesterThread(signal, ccll);
339 thread.setContextClassLoader(new TestClassLoader());
340
341 thread.start();
342 thread.join();
343
344 assertEquals("Signal not set by test thread", 2, signal.getSignal());
345 assertEquals("Second thread preserves value", new Integer(1776), ccll.get());
346 assertEquals("Second thread gets value it set", new Integer(1789), signal.getMarkerObject());
347 }
348
349 /** Tests whether calls are independent for different classloaders */
350 public void testContextClassloaderIndependence() throws Exception {
351
352 class TestIndependenceThread extends Thread {
353 private Signal signal;
354 private PrimitiveBean bean;
355
356 TestIndependenceThread(Signal signal, PrimitiveBean bean) {
357 this.signal = signal;
358 this.bean = bean;
359 }
360
361 public void run() {
362 try {
363 signal.setSignal(3);
364 ConvertUtils.register(new Converter() {
365 public Object convert(Class type, Object value) {
366 return new Integer(9);
367 }
368 }, Integer.TYPE);
369 BeanUtils.setProperty(bean, "int", new Integer(1));
370 } catch (Exception e) {
371 e.printStackTrace();
372 signal.setException(e);
373 }
374 }
375
376 public String toString() {
377 return "TestIndependenceThread";
378 }
379 }
380
381 PrimitiveBean bean = new PrimitiveBean();
382 BeanUtils.setProperty(bean, "int", new Integer(1));
383 assertEquals("Wrong property value (1)", 1, bean.getInt());
384
385 ConvertUtils.register(new Converter() {
386 public Object convert(Class type, Object value) {
387 return new Integer(5);
388 }
389 }, Integer.TYPE);
390 BeanUtils.setProperty(bean, "int", new Integer(1));
391 assertEquals("Wrong property value(2)", 5, bean.getInt());
392
393 Signal signal = new Signal();
394 signal.setSignal(1);
395 TestIndependenceThread thread = new TestIndependenceThread(signal, bean);
396 thread.setContextClassLoader(new TestClassLoader());
397
398 thread.start();
399 thread.join();
400
401 assertNull("Exception thrown by test thread:" + signal.getException(), signal.getException());
402 assertEquals("Signal not set by test thread", 3, signal.getSignal());
403 assertEquals("Wrong property value(3)", 9, bean.getInt());
404
405 }
406
407 /** Tests whether different threads can set beanutils instances correctly */
408 public void testBeanUtilsBeanSetInstance() throws Exception {
409
410 class SetInstanceTesterThread extends Thread {
411
412 private Signal signal;
413 private BeanUtilsBean bean;
414
415 SetInstanceTesterThread(Signal signal, BeanUtilsBean bean) {
416 this.signal = signal;
417 this.bean = bean;
418 }
419
420 public void run() {
421 BeanUtilsBean.setInstance(bean);
422 signal.setSignal(21);
423 signal.setBean(BeanUtilsBean.getInstance());
424 }
425
426 public String toString() {
427 return "SetInstanceTesterThread";
428 }
429 }
430
431 Signal signal = new Signal();
432 signal.setSignal(1);
433
434 BeanUtilsBean beanOne = new BeanUtilsBean();
435 BeanUtilsBean beanTwo = new BeanUtilsBean();
436
437 SetInstanceTesterThread thread = new SetInstanceTesterThread(signal, beanTwo);
438 thread.setContextClassLoader(new TestClassLoader());
439
440 BeanUtilsBean.setInstance(beanOne);
441 assertEquals("Start thread gets right instance", beanOne, BeanUtilsBean.getInstance());
442
443 thread.start();
444 thread.join();
445
446 assertEquals("Signal not set by test thread", 21, signal.getSignal());
447 assertEquals("Second thread preserves value", beanOne, BeanUtilsBean.getInstance());
448 assertEquals("Second thread gets value it set", beanTwo, signal.getBean());
449 }
450
451 /** Tests whether the unset method works*/
452 public void testContextClassLoaderUnset() throws Exception {
453 BeanUtilsBean beanOne = new BeanUtilsBean();
454 ContextClassLoaderLocal ccll = new ContextClassLoaderLocal();
455 ccll.set(beanOne);
456 assertEquals("Start thread gets right instance", beanOne, ccll.get());
457 ccll.unset();
458 assertTrue("Unset works", !beanOne.equals(ccll.get()));
459 }
460
461 private boolean isPre14JVM() {
462
463
464 String version = System.getProperty("java.specification.version");
465 StringTokenizer tokenizer = new StringTokenizer(version,".");
466 if (tokenizer.nextToken().equals("1")) {
467 String minorVersion = tokenizer.nextToken();
468 if (minorVersion.equals("0")) return true;
469 if (minorVersion.equals("1")) return true;
470 if (minorVersion.equals("2")) return true;
471 if (minorVersion.equals("3")) return true;
472 }
473 return false;
474 }
475
476
477
478 class TestClassLoader extends ClassLoader {
479 public String toString() {
480 return "TestClassLoader";
481 }
482 }
483
484 class Signal {
485 private Exception e;
486 private int signal = 0;
487 private BeanUtilsBean bean;
488 private PropertyUtilsBean propertyUtils;
489 private ConvertUtilsBean convertUtils;
490 private Object marker;
491
492 public Exception getException() {
493 return e;
494 }
495
496 public void setException(Exception e) {
497 this.e = e;
498 }
499
500 public int getSignal() {
501 return signal;
502 }
503
504 public void setSignal(int signal) {
505 this.signal = signal;
506 }
507
508 public Object getMarkerObject() {
509 return marker;
510 }
511
512 public void setMarkerObject(Object marker) {
513 this.marker = marker;
514 }
515
516 public BeanUtilsBean getBean() {
517 return bean;
518 }
519
520 public void setBean(BeanUtilsBean bean) {
521 this.bean = bean;
522 }
523
524 public PropertyUtilsBean getPropertyUtils() {
525 return propertyUtils;
526 }
527
528 public void setPropertyUtils(PropertyUtilsBean propertyUtils) {
529 this.propertyUtils = propertyUtils;
530 }
531
532 public ConvertUtilsBean getConvertUtils() {
533 return convertUtils;
534 }
535
536 public void setConvertUtils(ConvertUtilsBean convertUtils) {
537 this.convertUtils = convertUtils;
538 }
539 }
540 }
541