blob: 8431fd9494cfafee9d2b3fc6deb5aa39660aa68d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.beanutils2.bugs;
import java.beans.IntrospectionException;
import java.lang.ref.SoftReference;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import org.apache.commons.beanutils2.MappedPropertyDescriptor;
import org.apache.commons.beanutils2.memoryleaktests.MemoryLeakTestCase;
import junit.framework.TestCase;
/**
* Test case for Jira issue# BEANUTILS-347.
*
* @see <a href="https://issues.apache.org/jira/browse/BEANUTILS-347">https://issues.apache.org/jira/browse/BEANUTILS-347</a>
*/
public class Jira347TestCase extends TestCase {
/**
* Tests that MappedPropertyDescriptor does not throw an exception while re-creating a Method reference after it
* has been garbage collected under the following circumstances.
* - a class has a property 'mappedProperty'
* - there is no getter for this property
* - there is method setMappedProperty(MappedPropertyTestBean,MappedPropertyTestBean)
*
* In this case getMappedWriteMethod should not throw an exception after the method reference has been garbage collected.
* It is essential that in both cases the same method is returned or in both cases null.
* If the constructor of the MappedPropertyDescriptor would recognize the situation (of the first param not being of type String)
* this would be fine as well.
*/
public void testMappedPropertyDescriptor_AnyArgsProperty() throws Exception {
final String className = "org.apache.commons.beanutils2.MappedPropertyTestBean";
final ClassLoader loader = newClassLoader();
final Class<?> beanClass = loader.loadClass(className);
beanClass.newInstance();
// Sanity checks only
assertNotNull("ClassLoader is null", loader);
assertNotNull("BeanClass is null", beanClass);
assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
// now start the test
MappedPropertyDescriptor descriptor = null;
try {
descriptor = new MappedPropertyDescriptor("anyMapped", beanClass);
}
catch (final IntrospectionException e) {
// this would be fine as well
}
if (descriptor != null) {
final String m1 = getMappedWriteMethod(descriptor);
forceGarbageCollection();
try {
final String m2 = getMappedWriteMethod(descriptor);
assertEquals("Method returned post garbage collection differs from Method returned prior to gc", m1, m2);
}
catch (final RuntimeException e) {
fail("getMappedWriteMethod threw an exception after garbage collection " + e);
}
}
}
/**
* Retrieves the string representation of the mapped write method
* for the given descriptor.
* This conversion is needed as there must not be strong reference to the
* Method object outside of this method as otherwise the garbage collector will not
* clean up the soft reference within the MappedPropertyDescriptor.
*
* @return the string representation or null if mapped write method does not exist
*/
private String getMappedWriteMethod(final MappedPropertyDescriptor descriptor) {
final Method m = descriptor.getMappedWriteMethod();
return m == null ? null : m.toString();
}
/**
* Try to force the garbage collector to run by filling up memory and calling System.gc().
*/
private void forceGarbageCollection() throws Exception {
// Fill up memory
final SoftReference<Object> ref = new SoftReference<>(new Object());
int count = 0;
while(ref.get() != null && count++ < 5) {
ArrayList<Object> list = new ArrayList<>();
try {
long i = 0;
while (true && ref.get() != null) {
list.add("A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String " + i++);
}
} catch (final Throwable ignored) {
}
list.clear();
list = null;
// System.out.println("Count " + count + " : " + getMemoryStats());
System.gc();
Thread.sleep(1000);
}
// System.out.println("After GC: " + getMemoryStats());
if (ref.get() != null) {
throw new IllegalStateException("Your JVM is not releasing SoftReference, try running the testcase with less memory (-Xmx)");
}
}
/**
* Create a new class loader instance.
*/
private static URLClassLoader newClassLoader() throws MalformedURLException {
final String dataFilePath = MemoryLeakTestCase.class.getResource("pojotests").getFile();
//System.out.println("dataFilePath: " + dataFilePath);
final String location = "file://" + dataFilePath.substring(0,dataFilePath.length()-"org.apache.commons.beanutils2.memoryleaktests.pojotests".length());
//System.out.println("location: " + location);
final StringBuilder newString = new StringBuilder();
for (int i=0;i<location.length();i++) {
if (location.charAt(i)=='\\') {
newString.append("/");
} else {
newString.append(location.charAt(i));
}
}
final String classLocation = newString.toString();
//System.out.println("classlocation: " + classLocation);
final URLClassLoader theLoader = URLClassLoader.newInstance(new URL[]{new URL(classLocation)},null);
return theLoader;
}
}