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-k-runtime.git

commit 36a11797d1a0c51f27928c2067b9f7cb430b05c5
Author: Pasquale Congiusti <pasquale.congiu...@gmail.com>
AuthorDate: Tue Apr 13 12:45:48 2021 +0200

    feat(core): default error handler
    
    * Introduced a new SourceType, errorHandler, that can be used as a source 
for spotting a default error handler that will be used in those routes that 
don't specify any
    * Forced the sources sorting in order to load errorHandler first, sources 
and templates
    * Added a check to make sure only one error handler is provided
---
 .../main/java/org/apache/camel/k/SourceType.java   |   2 +
 .../k/listener/GlobalErrorHandlerConfigurer.java   |  67 ------------
 .../apache/camel/k/listener/SourcesConfigurer.java |  42 +++++++-
 .../org/apache/camel/k/support/SourcesSupport.java |  59 +++++++++--
 .../services/org.apache.camel.k.Runtime$Listener   |   1 -
 .../camel/k/listener/SourceConfigurerTest.java     | 114 +++++++++++++++++++++
 .../support/GlobalErrorHandlerConfigurerTest.java  |  47 ---------
 7 files changed, 206 insertions(+), 126 deletions(-)

diff --git a/camel-k-core/api/src/main/java/org/apache/camel/k/SourceType.java 
b/camel-k-core/api/src/main/java/org/apache/camel/k/SourceType.java
index ed9717a..243a7b8 100644
--- a/camel-k-core/api/src/main/java/org/apache/camel/k/SourceType.java
+++ b/camel-k-core/api/src/main/java/org/apache/camel/k/SourceType.java
@@ -17,6 +17,8 @@
 package org.apache.camel.k;
 
 public enum SourceType {
+    // Order matters. We want the sources to be loaded following this enum 
order.
+    errorHandler,
     source,
     template
 }
diff --git 
a/camel-k-core/support/src/main/java/org/apache/camel/k/listener/GlobalErrorHandlerConfigurer.java
 
