diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java index 3112365aaaa..c5c2464778b 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java +++ b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java @@ -17,8 +17,9 @@ package org.springframework.core.type.classreading; import java.io.IOException; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -33,8 +34,20 @@ import org.springframework.core.io.ResourceLoader; */ public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory { - private final Map classReaderCache = new HashMap(); + private static final int MAX_ENTRIES = 50; + @SuppressWarnings("serial") + private static final Map createLRUCache() { + return new LinkedHashMap(MAX_ENTRIES, 0.75f, true) { + + @Override + protected boolean removeEldestEntry(Entry eldest) { + return size() > MAX_ENTRIES; + } + }; + } + + private final Map classReaderCache = createLRUCache(); /** * Create a new CachingMetadataReaderFactory for the default class loader. @@ -60,7 +73,6 @@ public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory { super(classLoader); } - @Override public MetadataReader getMetadataReader(Resource resource) throws IOException { synchronized (this.classReaderCache) { @@ -72,5 +84,4 @@ public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory { return metadataReader; } } - -} +} \ No newline at end of file diff --git a/org.springframework.core/src/test/java/org/springframework/core/type/CachingMetadataReaderLeakTest.java b/org.springframework.core/src/test/java/org/springframework/core/type/CachingMetadataReaderLeakTest.java new file mode 100644 index 00000000000..3903214bd6e --- /dev/null +++ b/org.springframework.core/src/test/java/org/springframework/core/type/CachingMetadataReaderLeakTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2006-2010 the original author or authors. + * + * Licensed 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.springframework.core.type; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +import java.net.URL; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; + +/** + * Unit test checking the behaviour of {@link CachingMetadataReaderFactory under load. + * If the cache is not controller, this test should fail with an out of memory exception around entry + * 5k. + * + * @author Costin Leau + */ +public class CachingMetadataReaderLeakTest { + + private static int ITEMS_LOAD = 9999; + private MetadataReaderFactory mrf; + + @Before + public void before() { + mrf = new CachingMetadataReaderFactory(); + } + + @Test + public void testSignificantLoad() throws Exception { + URL url = getClass().getResource("/org/springframework/beans/TestBean.class"); + assertThat(url, notNullValue()); + + // look at a LOT of items + for (int i = 0; i < ITEMS_LOAD; i++) { + Resource resource = new UrlResource(url) { + private int counter = 0; + + @Override + public boolean equals(Object obj) { + return (obj == this); + + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + }; + + MetadataReader reader = mrf.getMetadataReader(resource); + assertThat(reader, notNullValue()); + } + } +}