You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/10/15 07:15:01 UTC

[camel] branch camel-4.0.x updated: Optimize EndpointHelper.matchEndpoint (#11723)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-4.0.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.0.x by this push:
     new edb7f9be4d6 Optimize EndpointHelper.matchEndpoint (#11723)
edb7f9be4d6 is described below

commit edb7f9be4d6d59393b5bbd4a4e1fa448a3994715
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Oct 15 09:14:18 2023 +0200

    Optimize EndpointHelper.matchEndpoint (#11723)
    
    * CAMEL-19987: camel-core - Optimize EndpointHelper.matchEndpoint
    
    * CAMEL-19987: camel-core - Optimize EndpointHelper.matchEndpoint to avoid regexp in fast-mode
    
    * CAMEL-19987: camel-core - Optimize EndpointHelper.matchEndpoint to avoid regexp in fast-mode
---
 .../camel/impl/engine/AbstractCamelContext.java    | 15 +++++-
 .../camel/impl/DefaultEndpointRegistryTest.java    | 55 ++++++++++++++++++++++
 .../org/apache/camel/support/EndpointHelper.java   | 33 +++++++++----
 .../org/apache/camel/support/PatternHelper.java    |  2 +-
 4 files changed, 93 insertions(+), 12 deletions(-)

diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index c92234628a6..8ad6d6e3d72 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -699,7 +699,20 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void removeEndpoint(Endpoint endpoint) throws Exception {
-        removeEndpoints(endpoint.getEndpointUri());
+        // optimize as uri on endpoint is already normalized
+        String uri = endpoint.getEndpointUri();
+        NormalizedUri key = NormalizedUri.newNormalizedUri(uri, true);
+        Endpoint oldEndpoint = endpoints.remove(key);
+        if (oldEndpoint != null) {
+            try {
+                stopServices(oldEndpoint);
+            } catch (Exception e) {
+                LOG.warn("Error stopping endpoint {}. This exception will be ignored.", oldEndpoint, e);
+            }
+            for (LifecycleStrategy strategy : lifecycleStrategies) {
+                strategy.onEndpointRemove(oldEndpoint);
+            }
+        }
     }
 
     @Override
diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointRegistryTest.java b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointRegistryTest.java
index 4bcae075a55..23341b587c2 100644
--- a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointRegistryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointRegistryTest.java
@@ -19,7 +19,10 @@ package org.apache.camel.impl;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.camel.Endpoint;
+import org.apache.camel.FluentProducerTemplate;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.ServiceStatus;
 import org.apache.camel.builder.RouteBuilder;
@@ -27,12 +30,64 @@ import org.apache.camel.impl.engine.DefaultEndpointRegistry;
 import org.apache.camel.impl.engine.SimpleCamelContext;
 import org.apache.camel.spi.EndpointRegistry;
 import org.apache.camel.support.NormalizedUri;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class DefaultEndpointRegistryTest {
 
+    @Test
+    public void testRemoveEndpoint() throws Exception {
+        DefaultCamelContext ctx = new DefaultCamelContext();
+        ctx.start();
+
+        ctx.getEndpoint("direct:one");
+        Endpoint e = ctx.getEndpoint("direct:two");
+        ctx.getEndpoint("direct:three");
+
+        Assertions.assertEquals(3, ctx.getEndpoints().size());
+        ctx.removeEndpoint(e);
+        Assertions.assertEquals(2, ctx.getEndpoints().size());
+    }
+
+    @Test
+    public void testRemoveEndpointToD() throws Exception {
+        DefaultCamelContext ctx = new DefaultCamelContext();
+        ctx.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                        .toD().cacheSize(10).uri("mock:${header.foo}");
+            }
+        });
+        final AtomicInteger cnt = new AtomicInteger();
+        ctx.addLifecycleStrategy(new DummyLifecycleStrategy() {
+            @Override
+            public void onEndpointRemove(Endpoint endpoint) {
+                cnt.incrementAndGet();
+            }
+        });
+        ctx.start();
+
+        Assertions.assertEquals(0, cnt.get());
+        Assertions.assertEquals(1, ctx.getEndpoints().size());
+
+        FluentProducerTemplate template = ctx.createFluentProducerTemplate();
+        for (int i = 0; i < 100; i++) {
+            template.withBody("Hello").withHeader("foo", "" + i).to("direct:start").send();
+        }
+
+        Awaitility.await().untilAsserted(() -> {
+            Assertions.assertEquals(11, ctx.getEndpoints().size());
+        });
+
+        Assertions.assertEquals(90, cnt.get());
+
+        ctx.stop();
+    }
+
     @Test
     public void testMigration() throws Exception {
         DefaultCamelContext ctx = new DefaultCamelContext();
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java
index c37820caa3f..4a6449eec02 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java
@@ -221,7 +221,7 @@ public final class EndpointHelper {
      * @param  context the Camel context, if <tt>null</tt> then property placeholder resolution is skipped.
      * @param  uri     the endpoint uri
      * @param  pattern a pattern to match
-     * @return         <tt>true</tt> if match, <tt>false</tt> otherwise.
+     * @return         <tt>true</tt> if matched, <tt>false</tt> otherwise.
      */
     public static boolean matchEndpoint(CamelContext context, String uri, String pattern) {
         if (context != null) {
@@ -235,18 +235,31 @@ public final class EndpointHelper {
         // normalize uri so we can do endpoint hits with minor mistakes and parameters is not in the same order
         uri = normalizeEndpointUri(uri);
 
-        // we need to test with and without scheme separators (//)
-        boolean match = PatternHelper.matchPattern(toggleUriSchemeSeparators(uri), pattern);
-        match |= PatternHelper.matchPattern(uri, pattern);
-        if (!match && pattern != null && pattern.contains("?")) {
-            // try normalizing the pattern as a uri for exact matching, so parameters are ordered the same as in the endpoint uri
+        // do fast matching without regexp first
+        boolean match = doMatchEndpoint(uri, pattern, false);
+        if (!match) {
+            // this is slower as pattern is compiled as regexp
+            match = doMatchEndpoint(uri, pattern, true);
+        }
+        return match;
+    }
+
+    private static boolean doMatchEndpoint(String uri, String pattern, boolean regexp) {
+        String toggleUri = null;
+        boolean match = regexp ? PatternHelper.matchRegex(uri, pattern) : PatternHelper.matchPattern(uri, pattern);
+        if (!match) {
+            toggleUri = toggleUriSchemeSeparators(uri);
+            match = regexp ? PatternHelper.matchRegex(toggleUri, pattern) : PatternHelper.matchPattern(toggleUri, pattern);
+        }
+        if (!match && !regexp && pattern != null && pattern.contains("?")) {
+            // this is only need to be done once (in fast mode when regexp=false)
+            // try normalizing the pattern as an uri for exact matching, so parameters are ordered the same as in the endpoint uri
             try {
                 pattern = URISupport.normalizeUri(pattern);
                 // try both with and without scheme separators (//)
-                match = toggleUriSchemeSeparators(uri).equalsIgnoreCase(pattern);
-                return match || uri.equalsIgnoreCase(pattern);
+                return uri.equalsIgnoreCase(pattern) || toggleUri.equalsIgnoreCase(pattern);
             } catch (URISyntaxException e) {
-                //Can't normalize and original match failed
+                // cannot normalize and original match failed
                 return false;
             } catch (Exception e) {
                 throw new ResolveEndpointFailedException(uri, e);
@@ -278,7 +291,7 @@ public final class EndpointHelper {
      * Is the given parameter a reference parameter (starting with a # char)
      *
      * @param  parameter the parameter
-     * @return           <tt>true</tt> if its a reference parameter
+     * @return           <tt>true</tt> if it's a reference parameter
      */
     public static boolean isReferenceParameter(String parameter) {
         return parameter != null && parameter.trim().startsWith("#") && parameter.trim().length() > 1;
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/PatternHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/PatternHelper.java
index e490f7e6a51..a716b860ea5 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/PatternHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/PatternHelper.java
@@ -116,7 +116,7 @@ public final class PatternHelper {
      * @param  pattern a pattern to match
      * @return         <tt>true</tt> if match, <tt>false</tt> otherwise.
      */
-    private static boolean matchRegex(String name, String pattern) {
+    public static boolean matchRegex(String name, String pattern) {
         // match by regular expression
         try {
             Pattern compiled = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);