b/camel-k-core/support/src/main/java/org/apache/camel/k/listener/GlobalErrorHandlerConfigurer.java
deleted file mode 100644
index 794f794..0000000
--- 
a/camel-k-core/support/src/main/java/org/apache/camel/k/listener/GlobalErrorHandlerConfigurer.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.k.listener;
-
-import org.apache.camel.ExtendedCamelContext;
-import org.apache.camel.builder.DeadLetterChannelBuilder;
-import org.apache.camel.k.Runtime;
-import org.apache.camel.k.support.PropertiesSupport;
-import org.apache.camel.spi.Configurer;
-import org.apache.camel.util.ObjectHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Configurer
-public class GlobalErrorHandlerConfigurer extends AbstractPhaseListener {
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(GlobalErrorHandlerConfigurer.class);
-
-    public static final String CAMEL_K_PREFIX = "camel.k.";
-    public static final String CAMEL_K_GLOBAL_ERROR_HANDLER_PREFIX = 
"camel.k.global-error-handler";
-
-    private String globalErrorHandler;
-
-    public GlobalErrorHandlerConfigurer() {
-        super(Runtime.Phase.ConfigureRoutes);
-    }
-
-    public String getGlobalErrorHandler(){
-        return this.globalErrorHandler;
-    }
-
-    public void setGlobalErrorHandler(String globalErrorHandler){
-        this.globalErrorHandler = globalErrorHandler;
-    }
-
-    @Override
-    protected void accept(Runtime runtime) {
-        PropertiesSupport.bindProperties(
-            runtime.getCamelContext(),
-            this,
-            k -> k.startsWith(CAMEL_K_GLOBAL_ERROR_HANDLER_PREFIX),
-            CAMEL_K_PREFIX);
-        if (ObjectHelper.isNotEmpty(this.globalErrorHandler)) {
-            loadGlobalErrorHandler(runtime, this.getGlobalErrorHandler());
-        }
-    }
-
-    public static void loadGlobalErrorHandler(Runtime runtime, String uri) {
-        LOGGER.info("Setting a default global error handler factory to 
deadletter channel {}", uri);
-        runtime.getCamelContext().adapt(ExtendedCamelContext.class)
-                .setErrorHandlerFactory(new DeadLetterChannelBuilder(uri));
-    }
-
-}
diff --git 
a/camel-k-core/support/src/main/java/org/apache/camel/k/listener/SourcesConfigurer.java
 
b/camel-k-core/support/src/main/java/org/apache/camel/k/listener/SourcesConfigurer.java
index 30c2efe..95ddd92 100644
--- 
a/camel-k-core/support/src/main/java/org/apache/camel/k/listener/SourcesConfigurer.java
+++ 
b/camel-k-core/support/src/main/java/org/apache/camel/k/listener/SourcesConfigurer.java
@@ -18,12 +18,16 @@ package org.apache.camel.k.listener;
 
 import org.apache.camel.k.Runtime;
 import org.apache.camel.k.SourceDefinition;
+import org.apache.camel.k.SourceType;
 import org.apache.camel.k.support.Constants;
 import org.apache.camel.k.support.PropertiesSupport;
 import org.apache.camel.k.support.SourcesSupport;
 import org.apache.camel.spi.Configurer;
 import org.apache.camel.util.ObjectHelper;
 
+import java.util.Arrays;
+import java.util.Collections;
+
 @Configurer
 public class SourcesConfigurer extends AbstractPhaseListener {
     public static final String CAMEL_K_PREFIX = "camel.k.";
@@ -64,14 +68,44 @@ public class SourcesConfigurer extends 
AbstractPhaseListener {
         // property that can't be bound to this configurer.
         //
         PropertiesSupport.bindProperties(
-            runtime.getCamelContext(),
-            this,
-            k -> k.startsWith(CAMEL_K_SOURCES_PREFIX),
-            CAMEL_K_PREFIX);
+                runtime.getCamelContext(),
+                this,
+                k -> k.startsWith(CAMEL_K_SOURCES_PREFIX),
+                CAMEL_K_PREFIX);
+
+        checkUniqueErrorHandler();
+        sortSources();
 
         if (ObjectHelper.isNotEmpty(this.getSources())) {
             SourcesSupport.loadSources(runtime, this.getSources());
         }
     }
 
+    private void checkUniqueErrorHandler() {
+        checkUniqueErrorHandler(this.sources);
+    }
+
+    static void checkUniqueErrorHandler(SourceDefinition[] sources) {
+        long errorHandlers = Arrays.stream(sources).filter(s -> s.getType() == 
SourceType.errorHandler).count();
+        if ( errorHandlers > 1) {
+            throw new IllegalArgumentException("Expected only one error 
handler source type, got " + errorHandlers);
+        }
+    }
+
+    private void sortSources() {
+        sortSources(this.getSources());
+    }
+
+    static void sortSources(SourceDefinition[] sources) {
+        // We must ensure the following source type order: errorHandler, 
source, template
+        Arrays.sort(sources,
+                (a, b) -> {
+                    if (a.getType() == null) {
+                        return SourceType.source.compareTo(b.getType());
+                    } else if (b.getType() == null) {
+                        return a.getType().compareTo(SourceType.source);
+                    } else return a.getType().compareTo(b.getType());
+                });
+    }
+
 }
diff --git 
a/camel-k-core/support/src/main/java/org/apache/camel/k/support/SourcesSupport.java
 
b/camel-k-core/support/src/main/java/org/apache/camel/k/support/SourcesSupport.java
index e393521..029b4d3 100644
--- 
a/camel-k-core/support/src/main/java/org/apache/camel/k/support/SourcesSupport.java
+++ 
b/camel-k-core/support/src/main/java/org/apache/camel/k/support/SourcesSupport.java
@@ -16,12 +16,10 @@
  */
 package org.apache.camel.k.support;
 
-import java.util.Collection;
-import java.util.List;
-
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.builder.ErrorHandlerBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.builder.RouteBuilderLifecycleStrategy;
 import org.apache.camel.k.Runtime;
@@ -37,6 +35,10 @@ import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.List;
+
 public final class SourcesSupport {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(SourcesConfigurer.class);
 
@@ -128,21 +130,64 @@ public final class SourcesSupport {
                     }
                 });
                 break;
