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