This is an automated email from the ASF dual-hosted git repository. ggrzybek pushed a commit to branch camel-2.25.x in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-2.25.x by this push: new 5698b0d [CAMEL-15061] Cache failed attempts to load classes to greatly improve performance (#3825) 5698b0d is described below commit 5698b0df3d16c30aae8561794a166f543c036bb6 Author: Grzegorz Grzybek <gr.grzy...@gmail.com> AuthorDate: Thu May 14 12:57:10 2020 +0200 [CAMEL-15061] Cache failed attempts to load classes to greatly improve performance (#3825) --- .../apache/camel/impl/DefaultFactoryFinder.java | 21 ++++++++++++++- .../camel/impl/DefaultFactoryFinderTest.java | 31 ++++++++++++++++++++++ .../blueprint/BlueprintContainerRegistry.java | 17 +++++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultFactoryFinder.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultFactoryFinder.java index 3ed501e..d8721dd 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/DefaultFactoryFinder.java +++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultFactoryFinder.java @@ -40,6 +40,7 @@ import org.apache.camel.util.IOHelper; public class DefaultFactoryFinder implements FactoryFinder { private final ConcurrentMap<String, Class<?>> classMap = new ConcurrentHashMap<>(); + private final ConcurrentMap<String, Exception> classesNotFound = new ConcurrentHashMap<>(); private final ClassResolver classResolver; private final String path; @@ -166,20 +167,38 @@ public class DefaultFactoryFinder implements FactoryFinder { */ protected Class<?> addToClassMap(String key, ClassSupplier mappingFunction) throws ClassNotFoundException, IOException { try { - return classMap.computeIfAbsent(key, new Function<String, Class<?>>() { + if (classesNotFound.containsKey(key)) { + Exception e = classesNotFound.get(key); + if (e == null) { + return null; + } else { + throw new WrappedRuntimeException(e); + } + } + + Class<?> suppliedClass = classMap.computeIfAbsent(key, new Function<String, Class<?>>() { @Override public Class<?> apply(String classKey) { try { return mappingFunction.get(); } catch (ClassNotFoundException e) { + classesNotFound.put(key, e); throw new WrappedRuntimeException(e); } catch (NoFactoryAvailableException e) { + classesNotFound.put(key, e); throw new WrappedRuntimeException(e); } catch (IOException e) { throw new WrappedRuntimeException(e); } } }); + + if (suppliedClass == null) { + // mark the key as non-resolvable to prevent pointless searching + classesNotFound.put(key, null); + } + + return suppliedClass; } catch (WrappedRuntimeException e) { if (e.getCause() instanceof ClassNotFoundException) { throw (ClassNotFoundException)e.getCause(); diff --git a/camel-core/src/test/java/org/apache/camel/impl/DefaultFactoryFinderTest.java b/camel-core/src/test/java/org/apache/camel/impl/DefaultFactoryFinderTest.java index 88f67ee..b34170b 100644 --- a/camel-core/src/test/java/org/apache/camel/impl/DefaultFactoryFinderTest.java +++ b/camel-core/src/test/java/org/apache/camel/impl/DefaultFactoryFinderTest.java @@ -34,6 +34,8 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; 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 { @@ -74,6 +76,35 @@ 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")) + .thenAnswer((invocation) -> new ByteArrayInputStream(properties.getBytes())); + + when(classResolver.resolveClass(TestImplA.class.getName())).thenReturn(null); + + final DefaultFactoryFinder factoryFinder = new DefaultFactoryFinder(classResolver, TEST_RESOURCE_PATH); + + try { + factoryFinder.findClass("TestImplA", null); + fail("Should have thrown ClassNotFoundException"); + } catch (final ClassNotFoundException e) { + assertEquals(TestImplA.class.getName(), e.getMessage()); + } + try { + factoryFinder.findClass("TestImplA", null); + fail("Should have thrown ClassNotFoundException"); + } catch (final ClassNotFoundException e) { + assertEquals(TestImplA.class.getName(), e.getMessage()); + } + + verify(classResolver, times(1)).resolveClass(TestImplA.class.getName()); + } + + @Test public void shouldComplainIfInstanceTypeIsNotAsExpected() throws ClassNotFoundException, IOException { final Injector injector = mock(Injector.class); diff --git a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintContainerRegistry.java b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintContainerRegistry.java index 9a60984..cde87dd 100644 --- a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintContainerRegistry.java +++ b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintContainerRegistry.java @@ -16,10 +16,12 @@ */ package org.apache.camel.blueprint; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.apache.camel.NoSuchBeanException; import org.apache.camel.spi.Registry; @@ -34,6 +36,8 @@ public class BlueprintContainerRegistry implements Registry { private final BlueprintContainer blueprintContainer; + private final Map<Class<?>, Map<String, Class<?>>> perBlueprintContainerCache = new ConcurrentHashMap<>(); + public BlueprintContainerRegistry(BlueprintContainer blueprintContainer) { this.blueprintContainer = blueprintContainer; } @@ -71,13 +75,24 @@ public class BlueprintContainerRegistry implements Registry { } @Override + @SuppressWarnings("unchecked") public <T> Map<String, T> findByTypeWithName(Class<T> type) { - return lookupByType(blueprintContainer, type); + if (perBlueprintContainerCache.containsKey(type)) { + return (Map<String, T>) perBlueprintContainerCache.get(type); + } + Map<String, T> result = lookupByType(blueprintContainer, type); + perBlueprintContainerCache.put(type, (Map<String, Class<?>>) result); + return result; } @Override + @SuppressWarnings("unchecked") public <T> Set<T> findByType(Class<T> type) { + if (perBlueprintContainerCache.containsKey(type)) { + return new HashSet<T>((Collection<? extends T>) perBlueprintContainerCache.get(type).values()); + } Map<String, T> map = lookupByType(blueprintContainer, type); + perBlueprintContainerCache.put(type, (Map<String, Class<?>>) map); return new HashSet<>(map.values()); }