You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2020/09/29 10:47:22 UTC

[camel] branch master updated: CAMEL-15590: route templates: add an hook to further customize the RouteDefinition computed out of a template

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

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new 5029d32  CAMEL-15590: route templates: add an hook to further customize the RouteDefinition computed out of a template
5029d32 is described below

commit 5029d32302fbd5e8d2688b82b0dc66637633ef62
Author: Luca Burgazzoli <lb...@gmail.com>
AuthorDate: Tue Sep 29 11:30:19 2020 +0200

    CAMEL-15590: route templates: add an hook to further customize the RouteDefinition computed out of a template
---
 .../org/apache/camel/impl/DefaultCamelContext.java |   6 +-
 .../java/org/apache/camel/impl/DefaultModel.java   |  65 +++++++---
 .../camel/impl/lw/LightweightCamelContext.java     |   5 +
 .../main/java/org/apache/camel/model/Model.java    |   9 ++
 .../camel/model/RouteTemplateDefinition.java       |   5 +
 .../camel/builder/RouteTemplateConverterTest.java  | 136 +++++++++++++++++++++
 6 files changed, 205 insertions(+), 21 deletions(-)

diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
index c4ead09..b6d16d9 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
@@ -183,6 +183,11 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
     }
 
     @Override
+    public void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter) {
+        model.addRouteTemplateDefinitionConverter(templateIdPattern, converter);
+    }
+
+    @Override
     public String addRouteFromTemplate(String routeId, String routeTemplateId, Map<String, Object> parameters)
             throws Exception {
         return model.addRouteFromTemplate(routeId, routeTemplateId, parameters);
@@ -487,5 +492,4 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
                 ? new TransformerKey(def.getScheme())
                 : new TransformerKey(new DataType(def.getFromType()), new DataType(def.getToType()));
     }
-
 }
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
index bdaf469..f545d28 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
@@ -46,6 +46,7 @@ import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.transformer.TransformerDefinition;
 import org.apache.camel.model.validator.ValidatorDefinition;