+            case errorHandler:
+                if (!source.getInterceptors().isEmpty()) {
+                    LOGGER.warn("Interceptors associated to the route template 
{} will be ignored", source.getName());
+                }
+
+                interceptors = List.of(new RouteBuilderLifecycleStrategy() {
+                    @Override
+                    public void afterConfigure(RouteBuilder builder) {
+                        List<RouteDefinition> routes = 
builder.getRouteCollection().getRoutes();
+                        List<RouteTemplateDefinition> templates = 
builder.getRouteTemplateCollection().getRouteTemplates();
+
+                        if (routes.size() > 0) {
+                            throw new IllegalArgumentException("There should 
be no route definition, got " + routes.size());
+                        }
+                        if (!templates.isEmpty()) {
+                            throw new IllegalArgumentException("There should 
not be any template, got " + templates.size());
+                        }
+
+                        if (existErrorHandler(builder)) {
+                            LOGGER.debug("Setting default error handler 
builder factory as {}", builder.getErrorHandlerBuilder());
+                            
runtime.getCamelContext().adapt(ExtendedCamelContext.class).setErrorHandlerFactory(builder.getErrorHandlerBuilder());
+                        }
+                    }
+                });
+                break;
             default:
                 throw new IllegalArgumentException("Unknown source type: " + 
source.getType());
         }
 
         try {
             final Resource resource = 
Sources.asResource(runtime.getCamelContext(), source);
-            final ExtendedCamelContext ecc =  
runtime.getCamelContext(ExtendedCamelContext.class);
+            final ExtendedCamelContext ecc = 
runtime.getCamelContext(ExtendedCamelContext.class);
             final Collection<RoutesBuilder> builders = 
ecc.getRoutesLoader().findRoutesBuilders(resource);
 
             builders.stream()
-                .map(RouteBuilder.class::cast)
-                .peek(b -> interceptors.forEach(b::addLifecycleInterceptor))
-                .forEach(runtime::addRoutes);
+                    .map(RouteBuilder.class::cast)
+                    .peek(b -> 
interceptors.forEach(b::addLifecycleInterceptor))
+                    .forEach(runtime::addRoutes);
         } catch (Exception e) {
             throw RuntimeCamelException.wrapRuntimeCamelException(e);
         }
     }
+
+    static boolean existErrorHandler(RouteBuilder builder) {
+        //return builder.hasErrorHandlerBuilder();
+        // TODO We need to replace the following workaround with the statement 
above once we switch to camel-3.10.0 or above
+        try {
+            Field f = 
RouteBuilder.class.getSuperclass().getDeclaredField("errorHandlerBuilder");
+            f.setAccessible(true);
+            ErrorHandlerBuilder privateErrorHandlerBuilder = 
(ErrorHandlerBuilder) f.get(builder);
+            return privateErrorHandlerBuilder != null;
+        } catch (Exception e){
+            throw new IllegalArgumentException("Something went wrong while 
checking the error handler builder", e);
+        }
+    }
+
+    public static void loadErrorHandlerSource(Runtime runtime, 
SourceDefinition errorHandlerSourceDefinition) {
+        LOGGER.info("Loading error handler from: {}", 
errorHandlerSourceDefinition);
+        load(runtime, Sources.fromDefinition(errorHandlerSourceDefinition));
+    }
 }
diff --git 
a/camel-k-core/support/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
 
b/camel-k-core/support/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
index 9fc5078..f0c50b7 100644
--- 
a/camel-k-core/support/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
+++ 
b/camel-k-core/support/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
@@ -16,6 +16,5 @@
 #
 
 org.apache.camel.k.listener.ContextConfigurer
-org.apache.camel.k.listener.GlobalErrorHandlerConfigurer
 org.apache.camel.k.listener.SourcesConfigurer
 org.apache.camel.k.listener.PropertiesConfigurer
diff --git 
a/camel-k-core/support/src/test/java/org/apache/camel/k/listener/SourceConfigurerTest.java
 
b/camel-k-core/support/src/test/java/org/apache/camel/k/listener/SourceConfigurerTest.java
new file mode 100644
index 0000000..ebdd22c
--- /dev/null
+++ 
b/camel-k-core/support/src/test/java/org/apache/camel/k/listener/SourceConfigurerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.k.listener;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.k.support.PropertiesSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.k.test.CamelKTestSupport.asProperties;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class SourceConfigurerTest {
+    @Test
+    public void shouldLoadMultipleSources() {
+        CamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(asProperties(
+                "camel.k.sources[0].name", "sourceName0",
+                "camel.k.sources[0].location", "classpath:MyTemplate1.java",
+                "camel.k.sources[0].type", "template",
+                "camel.k.sources[1].name", "err1",
+                "camel.k.sources[1.location", "classpath:MyTemplate2.java",
+                "camel.k.sources[2].name", "err2",
+                "camel.k.sources[2].location", "classpath:Err2.java",
+                "camel.k.sources[2].type", "errorHandler"
+        ));
+
+        SourcesConfigurer configuration = new SourcesConfigurer();
+
+        PropertiesSupport.bindProperties(
+                context,
+                configuration,
+                k -> k.startsWith(SourcesConfigurer.CAMEL_K_SOURCES_PREFIX),
+                SourcesConfigurer.CAMEL_K_PREFIX);
+
+        assertThat(configuration.getSources().length).isEqualTo(3);
+    }
+
+    @Test
+    public void shouldFailOnMultipleErrorHandlers() {
+        CamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(asProperties(
+                "camel.k.sources[0].name", "sourceName0",
+                "camel.k.sources[0].location", "classpath:MyTemplate1.java",
+                "camel.k.sources[0].type", "template",
+                "camel.k.sources[1].name", "err1",
+                "camel.k.sources[1.location", "classpath:Err1.java",
+                "camel.k.sources[1].type", "errorHandler",
+                "camel.k.sources[2].name", "err2",
+                "camel.k.sources[2].location", "classpath:Err2.java",
+                "camel.k.sources[2].type", "errorHandler"
+        ));
+
+        SourcesConfigurer configuration = new SourcesConfigurer();
+
+        PropertiesSupport.bindProperties(
+                context,
+                configuration,
+                k -> k.startsWith(SourcesConfigurer.CAMEL_K_SOURCES_PREFIX),
+                SourcesConfigurer.CAMEL_K_PREFIX);
+
+        assertThat(configuration.getSources().length).isEqualTo(3);
+        Assertions.assertThrows(IllegalArgumentException.class, () -> {
+            
SourcesConfigurer.checkUniqueErrorHandler(configuration.getSources());
+        }, "java.lang.IllegalArgumentException: Expected only one error 
handler source type, got 2");
+    }
+
+    @Test
+    public void shouldOrderSourcesByType() {
+        CamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(asProperties(
+                "camel.k.sources[0].name", "template1",
+                "camel.k.sources[0].type", "template",
+                "camel.k.sources[1].name", "source1",
+                "camel.k.sources[2].name", "source2",
+                "camel.k.sources[2].type", "source",
+                "camel.k.sources[3].name", "errorHandler1",
+                "camel.k.sources[3].type", "errorHandler"
+        ));
+
+        SourcesConfigurer configuration = new SourcesConfigurer();
+
+        PropertiesSupport.bindProperties(
+                context,
+                configuration,
+                k -> k.startsWith(SourcesConfigurer.CAMEL_K_SOURCES_PREFIX),
+                SourcesConfigurer.CAMEL_K_PREFIX);
+        SourcesConfigurer.sortSources(configuration.getSources());
+
+        assertThat(configuration.getSources().length).isEqualTo(4);
+
+        
assertThat(configuration.getSources()[0].getName()).isEqualTo("errorHandler1");
+        // Order for the same type does not matter
+        assertThat(configuration.getSources()[1].getName()).contains("source");
+        assertThat(configuration.getSources()[2].getName()).contains("source");
+        
assertThat(configuration.getSources()[3].getName()).isEqualTo("template1");
+    }
+}
diff --git 
a/camel-k-core/support/src/test/java/org/apache/camel/k/support/GlobalErrorHandlerConfigurerTest.java
 
b/camel-k-core/support/src/test/java/org/apache/camel/k/support/GlobalErrorHandlerConfigurerTest.java
deleted file mode 100644
index fdb7446..0000000
--- 
a/camel-k-core/support/src/test/java/org/apache/camel/k/support/GlobalErrorHandlerConfigurerTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.k.support;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.impl.DefaultCamelContext;
-import org.apache.camel.k.listener.GlobalErrorHandlerConfigurer;
-import org.apache.camel.k.listener.SourcesConfigurer;
-import org.junit.jupiter.api.Test;
-
-import static org.apache.camel.k.test.CamelKTestSupport.asProperties;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class GlobalErrorHandlerConfigurerTest {
-
-    @Test
-    public void globalErrorHandlerIsBoundToSourcesConfigurer() {
-        CamelContext context = new DefaultCamelContext();
-        context.getPropertiesComponent().setInitialProperties(asProperties(
-                "camel.k.global-error-handler", "someUriError"
-        ));
-
-        GlobalErrorHandlerConfigurer configuration = new 
GlobalErrorHandlerConfigurer();
-
-        PropertiesSupport.bindProperties(
-                context,
-                configuration,
-                k -> 
k.startsWith(GlobalErrorHandlerConfigurer.CAMEL_K_GLOBAL_ERROR_HANDLER_PREFIX),
-                SourcesConfigurer.CAMEL_K_PREFIX);
-
-        
assertThat(configuration.getGlobalErrorHandler()).isEqualTo("someUriError");
-    }
-}

Reply via email to