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 2020/10/08 04:46:53 UTC

[camel] branch master updated: Improve BaseMainSupport (#4388)

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

davsclaus 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 ef510b0  Improve BaseMainSupport (#4388)
ef510b0 is described below

commit ef510b0d3781ec8acd92ff35d316720be6a276d3
Author: Luca Burgazzoli <lb...@users.noreply.github.com>
AuthorDate: Thu Oct 8 06:46:41 2020 +0200

    Improve BaseMainSupport (#4388)
    
    * main: move ProducerTemplate handling from BaseMainSupport to MainSupport
    
    * main: add support for setting initial/override properties from Map
    
    * main: move createCamelContext from BaseMainSupport to MainSupport
    
    * main: move static utility methods to MainHelper to declutter BaseMainSupport
---
 .../org/apache/camel/main/BaseMainSupport.java     | 283 ++-------------------
 .../java/org/apache/camel/main/MainHelper.java     | 218 ++++++++++++++++
 .../java/org/apache/camel/main/MainSupport.java    |  32 ++-
 .../org/apache/camel/main/PropertyOptionKey.java   |  57 +++++
 .../camel/main/MainPropertyPlaceholderTest.java    |  20 ++
 5 files changed, 346 insertions(+), 264 deletions(-)

diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index ffc5350..dff69bd 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -34,15 +34,12 @@ import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Component;
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.NoSuchLanguageException;
-import org.apache.camel.ProducerTemplate;
-import org.apache.camel.PropertyBindingException;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.builder.ThreadPoolProfileBuilder;
@@ -61,8 +58,6 @@ import org.apache.camel.spi.CamelBeanPostProcessor;
 import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.Language;
 import org.apache.camel.spi.PropertiesComponent;
-import org.apache.camel.spi.PropertyConfigurer;
-import org.apache.camel.spi.PropertyConfigurerGetter;
 import org.apache.camel.spi.RouteTemplateParameterSource;
 import org.apache.camel.spi.ThreadPoolProfile;
 import org.apache.camel.support.CamelContextHelper;
@@ -70,7 +65,6 @@ import org.apache.camel.support.LifecycleStrategySupport;
 import org.apache.camel.support.PropertyBindingSupport;
 import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.support.service.BaseService;
-import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
@@ -80,8 +74,12 @@ import org.apache.camel.util.StringHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.camel.main.MainHelper.computeProperties;
 import static org.apache.camel.main.MainHelper.loadEnvironmentVariablesAsProperties;
 import static org.apache.camel.main.MainHelper.lookupPropertyFromSysOrEnv;
+import static org.apache.camel.main.MainHelper.optionKey;
+import static org.apache.camel.main.MainHelper.setPropertiesOnTarget;
+import static org.apache.camel.main.MainHelper.validateOptionAndValue;
 import static org.apache.camel.support.ObjectHelper.invokeMethod;
 import static org.apache.camel.util.ReflectionHelper.findMethod;
 import static org.apache.camel.util.StringHelper.matches;
@@ -101,7 +99,6 @@ public abstract class BaseMainSupport extends BaseService {
             = "passphrase|password|secretkey|accesstoken|clientsecret|authorizationtoken|sasljaasconfig";
 
     protected volatile CamelContext camelContext;
-    protected volatile ProducerTemplate camelTemplate;
 
     protected final List<MainListener> listeners = new ArrayList<>();
     protected final MainConfigurationProperties mainConfigurationProperties = new MainConfigurationProperties();
@@ -112,127 +109,11 @@ public abstract class BaseMainSupport extends BaseService {
     protected Properties initialProperties;
     protected Properties overrideProperties;
 
-    protected static String optionKey(String key) {
-        // as we ignore case for property names we should use keys in same case and without dashes
-        key = StringHelper.dashToCamelCase(key);
-        return key;
+    protected BaseMainSupport() {
     }
 
-    protected static boolean setPropertiesOnTarget(CamelContext context, Object target, Object source) throws Exception {
-        ObjectHelper.notNull(context, "context");
-        ObjectHelper.notNull(target, "target");
-
-        boolean rc = false;
-
-        PropertyConfigurer targetConfigurer = null;
-        if (target instanceof Component) {
-            // the component needs to be initialized to have the configurer ready
-            ServiceHelper.initService(target);
-            targetConfigurer = ((Component) target).getComponentPropertyConfigurer();
-        }
-        if (targetConfigurer == null) {
-            String name = target.getClass().getName();
-            // see if there is a configurer for it
-            targetConfigurer = context.adapt(ExtendedCamelContext.class)
-                    .getConfigurerResolver().resolvePropertyConfigurer(name, context);
-        }
-
-        PropertyConfigurer sourceConfigurer = null;
-        if (source instanceof Component) {
-            // the component needs to be initialized to have the configurer ready
-            ServiceHelper.initService(source);
-            sourceConfigurer = ((Component) source).getComponentPropertyConfigurer();
-        }
-        if (sourceConfigurer == null) {
-            String name = source.getClass().getName();
-            // see if there is a configurer for it
-            sourceConfigurer = context.adapt(ExtendedCamelContext.class)
-                    .getConfigurerResolver().resolvePropertyConfigurer(name, context);
-        }
-
-        if (targetConfigurer != null && sourceConfigurer instanceof PropertyConfigurerGetter) {
-            PropertyConfigurerGetter getter = (PropertyConfigurerGetter) sourceConfigurer;
-            for (String key : getter.getAllOptions(source).keySet()) {
-                Object value = getter.getOptionValue(source, key, true);
-                if (value != null) {
-                    rc |= targetConfigurer.configure(context, target, key, value, true);
-                }
-            }
-        }
-        return rc;
-    }
-
-    protected static boolean setPropertiesOnTarget(
-            CamelContext context, Object target, Map<String, Object> properties,
-            String optionPrefix, boolean failIfNotSet, boolean ignoreCase,
-            Map<String, String> autoConfiguredProperties)
-            throws Exception {
-        ObjectHelper.notNull(context, "context");
-        ObjectHelper.notNull(target, "target");
-        ObjectHelper.notNull(properties, "properties");
-
-        boolean rc = false;
-        PropertyConfigurer configurer = null;
-        if (target instanceof Component) {
-            // the component needs to be initialized to have the configurer ready
-            ServiceHelper.initService(target);
-            configurer = ((Component) target).getComponentPropertyConfigurer();
-        }
-
-        if (configurer == null) {
-            String name = target.getClass().getName();
-            // see if there is a configurer for it
-            configurer = context.adapt(ExtendedCamelContext.class)
-                    .getConfigurerResolver().resolvePropertyConfigurer(name, context);
-        }
-
-        try {
-            // keep a reference of the original keys
-            Map<String, Object> backup = new LinkedHashMap<>(properties);
-
-            rc = PropertyBindingSupport.build()
-                    .withMandatory(failIfNotSet)
-                    .withRemoveParameters(true)
-                    .withConfigurer(configurer)
-                    .withIgnoreCase(ignoreCase)
-                    .bind(context, target, properties);
-
-            for (Map.Entry<String, Object> entry : backup.entrySet()) {
-                if (entry.getValue() != null && !properties.containsKey(entry.getKey())) {
-                    String prefix = optionPrefix;
-                    if (prefix != null && !prefix.endsWith(".")) {
-                        prefix = "." + prefix;
-                    }
-
-                    LOG.debug("Configured property: {}{}={} on bean: {}", prefix, entry.getKey(), entry.getValue(), target);
-                    autoConfiguredProperties.put(prefix + entry.getKey(), entry.getValue().toString());
-                }
-            }
-        } catch (PropertyBindingException e) {
-            String key = e.getOptionKey();
-            if (key == null) {
-                String prefix = e.getOptionPrefix();
-                if (prefix != null && !prefix.endsWith(".")) {
-                    prefix = "." + prefix;
-                }
-
-                key = prefix != null
-                        ? prefix + "." + e.getPropertyName()
-                        : e.getPropertyName();
-            }
-
-            if (failIfNotSet) {
-                // enrich the error with more precise details with option prefix and key
-                throw new PropertyBindingException(
-                        e.getTarget(), e.getPropertyName(), e.getValue(), optionPrefix, key, e.getCause());
-            } else {
-                LOG.debug("Error configuring property (" + key + ") with name: " + e.getPropertyName() + ") on bean: " + target
-                          + " with value: " + e.getValue() + ". This exception is ignored as failIfNotSet=false.",
-                        e);
-            }
-        }
-
-        return rc;
+    protected BaseMainSupport(CamelContext camelContext) {
+        this.camelContext = camelContext;
     }
 
     /**
@@ -297,7 +178,7 @@ public abstract class BaseMainSupport extends BaseService {
      * {@link org.apache.camel.BindToRegistry} annotation style.
      * <p/>
      * This option is default enabled.
-     * 
+     *
      * @deprecated use {@link #configure()}
      */
     @Deprecated
@@ -317,6 +198,14 @@ public abstract class BaseMainSupport extends BaseService {
     }
 
     /**
+     * Sets initial properties for the properties component, which will be used before any locations are resolved.
+     */
+    public void setInitialProperties(Map<String, Object> initialProperties) {
+        this.initialProperties = new OrderedProperties();
+        this.initialProperties.putAll(initialProperties);
+    }
+
+    /**
      * Adds a property (initial) for the properties component, which will be used before any locations are resolved.
      *
      * @param key   the property key
@@ -354,6 +243,14 @@ public abstract class BaseMainSupport extends BaseService {
     }
 
     /**
+     * Sets a special list of override properties that take precedence and will use first, if a property exist.
+     */
+    public void setOverrideProperties(Map<String, Object> initialProperties) {
+        this.overrideProperties = new OrderedProperties();
+        this.overrideProperties.putAll(initialProperties);
+    }
+
+    /**
      * Adds an override property that take precedence and will use first, if a property exist.
      *
      * @param key   the property key
@@ -396,25 +293,6 @@ public abstract class BaseMainSupport extends BaseService {
         return answer;
     }
 
-    public ProducerTemplate getCamelTemplate() throws Exception {
-        if (camelTemplate == null) {
-            camelTemplate = findOrCreateCamelTemplate();
-        }
-        return camelTemplate;
-    }
-
-    protected abstract ProducerTemplate findOrCreateCamelTemplate();
-
-    protected abstract CamelContext createCamelContext();
-
-    protected void initCamelContext() throws Exception {
-        camelContext = createCamelContext();
-        if (camelContext == null) {
-            throw new IllegalStateException("Created CamelContext is null");
-        }
-        postProcessCamelContext(camelContext);
-    }
-
     protected void loadRouteBuilders(CamelContext camelContext) throws Exception {
         // lets use Camel's bean post processor on any existing route builder classes
         // so the instance has some support for dependency injection
@@ -1500,117 +1378,4 @@ public abstract class BaseMainSupport extends BaseService {
         }
     }
 
-    protected static void validateOptionAndValue(String key, String option, String value) {
-        if (ObjectHelper.isEmpty(option)) {
-            throw new IllegalArgumentException("Error configuring property: " + key + " because option is empty");
-        }
-        if (ObjectHelper.isEmpty(value)) {
-            throw new IllegalArgumentException("Error configuring property: " + key + " because value is empty");
-        }
-    }
-
-    private static final class PropertyOptionKey {
-        private final Object instance;
-        private final String optionPrefix;
-
-        private PropertyOptionKey(Object instance, String optionPrefix) {
-            this.instance = ObjectHelper.notNull(instance, "instance");
-            this.optionPrefix = ObjectHelper.notNull(optionPrefix, "optionPrefix");
-        }
-
-        public Object getInstance() {
-            return instance;
-        }
-
-        public String getOptionPrefix() {
-            return optionPrefix;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (!(o instanceof PropertyOptionKey)) {
-                return false;
-            }
-            PropertyOptionKey key = (PropertyOptionKey) o;
-            return Objects.equals(instance, key.instance)
-                    && Objects.equals(optionPrefix, key.optionPrefix);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(instance, optionPrefix);
-        }
-    }
-
-    protected static void computeProperties(
-            String keyPrefix, String key, Properties prop, Map<PropertyOptionKey, Map<String, Object>> properties,
-            Function<String, Iterable<Object>> supplier) {
-        if (key.startsWith(keyPrefix)) {
-            // grab name
-            final int dot = key.indexOf('.', keyPrefix.length());
-            final String name = dot == -1 ? key.substring(keyPrefix.length()) : key.substring(keyPrefix.length(), dot);
-
-            // enabled is a virtual property
-            if ("enabled".equals(name)) {
-                return;
-            }
-            // skip properties as its already keyPrefix earlier
-            if ("properties".equals(name)) {
-                return;
-            }
-
-            // determine if the service is enabled or not by taking into account two options:
-            //
-            //   1. ${keyPrefix}.enabled = true|false
-            //   2. ${keyPrefix}.${name}.enabled = true|false
-            //
-            // The option [2] has the higher priority so as example:
-            //
-            //   camel.component.enabled = false
-            //   camel.component.seda.enabled = true
-            //
-            // enables auto configuration of the seda component only
-            if (!isServiceEnabled(keyPrefix, name, prop)) {
-                return;
-            }
-
-            String prefix = dot == -1 ? "" : key.substring(0, dot + 1);
-            String option = dot == -1 ? "" : key.substring(dot + 1);
-            String value = prop.getProperty(key, "");
-
-            // enabled is a virtual property
-            if ("enabled".equalsIgnoreCase(option)) {
-                return;
-            }
-
-            validateOptionAndValue(key, option, value);
-
-            Iterable<Object> targets = supplier.apply(name);
-            for (Object target : targets) {
-                PropertyOptionKey pok = new PropertyOptionKey(target, prefix);
-                Map<String, Object> values = properties.computeIfAbsent(pok, k -> new LinkedHashMap<>());
-
-                // we ignore case for property keys (so we should store them in canonical style
-                values.put(optionKey(option), value);
-            }
-        }
-    }
-
-    protected static boolean isServiceEnabled(String prefix, String name, Properties properties) {
-        ObjectHelper.notNull(prefix, "prefix");
-        ObjectHelper.notNull(name, "name");
-        ObjectHelper.notNull(properties, "properties");
-
-        if (!prefix.endsWith(".")) {
-            prefix = prefix + ".";
-        }
-
-        final String group = properties.getProperty(prefix + "enabled", "true");
-        final String item = properties.getProperty(prefix + name + ".enabled", group);
-
-        return Boolean.parseBoolean(item);
-    }
 }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainHelper.java b/core/camel-main/src/main/java/org/apache/camel/main/MainHelper.java
index a8ff18e..7c1fe8f 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/MainHelper.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainHelper.java
@@ -16,13 +16,30 @@
  */
 package org.apache.camel.main;
 
+import java.util.LinkedHashMap;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Properties;
+import java.util.function.Function;
 
+import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.PropertyBindingException;
+import org.apache.camel.spi.PropertyConfigurer;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.service.ServiceHelper;
+import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.OrderedProperties;
+import org.apache.camel.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public final class MainHelper {
+    private static final Logger LOG = LoggerFactory.getLogger(MainHelper.class);
+
     private MainHelper() {
     }
 
@@ -60,4 +77,205 @@ public final class MainHelper {
         return answer;
     }
 
+    public static String optionKey(String key) {
+        // as we ignore case for property names we should use keys in same case and without dashes
+        key = StringHelper.dashToCamelCase(key);
+        return key;
+    }
+
+    public static boolean setPropertiesOnTarget(CamelContext context, Object target, Object source) throws Exception {
+        ObjectHelper.notNull(context, "context");
+        ObjectHelper.notNull(target, "target");
+
+        boolean rc = false;
+
+        PropertyConfigurer targetConfigurer = null;
+        if (target instanceof Component) {
+            // the component needs to be initialized to have the configurer ready
+            ServiceHelper.initService(target);
+            targetConfigurer = ((Component) target).getComponentPropertyConfigurer();
+        }
+        if (targetConfigurer == null) {
+            String name = target.getClass().getName();
+            // see if there is a configurer for it
+            targetConfigurer = context.adapt(ExtendedCamelContext.class)
+                    .getConfigurerResolver().resolvePropertyConfigurer(name, context);
+        }
+
+        PropertyConfigurer sourceConfigurer = null;
+        if (source instanceof Component) {
+            // the component needs to be initialized to have the configurer ready
+            ServiceHelper.initService(source);
+            sourceConfigurer = ((Component) source).getComponentPropertyConfigurer();
+        }
+        if (sourceConfigurer == null) {
+            String name = source.getClass().getName();
+            // see if there is a configurer for it
+            sourceConfigurer = context.adapt(ExtendedCamelContext.class)
+                    .getConfigurerResolver().resolvePropertyConfigurer(name, context);
+        }
+
+        if (targetConfigurer != null && sourceConfigurer instanceof PropertyConfigurerGetter) {
+            PropertyConfigurerGetter getter = (PropertyConfigurerGetter) sourceConfigurer;
+            for (String key : getter.getAllOptions(source).keySet()) {
+                Object value = getter.getOptionValue(source, key, true);
+                if (value != null) {
+                    rc |= targetConfigurer.configure(context, target, key, value, true);
+                }
+            }
+        }
+        return rc;
+    }
+
+    public static boolean setPropertiesOnTarget(
+            CamelContext context, Object target, Map<String, Object> properties,
+            String optionPrefix, boolean failIfNotSet, boolean ignoreCase,
+            Map<String, String> autoConfiguredProperties) {
+
+        ObjectHelper.notNull(context, "context");
+        ObjectHelper.notNull(target, "target");
+        ObjectHelper.notNull(properties, "properties");
+
+        boolean rc = false;
+        PropertyConfigurer configurer = null;
+        if (target instanceof Component) {
+            // the component needs to be initialized to have the configurer ready
+            ServiceHelper.initService(target);
+            configurer = ((Component) target).getComponentPropertyConfigurer();
+        }
+
+        if (configurer == null) {
+            String name = target.getClass().getName();
+            // see if there is a configurer for it
+            configurer = context.adapt(ExtendedCamelContext.class)
+                    .getConfigurerResolver().resolvePropertyConfigurer(name, context);
+        }
+
+        try {
+            // keep a reference of the original keys
+            Map<String, Object> backup = new LinkedHashMap<>(properties);
+
+            rc = PropertyBindingSupport.build()
+                    .withMandatory(failIfNotSet)
+                    .withRemoveParameters(true)
+                    .withConfigurer(configurer)
+                    .withIgnoreCase(ignoreCase)
+                    .bind(context, target, properties);
+
+            for (Map.Entry<String, Object> entry : backup.entrySet()) {
+                if (entry.getValue() != null && !properties.containsKey(entry.getKey())) {
+                    String prefix = optionPrefix;
+                    if (prefix != null && !prefix.endsWith(".")) {
+                        prefix = "." + prefix;
+                    }
+
+                    LOG.debug("Configured property: {}{}={} on bean: {}", prefix, entry.getKey(), entry.getValue(), target);
+                    autoConfiguredProperties.put(prefix + entry.getKey(), entry.getValue().toString());
+                }
+            }
+        } catch (PropertyBindingException e) {
+            String key = e.getOptionKey();
+            if (key == null) {
+                String prefix = e.getOptionPrefix();
+                if (prefix != null && !prefix.endsWith(".")) {
+                    prefix = "." + prefix;
+                }
+
+                key = prefix != null
+                        ? prefix + "." + e.getPropertyName()
+                        : e.getPropertyName();
+            }
+
+            if (failIfNotSet) {
+                // enrich the error with more precise details with option prefix and key
+                throw new PropertyBindingException(
+                        e.getTarget(), e.getPropertyName(), e.getValue(), optionPrefix, key, e.getCause());
+            } else {
+                LOG.debug("Error configuring property (" + key + ") with name: " + e.getPropertyName() + ") on bean: " + target
+                          + " with value: " + e.getValue() + ". This exception is ignored as failIfNotSet=false.",
+                        e);
+            }
+        }
+
+        return rc;
+    }
+
+    public static void computeProperties(
+            String keyPrefix, String key, Properties prop, Map<PropertyOptionKey, Map<String, Object>> properties,
+            Function<String, Iterable<Object>> supplier) {
+        if (key.startsWith(keyPrefix)) {
+            // grab name
+            final int dot = key.indexOf('.', keyPrefix.length());
+            final String name = dot == -1 ? key.substring(keyPrefix.length()) : key.substring(keyPrefix.length(), dot);
+
+            // enabled is a virtual property
+            if ("enabled".equals(name)) {
+                return;
+            }
+            // skip properties as its already keyPrefix earlier
+            if ("properties".equals(name)) {
+                return;
+            }
+
+            // determine if the service is enabled or not by taking into account two options:
+            //
+            //   1. ${keyPrefix}.enabled = true|false
+            //   2. ${keyPrefix}.${name}.enabled = true|false
+            //
+            // The option [2] has the higher priority so as example:
+            //
+            //   camel.component.enabled = false
+            //   camel.component.seda.enabled = true
+            //
+            // enables auto configuration of the seda component only
+            if (!isServiceEnabled(keyPrefix, name, prop)) {
+                return;
+            }
+
+            String prefix = dot == -1 ? "" : key.substring(0, dot + 1);
+            String option = dot == -1 ? "" : key.substring(dot + 1);
+            String value = prop.getProperty(key, "");
+
+            // enabled is a virtual property
+            if ("enabled".equalsIgnoreCase(option)) {
+                return;
+            }
+
+            validateOptionAndValue(key, option, value);
+
+            Iterable<Object> targets = supplier.apply(name);
+            for (Object target : targets) {
+                PropertyOptionKey pok = new PropertyOptionKey(target, prefix);
+                Map<String, Object> values = properties.computeIfAbsent(pok, k -> new LinkedHashMap<>());
+
+                // we ignore case for property keys (so we should store them in canonical style
+                values.put(optionKey(option), value);
+            }
+        }
+    }
+
+    public static boolean isServiceEnabled(String prefix, String name, Properties properties) {
+        ObjectHelper.notNull(prefix, "prefix");
+        ObjectHelper.notNull(name, "name");
+        ObjectHelper.notNull(properties, "properties");
+
+        if (!prefix.endsWith(".")) {
+            prefix = prefix + ".";
+        }
+
+        final String group = properties.getProperty(prefix + "enabled", "true");
+        final String item = properties.getProperty(prefix + name + ".enabled", group);
+
+        return Boolean.parseBoolean(item);
+    }
+
+    public static void validateOptionAndValue(String key, String option, String value) {
+        if (ObjectHelper.isEmpty(option)) {
+            throw new IllegalArgumentException("Error configuring property: " + key + " because option is empty");
+        }
+        if (ObjectHelper.isEmpty(value)) {
+            throw new IllegalArgumentException("Error configuring property: " + key + " because value is empty");
+        }
+    }
+
 }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/MainSupport.java
index 1dbe651..3176f8d 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/MainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainSupport.java
@@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.spi.EventNotifier;
 import org.apache.camel.support.service.ServiceHelper;
 import org.slf4j.Logger;
@@ -38,6 +39,8 @@ public abstract class MainSupport extends BaseMainSupport {
     protected final AtomicInteger exitCode = new AtomicInteger(UNINITIALIZED_EXIT_CODE);
     protected MainShutdownStrategy shutdownStrategy;
 
+    protected volatile ProducerTemplate camelTemplate;
+
     protected MainSupport(Class<?>... configurationClasses) {
         this();
         configure().addConfigurationClass(configurationClasses);
@@ -152,7 +155,7 @@ public abstract class MainSupport extends BaseMainSupport {
     /**
      * Sets the duration (in seconds) to run the application until it should be terminated. Defaults to -1. Any value <=
      * 0 will run forever.
-     * 
+     *
      * @deprecated use {@link #configure()}
      */
     @Deprecated
@@ -169,7 +172,7 @@ public abstract class MainSupport extends BaseMainSupport {
      * Sets the maximum idle duration (in seconds) when running the application, and if there has been no message
      * processed after being idle for more than this duration then the application should be terminated. Defaults to -1.
      * Any value <= 0 will run forever.
-     * 
+     *
      * @deprecated use {@link #configure()}
      */
     @Deprecated
@@ -185,7 +188,7 @@ public abstract class MainSupport extends BaseMainSupport {
     /**
      * Sets the duration to run the application to process at most max messages until it should be terminated. Defaults
      * to -1. Any value <= 0 will run forever.
-     * 
+     *
      * @deprecated use {@link #configure()}
      */
     @Deprecated
@@ -195,7 +198,7 @@ public abstract class MainSupport extends BaseMainSupport {
 
     /**
      * Sets the exit code for the application if duration was hit
-     * 
+     *
      * @deprecated use {@link #configure()}
      */
     @Deprecated
@@ -227,7 +230,7 @@ public abstract class MainSupport extends BaseMainSupport {
     /**
      * Set the {@link MainShutdownStrategy} used to properly shut-down the main instance. By default a
      * {@link DefaultMainShutdownStrategy} will be used.
-     * 
+     *
      * @param shutdownStrategy the shutdown strategy
      */
     public void setShutdownStrategy(MainShutdownStrategy shutdownStrategy) {
@@ -297,4 +300,23 @@ public abstract class MainSupport extends BaseMainSupport {
             }
         }
     }
+
+    protected abstract ProducerTemplate findOrCreateCamelTemplate();
+
+    protected abstract CamelContext createCamelContext();
+
+    public ProducerTemplate getCamelTemplate() throws Exception {
+        if (camelTemplate == null) {
+            camelTemplate = findOrCreateCamelTemplate();
+        }
+        return camelTemplate;
+    }
+
+    protected void initCamelContext() throws Exception {
+        camelContext = createCamelContext();
+        if (camelContext == null) {
+            throw new IllegalStateException("Created CamelContext is null");
+        }
+        postProcessCamelContext(camelContext);
+    }
 }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/PropertyOptionKey.java b/core/camel-main/src/main/java/org/apache/camel/main/PropertyOptionKey.java
new file mode 100644
index 0000000..c732c00
--- /dev/null
+++ b/core/camel-main/src/main/java/org/apache/camel/main/PropertyOptionKey.java
@@ -0,0 +1,57 @@
+/*
+ * 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.main;
+
+import java.util.Objects;
+
+import org.apache.camel.util.ObjectHelper;
+
+final class PropertyOptionKey {
+    private final Object instance;
+    private final String optionPrefix;
+
+    PropertyOptionKey(Object instance, String optionPrefix) {
+        this.instance = ObjectHelper.notNull(instance, "instance");
+        this.optionPrefix = ObjectHelper.notNull(optionPrefix, "optionPrefix");
+    }
+
+    public Object getInstance() {
+        return instance;
+    }
+
+    public String getOptionPrefix() {
+        return optionPrefix;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof PropertyOptionKey)) {
+            return false;
+        }
+        PropertyOptionKey key = (PropertyOptionKey) o;
+        return Objects.equals(instance, key.instance)
+                && Objects.equals(optionPrefix, key.optionPrefix);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(instance, optionPrefix);
+    }
+}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainPropertyPlaceholderTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainPropertyPlaceholderTest.java
index a3c9c7b..41e3d56 100644
--- a/core/camel-main/src/test/java/org/apache/camel/main/MainPropertyPlaceholderTest.java
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainPropertyPlaceholderTest.java
@@ -18,6 +18,7 @@ package org.apache.camel.main;
 
 import org.junit.jupiter.api.Test;
 
+import static org.apache.camel.util.CollectionHelper.mapOf;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.fail;
 
@@ -59,4 +60,23 @@ public class MainPropertyPlaceholderTest {
             main.stop();
         }
     }
+
+    @Test
+    public void testPropertiesWithMap() {
+        Main main = new Main();
+        try {
+            main.setInitialProperties(
+                    mapOf("1", "val-init", "2", "val-init"));
+            main.setOverrideProperties(
+                    mapOf("1", "val-override", "3", "val-override"));
+
+            main.start();
+
+            assertEquals("val-override", main.getCamelContext().resolvePropertyPlaceholders("{{1}}"));
+            assertEquals("val-init", main.getCamelContext().resolvePropertyPlaceholders("{{2}}"));
+            assertEquals("val-override", main.getCamelContext().resolvePropertyPlaceholders("{{3}}"));
+        } finally {
+            main.stop();
+        }
+    }
 }