+import org.apache.camel.util.AntPathMatcher;
 
 public class DefaultModel implements Model {
 
@@ -55,6 +56,7 @@ public class DefaultModel implements Model {
     private final List<RouteDefinition> routeDefinitions = new ArrayList<>();
     private final List<RouteTemplateDefinition> routeTemplateDefinitions = new ArrayList<>();
     private final List<RestDefinition> restDefinitions = new ArrayList<>();
+    private final Map<String, RouteTemplateDefinition.Converter> routeTemplateConverters = new ConcurrentHashMap<>();
     private Map<String, DataFormatDefinition> dataFormats = new HashMap<>();
     private List<TransformerDefinition> transformers = new ArrayList<>();
     private List<ValidatorDefinition> validators = new ArrayList<>();
@@ -68,6 +70,15 @@ public class DefaultModel implements Model {
         this.camelContext = camelContext;
     }
 
+    protected static <T> T lookup(CamelContext context, String ref, Class<T> type) {
+        try {
+            return context.getRegistry().lookupByNameAndType(ref, type);
+        } catch (Exception e) {
+            // need to ignore not same type and return it as null
+            return null;
+        }
+    }
+
     public CamelContext getCamelContext() {
         return camelContext;
     }
@@ -194,6 +205,11 @@ public class DefaultModel implements Model {
     }
 
     @Override
+    public void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter) {
+        routeTemplateConverters.put(templateIdPattern, converter);
+    }
+
+    @Override
     public String addRouteFromTemplate(final String routeId, final String routeTemplateId, final Map<String, Object> parameters)
             throws Exception {
         RouteTemplateDefinition target = null;
@@ -208,7 +224,7 @@ public class DefaultModel implements Model {
         }
 
         StringJoiner templatesBuilder = new StringJoiner(", ");
-        final Map<String, Object> prop = new HashMap();
+        final Map<String, Object> prop = new HashMap<>();
         // include default values first from the template (and validate that we have inputs for all required parameters)
         if (target.getTemplateParameters() != null) {
             for (RouteTemplateParameterDefinition temp : target.getTemplateParameters()) {
@@ -232,7 +248,25 @@ public class DefaultModel implements Model {
             prop.putAll(parameters);
         }
 
-        RouteDefinition def = target.asRouteDefinition();
+        RouteTemplateDefinition.Converter converter = RouteTemplateDefinition::asRouteDefinition;
+
+        for (Map.Entry<String, RouteTemplateDefinition.Converter> entry : routeTemplateConverters.entrySet()) {
+            final String key = entry.getKey();
+            final String templateId = target.getId();
+
+            if ("*".equals(key) || templateId.equals(key)) {
+                converter = entry.getValue();
+                break;
+            } else if (AntPathMatcher.INSTANCE.match(key, templateId)) {
+                converter = entry.getValue();
+                break;
+            } else if (templateId.matches(key)) {
+                converter = entry.getValue();
+                break;
+            }
+        }
+
+        RouteDefinition def = converter.apply(target);
         if (routeId != null) {
             def.setId(routeId);
         }
@@ -411,18 +445,13 @@ public class DefaultModel implements Model {
     }
 
     @Override
-    public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) {
-        this.dataFormats = dataFormats;
-    }
-
-    @Override
     public Map<String, DataFormatDefinition> getDataFormats() {
         return dataFormats;
     }
 
     @Override
-    public void setTransformers(List<TransformerDefinition> transformers) {
-        this.transformers = transformers;
+    public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) {
+        this.dataFormats = dataFormats;
     }
 
     @Override
@@ -431,8 +460,8 @@ public class DefaultModel implements Model {
     }
 
     @Override
-    public void setValidators(List<ValidatorDefinition> validators) {
-        this.validators = validators;
+    public void setTransformers(List<TransformerDefinition> transformers) {
+        this.transformers = transformers;
     }
 
     @Override
@@ -441,6 +470,11 @@ public class DefaultModel implements Model {
     }
 
     @Override
+    public void setValidators(List<ValidatorDefinition> validators) {
+        this.validators = validators;
+    }
+
+    @Override
     public void setRouteFilterPattern(String include, String exclude) {
         setRouteFilter(RouteFilters.filterByPattern(include, exclude));
     }
@@ -462,13 +496,4 @@ public class DefaultModel implements Model {
         return camelContext.isStarted() && !camelContext.isStarting();
     }
 
-    protected static <T> T lookup(CamelContext context, String ref, Class<T> type) {
-        try {
-            return context.getRegistry().lookupByNameAndType(ref, type);
-        } catch (Exception e) {
-            // need to ignore not same type and return it as null
-            return null;
-        }
-    }
-
 }
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
index f2fc22d..3fc21ce 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
@@ -1554,6 +1554,11 @@ public class LightweightCamelContext implements ExtendedCamelContext, CatalogCam
     }
 
     @Override
+    public void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter) {
+        getModelCamelContext().addRouteTemplateDefinitionConverter(templateIdPattern, converter);
+    }
+
+    @Override
     public String addRouteFromTemplate(String routeId, String routeTemplateId, Map<String, Object> parameters)
             throws Exception {
         return getModelCamelContext().addRouteFromTemplate(routeId, routeTemplateId, parameters);
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/model/Model.java b/core/camel-core-engine/src/main/java/org/apache/camel/model/Model.java
index 956fab8..4981706 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/model/Model.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/model/Model.java
@@ -156,6 +156,15 @@ public interface Model {
     void removeRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception;
 
     /**
+     * Add a converter to translate a {@link RouteTemplateDefinition} to a {@link RouteDefinition}.
+     *
+     * @param templateIdPattern the route template ut to whom a pattern should eb applied
+     * @param converter         the {@link RouteTemplateDefinition.Converter} used to convert a
+     *                          {@link RouteTemplateDefinition} to a {@link RouteDefinition}
+     */
+    void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter);
+
+    /**
      * Adds a new route from a given route template
      *
      * @param  routeId         the id of the new route to add (optional)
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
index 5871cd0..c4c1252 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
@@ -30,6 +30,7 @@ import org.apache.camel.Endpoint;
 import org.apache.camel.builder.EndpointConsumerBuilder;
 import org.apache.camel.spi.AsEndpointUri;
 import org.apache.camel.spi.Metadata;
+import org.apache.camel.util.function.ThrowingFunction;
 
 /**
  * Defines a route template (parameterized routes)
@@ -213,4 +214,8 @@ public class RouteTemplateDefinition extends OptionalIdentifiedDefinition {
 
         return copy;
     }
+
+    @FunctionalInterface
+    public interface Converter extends ThrowingFunction<RouteTemplateDefinition, RouteDefinition, Exception> {
+    }
 }
diff --git a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateConverterTest.java b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateConverterTest.java
new file mode 100644
index 0000000..c884969
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateConverterTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.camel.builder;
+
+import java.util.stream.IntStream;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.model.FromDefinition;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.RouteTemplateDefinition;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.util.CollectionHelper.mapOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteTemplateConverterTest extends ContextTestSupport {
+    @Test
+    public void testCreateRouteFromRouteTemplateWithDefaultConverter() throws Exception {
+        context.addRouteTemplateDefinitionConverter("myTemplate1", RouteTemplateDefinition::asRouteDefinition);
+        context.addRouteFromTemplate("first", "myTemplate1", mapOf("foo", "one", "bar", "cheese"));
+
+        assertEquals(1, context.getRouteDefinitions().size());
+        assertEquals(1, context.getRoutes().size());
+
+        assertEquals("direct:{{foo}}", context.getRouteDefinition("first").getInput().getEndpointUri());
+        assertEquals("direct://one", context.getRoute("first").getEndpoint().getEndpointUri());
+    }
+
+    @Test
+    public void testCreateRouteFromRouteTemplateWithCustomConverter() throws Exception {
+        context.addRouteTemplateDefinitionConverter("myTemplate1", template -> {
+            final RouteDefinition def = template.asRouteDefinition();
+            final String inUri = def.getInput().getEndpointUri();
+            def.setInput(null);
+            def.setInput(new FromDefinition(inUri + "?timeout=60s"));
+            return def;
+        });
+
+        context.addRouteFromTemplate("first", "myTemplate1", mapOf("foo", "one", "bar", "cheese"));
+
+        assertEquals(1, context.getRouteDefinitions().size());
+        assertEquals(1, context.getRoutes().size());
+
+        assertEquals("direct:{{foo}}?timeout=60s", context.getRouteDefinition("first").getInput().getEndpointUri());
+        assertEquals("direct://one?timeout=60s", context.getRoute("first").getEndpoint().getEndpointUri());
+    }
+
+    @Test
+    public void testCreateRouteFromRouteTemplateWithCustomConverterPatter() throws Exception {
+        context.addRouteTemplateDefinitionConverter("myTemplate[12]", template -> {
+            final RouteDefinition def = template.asRouteDefinition();
+            final String inUri = def.getInput().getEndpointUri();
+            def.setInput(null);
+            def.setInput(new FromDefinition(inUri + "?timeout=60s"));
+            return def;
+        });
+
+        IntStream.of(1, 2, 3).mapToObj(Integer::toString).forEach(index -> {
+            try {
+                context.addRouteFromTemplate(index, "myTemplate" + index, mapOf("foo", index, "bar", "cheese"));
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        assertEquals(3, context.getRouteDefinitions().size());
+        assertEquals(3, context.getRoutes().size());
+
+        assertEquals("direct:{{foo}}?timeout=60s", context.getRouteDefinition("1").getInput().getEndpointUri());
+        assertEquals("direct://1?timeout=60s", context.getRoute("1").getEndpoint().getEndpointUri());
+        assertEquals("direct:{{foo}}?timeout=60s", context.getRouteDefinition("2").getInput().getEndpointUri());
+        assertEquals("direct://2?timeout=60s", context.getRoute("2").getEndpoint().getEndpointUri());
+        assertEquals("direct:{{foo}}", context.getRouteDefinition("3").getInput().getEndpointUri());
+        assertEquals("direct://3", context.getRoute("3").getEndpoint().getEndpointUri());
+    }
+
+    @Test
+    public void testCreateRouteFromRouteTemplateWithCustomConverterGlob() {
+        context.addRouteTemplateDefinitionConverter("*", template -> {
+            final RouteDefinition def = template.asRouteDefinition();
+            final String inUri = def.getInput().getEndpointUri();
+            def.setInput(null);
+            def.setInput(new FromDefinition(inUri + "?timeout=60s"));
+            return def;
+        });
+
+        IntStream.of(1, 2, 3).mapToObj(Integer::toString).forEach(index -> {
+            try {
+                context.addRouteFromTemplate(index, "myTemplate" + index, mapOf("foo", index, "bar", "cheese"));
+
+                assertEquals("direct:{{foo}}?timeout=60s", context.getRouteDefinition(index).getInput().getEndpointUri());
+                assertEquals("direct://" + index + "?timeout=60s", context.getRoute(index).getEndpoint().getEndpointUri());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                routeTemplate("myTemplate1")
+                        .templateParameter("foo")
+                        .templateParameter("bar")
+                        .from("direct:{{foo}}")
+                        .to("mock:{{bar}}");
+                routeTemplate("myTemplate2")
+                        .templateParameter("foo")
+                        .templateParameter("bar")
+                        .from("direct:{{foo}}")
+                        .to("mock:{{bar}}");
+                routeTemplate("myTemplate3")
+                        .templateParameter("foo")
+                        .templateParameter("bar")
+                        .from("direct:{{foo}}")
+                        .to("mock:{{bar}}");
+            }
+        };
+    }
+}