This is an automated email from the ASF dual-hosted git repository. ggrzybek pushed a commit to branch CAMEL-15061-3.4.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit 790cba05d465ea426a0488cc59149534c50fdb7c Author: Grzegorz Grzybek <gr.grzy...@gmail.com> AuthorDate: Thu May 14 14:50:03 2020 +0200 [CAMEL-15061] Cache failed attempts to load classes to greatly improve performance --- .../camel/impl/engine/DefaultFactoryFinder.java | 21 ++++++++++++++++++++- .../camel/impl/engine/DefaultFactoryFinderTest.java | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultFactoryFinder.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultFactoryFinder.java index d1ecced..bd4f64e 100644 --- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultFactoryFinder.java +++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultFactoryFinder.java @@ -37,6 +37,8 @@ import org.apache.camel.util.IOHelper; public class DefaultFactoryFinder implements FactoryFinder { private final ConcurrentMap<String, Class<?>> classMap = new ConcurrentHashMap<>(); + private final ConcurrentMap<String, Boolean> classesNotFound = new ConcurrentHashMap<>(); + private final ConcurrentMap<String, Exception> classesNotFoundExceptions = new ConcurrentHashMap<>(); private final ClassResolver classResolver; private final String path; @@ -135,16 +137,33 @@ public class DefaultFactoryFinder implements FactoryFinder { * later on with the only purpose to re-throw the original exception. */ protected Class<?> addToClassMap(String key, ClassSupplier mappingFunction) { - return classMap.computeIfAbsent(key, new Function<String, Class<?>>() { + if (classesNotFound.containsKey(key) || classesNotFoundExceptions.containsKey(key)) { + Exception e = classesNotFoundExceptions.get(key); + if (e == null) { + return null; + } else { + throw RuntimeCamelException.wrapRuntimeException(e); + } + } + + Class<?> suppliedClass = classMap.computeIfAbsent(key, new Function<String, Class<?>>() { @Override public Class<?> apply(String classKey) { try { return mappingFunction.get(); } catch (Exception e) { + classesNotFoundExceptions.put(key, e); throw RuntimeCamelException.wrapRuntimeException(e); } } }); + + if (suppliedClass == null) { + // mark the key as non-resolvable to prevent pointless searching + classesNotFound.put(key, Boolean.TRUE); + } + + return suppliedClass; } @FunctionalInterface diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultFactoryFinderTest.java b/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultFactoryFinderTest.java index 443b88b..615af00 100644 --- a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultFactoryFinderTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultFactoryFinderTest.java @@ -31,6 +31,8 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class DefaultFactoryFinderTest { @@ -64,6 +66,24 @@ public class DefaultFactoryFinderTest { } @Test + public void shouldCacheFailedAttemptToResolveClass() throws IOException { + final ClassResolver classResolver = mock(ClassResolver.class); + + final String properties = "class=" + TestImplA.class.getName(); + + when(classResolver.loadResourceAsStream("/org/apache/camel/impl/TestImplA")).thenReturn(new ByteArrayInputStream(properties.getBytes())); + + when(classResolver.resolveClass(TestImplA.class.getName())).thenReturn(null); + + final DefaultFactoryFinder factoryFinder = new DefaultFactoryFinder(classResolver, TEST_RESOURCE_PATH); + + assertFalse(factoryFinder.findClass("TestImplA").isPresent()); + assertFalse(factoryFinder.findClass("TestImplA").isPresent()); + + verify(classResolver, times(1)).resolveClass(TestImplA.class.getName()); + } + + @Test public void shouldComplainIfInstanceTypeIsNotAsExpected() throws ClassNotFoundException, IOException { final Injector injector = mock(Injector.class);