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/04 15:03:58 UTC

[camel] branch master updated (11a2c88 -> 3267b2f)

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

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


    from 11a2c88  Sync properties
     new 493e93d  CAMEL-14672: Invoke customizers as part of services initialization
     new d96c2c8  CAMEL-14672: cleanup component resolution
     new 3267b2f  CAMEL-14672: add migration instruction

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/camel/spi/ComponentCustomizer.java  | 209 +++++-
 .../org/apache/camel/spi/DataFormatCustomizer.java | 213 +++++-
 .../org/apache/camel/spi/DataFormatResolver.java   |  10 -
 .../org/apache/camel/spi/LanguageCustomizer.java   | 213 +++++-
 .../org/apache/camel/spi/LifecycleStrategy.java    |  18 +
 .../camel/impl/engine/AbstractCamelContext.java    |  94 ++-
 .../impl/engine/CustomizersLifecycleStrategy.java  |  87 +++
 .../impl/engine/DefaultComponentResolver.java      |   7 -
 .../impl/engine/DefaultDataFormatResolver.java     |  14 -
 .../camel/impl/engine/DefaultLanguageResolver.java |   7 -
 .../org/apache/camel/support/CustomizersTest.java  | 457 ++++++++++++
 .../org/apache/camel/main/MainCustomizerTest.java  |  99 +++
 .../apache/camel/support/CustomizersSupport.java   | 129 ++++
 .../camel/support/PropertyBindingSupport.java      | 787 ++++++++++-----------
 .../camel/support/PropertyConfigurerHelper.java    |  52 +-
 .../org/apache/camel/util/CollectionHelper.java    |   1 +
 .../ROOT/pages/camel-3x-upgrade-guide-3_6.adoc     |  86 +++
 17 files changed, 1992 insertions(+), 491 deletions(-)
 create mode 100644 core/camel-base/src/main/java/org/apache/camel/impl/engine/CustomizersLifecycleStrategy.java
 create mode 100644 core/camel-core/src/test/java/org/apache/camel/support/CustomizersTest.java
 create mode 100644 core/camel-main/src/test/java/org/apache/camel/main/MainCustomizerTest.java
 create mode 100644 core/camel-support/src/main/java/org/apache/camel/support/CustomizersSupport.java


[camel] 01/03: CAMEL-14672: Invoke customizers as part of services initialization

Posted by lb...@apache.org.
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

commit 493e93d44ba82d201a3a75e095b43a547b86c271
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Tue Sep 1 19:40:57 2020 +0200

    CAMEL-14672: Invoke customizers as part of services initialization
---
 .../org/apache/camel/spi/ComponentCustomizer.java  | 209 +++++-
 .../org/apache/camel/spi/DataFormatCustomizer.java | 213 +++++-
 .../org/apache/camel/spi/DataFormatResolver.java   |  10 -
 .../org/apache/camel/spi/LanguageCustomizer.java   | 213 +++++-
 .../org/apache/camel/spi/LifecycleStrategy.java    |  18 +
 .../camel/impl/engine/AbstractCamelContext.java    |  76 +-
 .../impl/engine/CustomizersLifecycleStrategy.java  |  87 +++
 .../impl/engine/DefaultDataFormatResolver.java     |  14 -
 .../camel/impl/engine/DefaultLanguageResolver.java |   7 -
 .../org/apache/camel/support/CustomizersTest.java  | 457 ++++++++++++
 .../org/apache/camel/main/MainCustomizerTest.java  |  99 +++
 .../apache/camel/support/CustomizersSupport.java   | 129 ++++
 .../camel/support/PropertyBindingSupport.java      | 787 ++++++++++-----------
 .../camel/support/PropertyConfigurerHelper.java    |  52 +-
 .../org/apache/camel/util/CollectionHelper.java    |   1 +
 15 files changed, 1901 insertions(+), 471 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ComponentCustomizer.java b/core/camel-api/src/main/java/org/apache/camel/spi/ComponentCustomizer.java
index 59dc6c7..2a2cf28 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/ComponentCustomizer.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/ComponentCustomizer.java
@@ -16,14 +16,217 @@
  */
 package org.apache.camel.spi;
 
+import java.util.function.BiPredicate;
+
 import org.apache.camel.Component;
+import org.apache.camel.Ordered;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.function.ThrowingBiConsumer;
+import org.apache.camel.util.function.ThrowingConsumer;
 
+/**
+ * To apply custom configurations to {@link Component} instances.
+ */
 @FunctionalInterface
-public interface ComponentCustomizer<T extends Component> {
+public interface ComponentCustomizer extends Ordered {
+    /**
+     * Create a generic {@link Builder}.
+     *
+     * @return the {@link Builder}
+     */
+    static Builder<Component> builder() {
+        return builder(Component.class);
+    }
+
+    /**
+     * Create a typed {@link Builder} that can process a concrete component type instance.
+     *
+     * @param  type the concrete type of the {@link Component}
+     * @return      the {@link Builder}
+     */
+    static <T extends Component> Builder<T> builder(Class<T> type) {
+        return new Builder<>(type);
+    }
+
+    /**
+     * Create a {@link ComponentCustomizer} that can process a concrete component type instance.
+     *
+     * @param  type     the concrete type of the {@link Component}
+     * @param  consumer the {@link Component} configuration logic
+     * @return          the {@link ComponentCustomizer}
+     */
+    static <T extends Component> ComponentCustomizer forType(Class<T> type, ThrowingConsumer<T, Exception> consumer) {
+        return builder(type).build(consumer);
+    }
+
     /**
      * Customize the specified {@link Component}.
      *
-     * @param component the component to customize
+     * @param name   the unique name of the component
+     * @param target the component to configure
+     */
+    void configure(String name, Component target);
+
+    /**
+     * Checks whether this customizer should be applied to the given {@link Component}.
+     *
+     * @param  name   the unique name of the component
+     * @param  target the component to configure
+     * @return        <tt>true</tt> if the customizer should be applied
+     */
+    default boolean isEnabled(String name, Component target) {
+        return true;
+    }
+
+    @Override
+    default int getOrder() {
+        return 0;
+    }
+
+    // ***************************************
+    //
+    // Filter
+    //
+    // ***************************************
+
+    /**
+     * Used as additional filer mechanism to control if customizers need to be applied or not. Each of this filter is
+     * applied before any of the discovered {@link ComponentCustomizer} is invoked.
+     * </p>
+     * This interface is useful to implement enable/disable logic on a group of customizers.
+     */
+    @FunctionalInterface
+    interface Policy extends BiPredicate<String, Component> {
+        /**
+         * A simple deny-all policy.
+         *
+         * @return false
+         */
+        static Policy none() {
+            return new Policy() {
+                @Override
+                public boolean test(String s, Component target) {
+                    return false;
+                }
+            };
+        }
+
+        /**
+         * A simple allow-all policy.
+         *
+         * @return true
+         */
+        static Policy any() {
+            return new Policy() {
+                @Override
+                public boolean test(String s, Component target) {
+                    return true;
+                }
+            };
+        }
+    }
+
+    // ***************************************
+    //
+    // Builders
+    //
+    // ***************************************
+
+    /**
+     * A fluent builder to create a {@link ComponentCustomizer} instance.
+     *
+     * @param <T> the concrete type of the {@link Component}.
      */
-    void customize(T component);
+    class Builder<T extends Component> {
+        private final Class<T> type;
+        private BiPredicate<String, Component> condition;
+        private int order;
+
+        public Builder(Class<T> type) {
+            this.type = type;
+        }
+
+        public Builder<T> withOrder(int order) {
+            this.order = order;
+
+            return this;
+        }
+
+        public Builder<T> withCondition(BiPredicate<String, Component> condition) {
+            this.condition = condition;
+
+            return this;
+        }
+
+        public ComponentCustomizer build(ThrowingConsumer<T, Exception> consumer) {
+            return build(new ThrowingBiConsumer<String, T, Exception>() {
+                @Override
+                public void accept(String name, T target) throws Exception {
+                    consumer.accept(target);
+                }
+            });
+        }
+
+        public ComponentCustomizer build(ThrowingBiConsumer<String, T, Exception> consumer) {
+            final int order = this.order;
+            final BiPredicate<String, Component> condition = condition();
+
+            return new ComponentCustomizer() {
+                @SuppressWarnings("unchecked")
+                @Override
+                public void configure(String name, Component target) {
+                    ObjectHelper.notNull(name, "component name");
+                    ObjectHelper.notNull(target, "component instance");
+
+                    try {
+                        consumer.accept(name, (T) target);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+
+                @Override
+                public boolean isEnabled(String name, Component target) {
+                    ObjectHelper.notNull(name, "component name");
+                    ObjectHelper.notNull(target, "component instance");
+
+                    return condition.test(name, target);
+                }
+
+                @Override
+                public int getOrder() {
+                    return order;
+                }
+            };
+        }
+
+        private BiPredicate<String, Component> condition() {
+            if (type.equals(Component.class)) {
+                return this.condition != null
+                        ? this.condition
+                        : new BiPredicate<String, Component>() {
+                            @Override
+                            public boolean test(String s, Component language) {
+                                return true;
+                            }
+                        };
+            }
+
+            if (condition == null) {
+                return new BiPredicate<String, Component>() {
+                    @Override
+                    public boolean test(String name, Component target) {
+                        return type.isAssignableFrom(target.getClass());
+                    }
+                };
+            }
+
+            return new BiPredicate<String, Component>() {
+                @Override
+                public boolean test(String name, Component target) {
+                    return type.isAssignableFrom(target.getClass()) && condition.test(name, target);
+                }
+            };
+        }
+    }
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/DataFormatCustomizer.java b/core/camel-api/src/main/java/org/apache/camel/spi/DataFormatCustomizer.java
index 7e128f6..7507f75 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/DataFormatCustomizer.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/DataFormatCustomizer.java
@@ -16,12 +16,217 @@
  */
 package org.apache.camel.spi;
 
+import java.util.function.BiPredicate;
+
+import org.apache.camel.Component;
+import org.apache.camel.Ordered;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.function.ThrowingBiConsumer;
+import org.apache.camel.util.function.ThrowingConsumer;
+
+/**
+ * To apply custom configurations to {@link DataFormat} instances.
+ */
 @FunctionalInterface
-public interface DataFormatCustomizer<T extends DataFormat> {
+public interface DataFormatCustomizer extends Ordered {
+    /**
+     * Create a generic {@link Builder}.
+     *
+     * @return the {@link Builder}
+     */
+    static Builder<DataFormat> builder() {
+        return builder(DataFormat.class);
+    }
+
+    /**
+     * Create a typed {@link Builder} that can process a concrete data format type instance.
+     *
+     * @param  type the concrete type of the {@link Component}
+     * @return      the {@link ComponentCustomizer.Builder}
+     */
+    static <T extends DataFormat> Builder<T> builder(Class<T> type) {
+        return new Builder<>(type);
+    }
+
     /**
-     * Customize the specified {@link DataFormat}
+     * Create a {@link DataFormatCustomizer} that can process a concrete data format type instance.
      *
-     * @param dataformat the dataformat to customize
+     * @param  type     the concrete type of the {@link DataFormat}
+     * @param  consumer the {@link DataFormat} configuration logic
+     * @return          the {@link DataFormatCustomizer}
      */
-    void customize(T dataformat);
+    static <T extends DataFormat> DataFormatCustomizer forType(Class<T> type, ThrowingConsumer<T, Exception> consumer) {
+        return builder(type).build(consumer);
+    }
+
+    /**
+     * Customize the specified {@link DataFormat}.
+     *
+     * @param name   the unique name of the data format
+     * @param target the data format to configure
+     */
+    void configure(String name, DataFormat target);
+
+    /**
+     * Checks whether this customizer should be applied to the given {@link DataFormat}.
+     *
+     * @param  name   the unique name of the data format
+     * @param  target the data format to configure
+     * @return        <tt>true</tt> if the customizer should be applied
+     */
+    default boolean isEnabled(String name, DataFormat target) {
+        return true;
+    }
+
+    @Override
+    default int getOrder() {
+        return 0;
+    }
+
+    // ***************************************
+    //
+    // Filter
+    //
+    // ***************************************
+
+    /**
+     * Used as additional filer mechanism to control if customizers need to be applied or not. Each of this filter is
+     * applied before any of the discovered {@link DataFormatCustomizer} is invoked.
+     * </p>
+     * This interface is useful to implement enable/disable logic on a group of customizers.
+     */
+    @FunctionalInterface
+    interface Policy extends BiPredicate<String, DataFormat> {
+        /**
+         * A simple deny-all policy.
+         *
+         * @return false
+         */
+        static Policy none() {
+            return new Policy() {
+                @Override
+                public boolean test(String s, DataFormat target) {
+                    return false;
+                }
+            };
+        }
+
+        /**
+         * A simple allow-all policy.
+         *
+         * @return true
+         */
+        static Policy any() {
+            return new Policy() {
+                @Override
+                public boolean test(String s, DataFormat target) {
+                    return true;
+                }
+            };
+        }
+    }
+
+    // ***************************************
+    //
+    // Builders
+    //
+    // ***************************************
+
+    /**
+     * A fluent builder to create a {@link DataFormatCustomizer} instance.
+     *
+     * @param <T> the concrete type of the {@link DataFormat}.
+     */
+    class Builder<T extends DataFormat> {
+        private final Class<T> type;
+        private BiPredicate<String, DataFormat> condition;
+        private int order;
+
+        public Builder(Class<T> type) {
+            this.type = type;
+        }
+
+        public Builder<T> withOrder(int order) {
+            this.order = order;
+
+            return this;
+        }
+
+        public Builder<T> withCondition(BiPredicate<String, DataFormat> condition) {
+            this.condition = condition;
+
+            return this;
+        }
+
+        public DataFormatCustomizer build(ThrowingConsumer<T, Exception> consumer) {
+            return build(new ThrowingBiConsumer<String, T, Exception>() {
+                @Override
+                public void accept(String name, T target) throws Exception {
+                    consumer.accept(target);
+                }
+            });
+        }
+
+        public DataFormatCustomizer build(ThrowingBiConsumer<String, T, Exception> consumer) {
+            final int order = this.order;
+            final BiPredicate<String, DataFormat> condition = condition();
+
+            return new DataFormatCustomizer() {
+                @SuppressWarnings("unchecked")
+                @Override
+                public void configure(String name, DataFormat target) {
+                    ObjectHelper.notNull(name, "data format name");
+                    ObjectHelper.notNull(target, "data format instance");
+
+                    try {
+                        consumer.accept(name, (T) target);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+
+                @Override
+                public boolean isEnabled(String name, DataFormat target) {
+                    ObjectHelper.notNull(name, "data format name");
+                    ObjectHelper.notNull(target, "data format instance");
+
+                    return condition.test(name, target);
+                }
+
+                @Override
+                public int getOrder() {
+                    return order;
+                }
+            };
+        }
+
+        private BiPredicate<String, DataFormat> condition() {
+            if (type.equals(DataFormat.class)) {
+                return this.condition != null
+                        ? this.condition
+                        : new BiPredicate<String, DataFormat>() {
+                            @Override
+                            public boolean test(String s, DataFormat language) {
+                                return true;
+                            }
+                        };
+            }
+
+            if (condition == null) {
+                return new BiPredicate<String, DataFormat>() {
+                    @Override
+                    public boolean test(String name, DataFormat target) {
+                        return type.isAssignableFrom(target.getClass());
+                    }
+                };
+            }
+
+            return new BiPredicate<String, DataFormat>() {
+                @Override
+                public boolean test(String name, DataFormat target) {
+                    return type.isAssignableFrom(target.getClass()) && condition.test(name, target);
+                }
+            };
+        }
+    }
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/DataFormatResolver.java b/core/camel-api/src/main/java/org/apache/camel/spi/DataFormatResolver.java
index 12b47ad..492f247 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/DataFormatResolver.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/DataFormatResolver.java
@@ -22,16 +22,6 @@ import org.apache.camel.CamelContext;
  * Represents a resolver of data formats.
  */
 public interface DataFormatResolver {
-
-    /**
-     * Resolves the given data format given its name.
-     *
-     * @param  name    the name of the data format to lookup in {@link org.apache.camel.spi.Registry} or create
-     * @param  context the camel context
-     * @return         the data format or <tt>null</tt> if not possible to resolve
-     */
-    DataFormat resolveDataFormat(String name, CamelContext context);
-
     /**
      * Creates the given data format given its name.
      *
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/LanguageCustomizer.java b/core/camel-api/src/main/java/org/apache/camel/spi/LanguageCustomizer.java
index 34b3218..76c2ea8 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/LanguageCustomizer.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/LanguageCustomizer.java
@@ -16,12 +16,217 @@
  */
 package org.apache.camel.spi;
 
+import java.util.function.BiPredicate;
+
+import org.apache.camel.Component;
+import org.apache.camel.Ordered;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.function.ThrowingBiConsumer;
+import org.apache.camel.util.function.ThrowingConsumer;
+
+/**
+ * To apply custom configurations to {@link Language} instances.
+ */
 @FunctionalInterface
-public interface LanguageCustomizer<T extends Language> {
+public interface LanguageCustomizer extends Ordered {
+    /**
+     * Create a generic {@link Builder}.
+     *
+     * @return the {@link Builder}
+     */
+    static Builder<Language> builder() {
+        return builder(Language.class);
+    }
+
+    /**
+     * Create a typed {@link Builder} that can process a concrete language type instance.
+     *
+     * @param  type the concrete type of the {@link Component}
+     * @return      the {@link ComponentCustomizer.Builder}
+     */
+    static <T extends Language> Builder<T> builder(Class<T> type) {
+        return new Builder<>(type);
+    }
+
     /**
-     * Customize the specified {@link Language}
+     * Create a {@link DataFormatCustomizer} that can process a concrete language type instance.
      *
-     * @param language the language to customize
+     * @param  type     the concrete type of the {@link Language}
+     * @param  consumer the {@link Language} configuration logic
+     * @return          the {@link LanguageCustomizer}
      */
-    void customize(T language);
+    static <T extends Language> LanguageCustomizer forType(Class<T> type, ThrowingConsumer<T, Exception> consumer) {
+        return builder(type).build(consumer);
+    }
+
+    /**
+     * Customize the specified {@link Language}.
+     *
+     * @param name   the unique name of the language
+     * @param target the language to configure
+     */
+    void configure(String name, Language target);
+
+    /**
+     * Checks whether this customizer should be applied to the given {@link Language}.
+     *
+     * @param  name   the unique name of the language
+     * @param  target the language to configure
+     * @return        <tt>true</tt> if the customizer should be applied
+     */
+    default boolean isEnabled(String name, Language target) {
+        return true;
+    }
+
+    @Override
+    default int getOrder() {
+        return 0;
+    }
+
+    // ***************************************
+    //
+    // Filter
+    //
+    // ***************************************
+
+    /**
+     * Used as additional filer mechanism to control if customizers need to be applied or not. Each of this filter is
+     * applied before any of the discovered {@link LanguageCustomizer} is invoked.
+     * </p>
+     * This interface is useful to implement enable/disable logic on a group of customizers.
+     */
+    @FunctionalInterface
+    interface Policy extends BiPredicate<String, Language> {
+        /**
+         * A simple deny-all policy.
+         *
+         * @return false
+         */
+        static Policy none() {
+            return new Policy() {
+                @Override
+                public boolean test(String s, Language target) {
+                    return false;
+                }
+            };
+        }
+
+        /**
+         * A simple allow-all policy.
+         *
+         * @return true
+         */
+        static Policy any() {
+            return new Policy() {
+                @Override
+                public boolean test(String s, Language target) {
+                    return true;
+                }
+            };
+        }
+    }
+
+    // ***************************************
+    //
+    // Builders
+    //
+    // ***************************************
+
+    /**
+     * A fluent builder to create a {@link LanguageCustomizer} instance.
+     *
+     * @param <T> the concrete type of the {@link Language}.
+     */
+    class Builder<T extends Language> {
+        private final Class<T> type;
+        private BiPredicate<String, Language> condition;
+        private int order;
+
+        public Builder(Class<T> type) {
+            this.type = type;
+        }
+
+        public Builder<T> withOrder(int order) {
+            this.order = order;
+
+            return this;
+        }
+
+        public Builder<T> withCondition(BiPredicate<String, Language> condition) {
+            this.condition = condition;
+
+            return this;
+        }
+
+        public LanguageCustomizer build(ThrowingConsumer<T, Exception> consumer) {
+            return build(new ThrowingBiConsumer<String, T, Exception>() {
+                @Override
+                public void accept(String name, T target) throws Exception {
+                    consumer.accept(target);
+                }
+            });
+        }
+
+        public LanguageCustomizer build(ThrowingBiConsumer<String, T, Exception> consumer) {
+            final int order = this.order;
+            final BiPredicate<String, Language> condition = this.condition != null ? this.condition : (name, target) -> true;
+
+            return new LanguageCustomizer() {
+                @SuppressWarnings("unchecked")
+                @Override
+                public void configure(String name, Language target) {
+                    ObjectHelper.notNull(name, "language name");
+                    ObjectHelper.notNull(target, "language instance");
+
+                    try {
+                        consumer.accept(name, (T) target);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+
+                @Override
+                public boolean isEnabled(String name, Language target) {
+                    ObjectHelper.notNull(name, "language name");
+                    ObjectHelper.notNull(target, "language instance");
+
+                    return type.isAssignableFrom(target.getClass()) && condition.test(name, target);
+                }
+
+                @Override
+                public int getOrder() {
+                    return order;
+                }
+            };
+        }
+
+        private BiPredicate<String, Language> condition() {
+            if (type.equals(Language.class)) {
+                return this.condition != null
+                        ? this.condition
+                        : new BiPredicate<String, Language>() {
+                            @Override
+                            public boolean test(String s, Language language) {
+                                return true;
+                            }
+                        };
+            }
+
+            if (condition == null) {
+                return new BiPredicate<String, Language>() {
+                    @Override
+                    public boolean test(String name, Language target) {
+                        return type.isAssignableFrom(target.getClass());
+                    }
+                };
+            }
+
+            return new BiPredicate<String, Language>() {
+                @Override
+                public boolean test(String name, Language target) {
+                    return type.isAssignableFrom(target.getClass()) && condition.test(name, target);
+                }
+            };
+        }
+    }
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/LifecycleStrategy.java b/core/camel-api/src/main/java/org/apache/camel/spi/LifecycleStrategy.java
index a31edec..f837cae 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/LifecycleStrategy.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/LifecycleStrategy.java
@@ -144,6 +144,24 @@ public interface LifecycleStrategy {
     void onEndpointRemove(Endpoint endpoint);
 
     /**
+     * Notification on {@link DataFormat} being resolved from the {@link Registry}
+     *
+     * @param name       the unique name of the {@link DataFormat}
+     * @param dataFormat the resolved {@link DataFormat}
+     */
+    default void onDataFormatCreated(String name, DataFormat dataFormat) {
+    }
+
+    /**
+     * Notification on a {@link Language} instance being resolved.
+     *
+     * @param name     the unique name of the {@link Language}
+     * @param language the created {@link Language}
+     */
+    default void onLanguageCreated(String name, Language language) {
+    }
+
+    /**
      * Notification on adding a {@link Service}.
      *
      * @param context the camel context
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index f8a9252..5a6a30f 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -151,6 +151,7 @@ import org.apache.camel.support.LRUCacheFactory;
 import org.apache.camel.support.NormalizedUri;
 import org.apache.camel.support.OrderedComparator;
 import org.apache.camel.support.ProcessorEndpoint;
+import org.apache.camel.support.ResolverHelper;
 import org.apache.camel.support.jsse.SSLContextParameters;
 import org.apache.camel.support.service.BaseService;
 import org.apache.camel.support.service.ServiceHelper;
@@ -186,6 +187,7 @@ public abstract class AbstractCamelContext extends BaseService
     private final List<StartupListener> startupListeners = new CopyOnWriteArrayList<>();
     private final DeferServiceStartupListener deferStartupListener = new DeferServiceStartupListener();
     private final Map<String, Language> languages = new ConcurrentHashMap<>();
+    private final Map<String, DataFormat> dataformats = new ConcurrentHashMap<>();
     private final List<LifecycleStrategy> lifecycleStrategies = new CopyOnWriteArrayList<>();
     private final ThreadLocal<Boolean> isStartingRoutes = new ThreadLocal<>();
     private final ThreadLocal<Boolean> isSetupRoutes = new ThreadLocal<>();
@@ -329,6 +331,9 @@ public abstract class AbstractCamelContext extends BaseService
         // add a default LifecycleStrategy that discover strategies on the registry and invoke them
         this.lifecycleStrategies.add(new OnCamelContextLifecycleStrategy());
 
+        // add a default LifecycleStrategy to customize services using customizers from registry
+        this.lifecycleStrategies.add(new CustomizersLifecycleStrategy(this));
+
         if (build) {
             try {
                 build();
@@ -1730,22 +1735,38 @@ public abstract class AbstractCamelContext extends BaseService
     public Language resolveLanguage(String language) {
         Language answer;
         synchronized (languages) {
-            answer = languages.get(language);
+            // as first iteration, check if there is a language instance for the given name
+            // bound to the registry
+            answer = ResolverHelper.lookupLanguageInRegistryWithFallback(getCamelContextReference(), language);
+            if (answer != null) {
+                Language old = languages.put(language, answer);
+                // if the language has already been loaded, thus it is already registered
+                // in the local language cache, we can return it as it has already been
+                // initialized and configured
+                if (old == answer) {
+                    return answer;
+                }
+            } else {
+                answer = languages.get(language);
 
-            // check if the language is singleton, if so return the shared
-            // instance
-            if (IsSingleton.test(answer)) {
-                return answer;
+                // check if the language is singleton, if so return the shared
+                // instance
+                if (IsSingleton.test(answer)) {
+                    return answer;
+                } else {
+                    answer = null;
+                }
             }
 
-            // language not known or not singleton, then use resolver
-            answer = getLanguageResolver().resolveLanguage(language, getCamelContextReference());
+            if (answer == null) {
+                // language not known or not singleton, then use resolver
+                answer = getLanguageResolver().resolveLanguage(language, getCamelContextReference());
+            }
 
-            // inject CamelContext if aware
             if (answer != null) {
-                if (answer instanceof CamelContextAware) {
-                    ((CamelContextAware) answer).setCamelContext(getCamelContextReference());
-                }
+                // inject CamelContext if aware
+                CamelContextAware.trySetCamelContext(answer, getCamelContextReference());
+
                 if (answer instanceof Service) {
                     try {
                         startService((Service) answer);
@@ -1754,6 +1775,10 @@ public abstract class AbstractCamelContext extends BaseService
                     }
                 }
 
+                for (LifecycleStrategy strategy : lifecycleStrategies) {
+                    strategy.onLanguageCreated(language, answer);
+                }
+
                 languages.put(language, answer);
             }
         }
@@ -3739,14 +3764,25 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public DataFormat resolveDataFormat(String name) {
-        DataFormat answer = getDataFormatResolver().resolveDataFormat(name, getCamelContextReference());
+        final DataFormat answer = dataformats.computeIfAbsent(name, new Function<String, DataFormat>() {
+            @Override
+            public DataFormat apply(String s) {
+                DataFormat df = ResolverHelper.lookupDataFormatInRegistryWithFallback(getCamelContextReference(), name);
 
-        // inject CamelContext if aware
-        if (answer instanceof CamelContextAware) {
-            ((CamelContextAware) answer).setCamelContext(getCamelContextReference());
-        }
+                if (df != null) {
+                    // inject CamelContext if aware
+                    CamelContextAware.trySetCamelContext(df, getCamelContextReference());
 
-        return answer;
+                    for (LifecycleStrategy strategy : lifecycleStrategies) {
+                        strategy.onDataFormatCreated(name, df);
+                    }
+                }
+
+                return df;
+            }
+        });
+
+        return answer != null ? answer : createDataFormat(name);
     }
 
     @Override
@@ -3754,8 +3790,10 @@ public abstract class AbstractCamelContext extends BaseService
         DataFormat answer = getDataFormatResolver().createDataFormat(name, getCamelContextReference());
 
         // inject CamelContext if aware
-        if (answer instanceof CamelContextAware) {
-            ((CamelContextAware) answer).setCamelContext(getCamelContextReference());
+        CamelContextAware.trySetCamelContext(answer, getCamelContextReference());
+
+        for (LifecycleStrategy strategy : lifecycleStrategies) {
+            strategy.onDataFormatCreated(name, answer);
         }
 
         return answer;
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/CustomizersLifecycleStrategy.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/CustomizersLifecycleStrategy.java
new file mode 100644
index 0000000..d2b5855
--- /dev/null
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/CustomizersLifecycleStrategy.java
@@ -0,0 +1,87 @@
+/*
+ * 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.impl.engine;
+
+import java.util.Comparator;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
+import org.apache.camel.Ordered;
+import org.apache.camel.spi.ComponentCustomizer;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.DataFormatCustomizer;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.LanguageCustomizer;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.LifecycleStrategySupport;
+
+class CustomizersLifecycleStrategy extends LifecycleStrategySupport {
+    private final CamelContext camelContext;
+
+    public CustomizersLifecycleStrategy(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    public void onComponentAdd(String name, Component component) {
+        Registry registry = this.camelContext.getRegistry();
+        if (registry == null) {
+            return;
+        }
+
+        Set<ComponentCustomizer.Policy> filters = registry.findByType(ComponentCustomizer.Policy.class);
+        if (filters.isEmpty() || filters.stream().allMatch(filter -> filter.test(name, component))) {
+            registry.findByType(ComponentCustomizer.class).stream()
+                    .sorted(Comparator.comparingInt(Ordered::getOrder))
+                    .filter(customizer -> customizer.isEnabled(name, component))
+                    .forEach(customizer -> customizer.configure(name, component));
+        }
+    }
+
+    @Override
+    public void onDataFormatCreated(String name, DataFormat dataFormat) {
+        Registry registry = this.camelContext.getRegistry();
+        if (registry == null) {
+            return;
+        }
+
+        Set<DataFormatCustomizer.Policy> filters = registry.findByType(DataFormatCustomizer.Policy.class);
+        if (filters.isEmpty() || filters.stream().allMatch(filter -> filter.test(name, dataFormat))) {
+            registry.findByType(DataFormatCustomizer.class).stream()
+                    .sorted(Comparator.comparingInt(Ordered::getOrder))
+                    .filter(customizer -> customizer.isEnabled(name, dataFormat))
+                    .forEach(customizer -> customizer.configure(name, dataFormat));
+        }
+    }
+
+    @Override
+    public void onLanguageCreated(String name, Language language) {
+        Registry registry = this.camelContext.getRegistry();
+        if (registry == null) {
+            return;
+        }
+
+        Set<LanguageCustomizer.Policy> filters = registry.findByType(LanguageCustomizer.Policy.class);
+        if (filters.isEmpty() || filters.stream().allMatch(filter -> filter.test(name, language))) {
+            registry.findByType(LanguageCustomizer.class).stream()
+                    .sorted(Comparator.comparingInt(Ordered::getOrder))
+                    .filter(customizer -> customizer.isEnabled(name, language))
+                    .forEach(customizer -> customizer.configure(name, language));
+        }
+    }
+}
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultDataFormatResolver.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultDataFormatResolver.java
index 4d01e3e..525cba8 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultDataFormatResolver.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultDataFormatResolver.java
@@ -33,20 +33,6 @@ public class DefaultDataFormatResolver implements DataFormatResolver {
     private FactoryFinder dataformatFactory;
 
     @Override
-    public DataFormat resolveDataFormat(String name, CamelContext context) {
-        // lookup in registry first
-        DataFormat dataFormat = ResolverHelper.lookupDataFormatInRegistryWithFallback(context, name);
-
-        if (dataFormat == null) {
-            // If not found in the registry, try to create a new instance using
-            // a DataFormatFactory or from resources
-            dataFormat = createDataFormat(name, context);
-        }
-
-        return dataFormat;
-    }
-
-    @Override
     public DataFormat createDataFormat(String name, CamelContext context) {
         DataFormat dataFormat = null;
 
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultLanguageResolver.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultLanguageResolver.java
index 4043f9e..d596361 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultLanguageResolver.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultLanguageResolver.java
@@ -23,7 +23,6 @@ import org.apache.camel.NoSuchLanguageException;
 import org.apache.camel.spi.FactoryFinder;
 import org.apache.camel.spi.Language;
 import org.apache.camel.spi.LanguageResolver;
-import org.apache.camel.support.ResolverHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,12 +41,6 @@ public class DefaultLanguageResolver implements LanguageResolver {
 
     @Override
     public Language resolveLanguage(String name, CamelContext context) {
-        // lookup in registry first
-        Language languageReg = ResolverHelper.lookupLanguageInRegistryWithFallback(context, name);
-        if (languageReg != null) {
-            return languageReg;
-        }
-
         Class<?> type = null;
         try {
             type = findLanguage(name, context);
diff --git a/core/camel-core/src/test/java/org/apache/camel/support/CustomizersTest.java b/core/camel-core/src/test/java/org/apache/camel/support/CustomizersTest.java
new file mode 100644
index 0000000..151f67d
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/support/CustomizersTest.java
@@ -0,0 +1,457 @@
+/*
+ * 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.support;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Stream;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.log.LogComponent;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.language.tokenizer.TokenizeLanguage;
+import org.apache.camel.spi.ComponentCustomizer;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.DataFormatCustomizer;
+import org.apache.camel.spi.DataFormatFactory;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.LanguageCustomizer;
+import org.apache.camel.support.processor.DefaultExchangeFormatter;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.apache.camel.util.CollectionHelper.propertiesOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class CustomizersTest {
+
+    // *****************************
+    //
+    // Helpers
+    //
+    // *****************************
+
+    public static Stream<Arguments> disableLanguageCustomizationProperties() {
+        return Stream.of(
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true",
+                        "camel.customizer.language.enabled", "true",
+                        "camel.customizer.language.tokenize.enabled", "false")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true",
+                        "camel.customizer.language.enabled", "false")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false")));
+    }
+
+    public static Stream<Arguments> enableLanguageCustomizationProperties() {
+        return Stream.of(
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false",
+                        "camel.customizer.language.enabled", "false",
+                        "camel.customizer.language.tokenize.enabled", "true")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false",
+                        "camel.customizer.language.enabled", "true")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true")));
+    }
+
+    public static Stream<Arguments> disableDataFormatCustomizationProperties() {
+        return Stream.of(
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true",
+                        "camel.customizer.dataformat.enabled", "true",
+                        "camel.customizer.dataformat.my-df.enabled", "false")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true",
+                        "camel.customizer.dataformat.enabled", "false")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false")));
+    }
+
+    public static Stream<Arguments> enableDataFormatCustomizationProperties() {
+        return Stream.of(
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false",
+                        "camel.customizer.dataformat.enabled", "false",
+                        "camel.customizer.dataformat.my-df.enabled", "true")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false",
+                        "camel.customizer.dataformat.enabled", "true")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true")));
+    }
+
+    public static Stream<Arguments> disableComponentCustomizationProperties() {
+        return Stream.of(
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true",
+                        "camel.customizer.component.enabled", "true",
+                        "camel.customizer.component.log.enabled", "false")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true",
+                        "camel.customizer.component.enabled", "false")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false")));
+    }
+
+    public static Stream<Arguments> enableComponentCustomizationProperties() {
+        return Stream.of(
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false",
+                        "camel.customizer.component.enabled", "false",
+                        "camel.customizer.component.log.enabled", "true")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "false",
+                        "camel.customizer.component.enabled", "true")),
+                Arguments.of(propertiesOf(
+                        "camel.customizer.enabled", "true")));
+    }
+
+    // *****************************
+    //
+    // Component
+    //
+    // *****************************
+
+    @Test
+    public void testComponentCustomization() {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind(
+                "log-customizer",
+                ComponentCustomizer.forType(
+                        LogComponent.class,
+                        target -> target.setExchangeFormatter(new MyExchangeFormatter())));
+
+        assertTrue(context.getComponent("log", LogComponent.class).getExchangeFormatter() instanceof MyExchangeFormatter);
+    }
+
+    @Test
+    public void testComponentCustomizationWithFilter() {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind(
+                "customizer-filter",
+                ComponentCustomizer.Policy.none());
+        context.getRegistry().bind(
+                "log-customizer",
+                ComponentCustomizer.forType(
+                        LogComponent.class,
+                        target -> target.setExchangeFormatter(new MyExchangeFormatter())));
+
+        assertFalse(context.getComponent("log", LogComponent.class).getExchangeFormatter() instanceof MyExchangeFormatter);
+    }
+
+    @Test
+    public void testComponentCustomizationWithFluentBuilder() {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind(
+                "log-customizer",
+                ComponentCustomizer.forType(
+                        LogComponent.class,
+                        target -> target.setExchangeFormatter(new MyExchangeFormatter())));
+
+        assertTrue(context.getComponent("log", LogComponent.class).getExchangeFormatter() instanceof MyExchangeFormatter);
+    }
+
+    @ParameterizedTest
+    @MethodSource("disableComponentCustomizationProperties")
+    public void testComponentCustomizationDisabledByProperty(Properties properties) {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(properties);
+
+        context.getRegistry().bind(
+                "customizer-filter",
+                new CustomizersSupport.ComponentCustomizationEnabledPolicy());
+        context.getRegistry().bind(
+                "log-customizer",
+                ComponentCustomizer.forType(
+                        LogComponent.class,
+                        target -> target.setExchangeFormatter(new MyExchangeFormatter())));
+
+        assertFalse(context.getComponent("log", LogComponent.class).getExchangeFormatter() instanceof MyExchangeFormatter);
+    }
+
+    @ParameterizedTest
+    @MethodSource("enableComponentCustomizationProperties")
+    public void testComponentCustomizationEnabledByProperty(Properties properties) {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(properties);
+
+        context.getRegistry().bind(
+                "customizer-filter",
+                new CustomizersSupport.ComponentCustomizationEnabledPolicy());
+        context.getRegistry().bind(
+                "log-customizer",
+                ComponentCustomizer.forType(
+                        LogComponent.class,
+                        target -> target.setExchangeFormatter(new MyExchangeFormatter())));
+
+        assertTrue(context.getComponent("log", LogComponent.class).getExchangeFormatter() instanceof MyExchangeFormatter);
+    }
+
+    // *****************************
+    //
+    // Data Format
+    //
+    // *****************************
+
+    @Test
+    public void testDataFormatCustomization() {
+        AtomicInteger counter = new AtomicInteger();
+
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind(
+                "my-df",
+                (DataFormatFactory) MyDataFormat::new);
+        context.getRegistry().bind(
+                "my-df-customizer",
+                DataFormatCustomizer.forType(MyDataFormat.class, target -> target.setId(counter.incrementAndGet())));
+
+        DataFormat df1 = context.resolveDataFormat("my-df");
+        DataFormat df2 = context.resolveDataFormat("my-df");
+
+        assertNotEquals(df1, df2);
+
+        assertTrue(df1 instanceof MyDataFormat);
+        assertEquals(1, ((MyDataFormat) df1).getId());
+        assertTrue(df2 instanceof MyDataFormat);
+        assertEquals(2, ((MyDataFormat) df2).getId());
+    }
+
+    @Test
+    public void testDataFormatCustomizationWithFilter() {
+        AtomicInteger counter = new AtomicInteger();
+
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind(
+                "customizer-filter",
+                DataFormatCustomizer.Policy.none());
+        context.getRegistry().bind(
+                "my-df",
+                (DataFormatFactory) MyDataFormat::new);
+        context.getRegistry().bind(
+                "my-df-customizer",
+                DataFormatCustomizer.forType(MyDataFormat.class, target -> target.setId(counter.incrementAndGet())));
+
+        DataFormat df1 = context.resolveDataFormat("my-df");
+        DataFormat df2 = context.resolveDataFormat("my-df");
+
+        assertNotEquals(df1, df2);
+
+        assertTrue(df1 instanceof MyDataFormat);
+        assertEquals(0, ((MyDataFormat) df1).getId());
+        assertTrue(df2 instanceof MyDataFormat);
+        assertEquals(0, ((MyDataFormat) df2).getId());
+    }
+
+    @ParameterizedTest
+    @MethodSource("disableDataFormatCustomizationProperties")
+    public void testDataFormatCustomizationDisabledByProperty(Properties properties) {
+        AtomicInteger counter = new AtomicInteger();
+
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(properties);
+
+        context.getRegistry().bind(
+                "customizer-filter",
+                new CustomizersSupport.DataFormatCustomizationEnabledPolicy());
+        context.getRegistry().bind(
+                "my-df",
+                (DataFormatFactory) MyDataFormat::new);
+        context.getRegistry().bind(
+                "my-df-customizer",
+                DataFormatCustomizer.forType(MyDataFormat.class, target -> target.setId(counter.incrementAndGet())));
+
+        DataFormat df1 = context.resolveDataFormat("my-df");
+        assertEquals(0, ((MyDataFormat) df1).getId());
+    }
+
+    @ParameterizedTest
+    @MethodSource("enableDataFormatCustomizationProperties")
+    public void testDataFormatCustomizationEnabledByProperty(Properties properties) {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(properties);
+
+        context.getRegistry().bind(
+                "customizer-filter",
+                new CustomizersSupport.DataFormatCustomizationEnabledPolicy());
+        context.getRegistry().bind(
+                "my-df",
+                (DataFormatFactory) MyDataFormat::new);
+        context.getRegistry().bind(
+                "my-df-customizer",
+                DataFormatCustomizer.forType(MyDataFormat.class, target -> target.setId(1)));
+
+        DataFormat df1 = context.resolveDataFormat("my-df");
+        assertEquals(1, ((MyDataFormat) df1).getId());
+    }
+
+    // *****************************
+    //
+    // Language
+    //
+    // *****************************
+
+    @Test
+    public void testLanguageCustomizationFromRegistry() {
+        AtomicInteger counter = new AtomicInteger();
+
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind(
+                "tokenize",
+                new TokenizeLanguage());
+        context.getRegistry().bind(
+                "tokenize-customizer",
+                LanguageCustomizer.forType(TokenizeLanguage.class, target -> target.setGroup("" + counter.incrementAndGet())));
+
+        Language l1 = context.resolveLanguage("tokenize");
+        assertTrue(l1 instanceof TokenizeLanguage);
+        assertEquals("1", ((TokenizeLanguage) l1).getGroup());
+
+        Language l2 = context.resolveLanguage("tokenize");
+        assertTrue(l2 instanceof TokenizeLanguage);
+        assertEquals("1", ((TokenizeLanguage) l2).getGroup());
+
+        // as the language is resolved via the registry, then the instance is the same
+        // even if it is not a singleton
+        assertSame(l1, l2);
+    }
+
+    @Test
+    public void testLanguageCustomizationFromResource() {
+        AtomicInteger counter = new AtomicInteger();
+
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind(
+                "tokenize-customizer",
+                LanguageCustomizer.forType(TokenizeLanguage.class, target -> target.setGroup("" + counter.incrementAndGet())));
+
+        Language l1 = context.resolveLanguage("tokenize");
+        assertTrue(l1 instanceof TokenizeLanguage);
+        assertEquals("1", ((TokenizeLanguage) l1).getGroup());
+
+        Language l2 = context.resolveLanguage("tokenize");
+        assertTrue(l2 instanceof TokenizeLanguage);
+        assertEquals("2", ((TokenizeLanguage) l2).getGroup());
+
+        assertNotSame(l1, l2);
+    }
+
+    @Test
+    public void testLanguageCustomizationFromResourceWithFilter() {
+        AtomicInteger counter = new AtomicInteger();
+
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind(
+                "customizer-filter",
+                LanguageCustomizer.Policy.none());
+        context.getRegistry().bind(
+                "tokenize-customizer",
+                LanguageCustomizer.forType(TokenizeLanguage.class, target -> target.setGroup("" + counter.incrementAndGet())));
+
+        Language l1 = context.resolveLanguage("tokenize");
+        assertTrue(l1 instanceof TokenizeLanguage);
+        assertNull(((TokenizeLanguage) l1).getGroup());
+
+        Language l2 = context.resolveLanguage("tokenize");
+        assertTrue(l2 instanceof TokenizeLanguage);
+        assertNull(((TokenizeLanguage) l2).getGroup());
+
+        assertNotSame(l1, l2);
+    }
+
+    @ParameterizedTest
+    @MethodSource("disableLanguageCustomizationProperties")
+    public void testLanguageCustomizationDisabledByProperty(Properties initialProperties) {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(initialProperties);
+
+        context.getRegistry().bind(
+                "customizer-filter",
+                new CustomizersSupport.LanguageCustomizationEnabledPolicy());
+        context.getRegistry().bind(
+                "tokenize-customizer",
+                LanguageCustomizer.forType(TokenizeLanguage.class, target -> target.setGroup("something")));
+
+        assertNotEquals("something", ((TokenizeLanguage) context.resolveLanguage("tokenize")).getGroup());
+    }
+
+    @ParameterizedTest
+    @MethodSource("enableLanguageCustomizationProperties")
+    public void testLanguageCustomizationEnabledByProperty(Properties initialProperties) {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(initialProperties);
+
+        context.getRegistry().bind(
+                "customizer-filter",
+                new CustomizersSupport.LanguageCustomizationEnabledPolicy());
+        context.getRegistry().bind(
+                "tokenize-customizer",
+                LanguageCustomizer.forType(TokenizeLanguage.class, target -> target.setGroup("something")));
+
+        assertEquals("something", ((TokenizeLanguage) context.resolveLanguage("tokenize")).getGroup());
+    }
+
+    // *****************************
+    //
+    // Model
+    //
+    // *****************************
+
+    public static class MyDataFormat implements DataFormat {
+        private int id;
+
+        @Override
+        public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
+        }
+
+        @Override
+        public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
+            return null;
+        }
+
+        @Override
+        public void start() {
+        }
+
+        @Override
+        public void stop() {
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public void setId(int id) {
+            this.id = id;
+        }
+    }
+
+    public static class MyExchangeFormatter extends DefaultExchangeFormatter {
+    }
+}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainCustomizerTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainCustomizerTest.java
new file mode 100644
index 0000000..c4c8031
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainCustomizerTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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 org.apache.camel.BindToRegistry;
+import org.apache.camel.component.log.LogComponent;
+import org.apache.camel.spi.ComponentCustomizer;
+import org.apache.camel.support.CustomizersSupport;
+import org.apache.camel.support.processor.DefaultExchangeFormatter;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class MainCustomizerTest {
+    @Test
+    public void testComponentCustomizer() {
+        Main main = new Main();
+
+        try {
+            main.configure().addConfigurationClass(MyConfiguration.class);
+            main.start();
+
+            LogComponent component = main.getCamelContext().getComponent("log", LogComponent.class);
+            assertTrue(component.getExchangeFormatter() instanceof MyFormatter);
+        } finally {
+            main.stop();
+        }
+    }
+
+    @Test
+    public void testComponentCustomizerDisabledWithPolicy() {
+        Main main = new Main();
+
+        try {
+            main.bind("my-filter", ComponentCustomizer.Policy.none());
+            main.configure().addConfigurationClass(MyConfiguration.class);
+            main.start();
+
+            LogComponent component = main.getCamelContext().getComponent("log", LogComponent.class);
+            assertFalse(component.getExchangeFormatter() instanceof MyFormatter);
+        } finally {
+            main.stop();
+        }
+    }
+
+    @Test
+    public void testComponentCustomizerDisabledWithProperties() {
+        Main main = new Main();
+
+        try {
+            main.addInitialProperty("camel.customizer.component.log.enabled", "false");
+            main.bind("my-filter", ComponentCustomizer.Policy.any());
+            main.configure().addConfigurationClass(MyConfiguration.class);
+            main.start();
+
+            LogComponent component = main.getCamelContext().getComponent("log", LogComponent.class);
+            assertFalse(component.getExchangeFormatter() instanceof MyFormatter);
+        } finally {
+            main.stop();
+        }
+    }
+
+    // ****************************
+    //
+    // Helpers
+    //
+    // ****************************
+
+    public static class MyConfiguration {
+        @BindToRegistry
+        public ComponentCustomizer logCustomizer() {
+            return ComponentCustomizer.builder(LogComponent.class)
+                    .build(component -> component.setExchangeFormatter(new MyFormatter()));
+        }
+
+        @BindToRegistry
+        public ComponentCustomizer.Policy componentCustomizationPolicy() {
+            return new CustomizersSupport.ComponentCustomizationEnabledPolicy();
+        }
+    }
+
+    public static class MyFormatter extends DefaultExchangeFormatter {
+    }
+}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/CustomizersSupport.java b/core/camel-support/src/main/java/org/apache/camel/support/CustomizersSupport.java
new file mode 100644
index 0000000..6f28ad8
--- /dev/null
+++ b/core/camel-support/src/main/java/org/apache/camel/support/CustomizersSupport.java
@@ -0,0 +1,129 @@
+/*
+ * 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.support;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.Component;
+import org.apache.camel.spi.ComponentCustomizer;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.DataFormatCustomizer;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.LanguageCustomizer;
+
+public final class CustomizersSupport {
+    private CustomizersSupport() {
+    }
+
+    /**
+     * Determine the value of the "enabled" flag for a hierarchy of properties.
+     *
+     * @param  camelContext the {@link CamelContext}
+     * @param  prefixes     an ordered list of prefixed (less restrictive to more restrictive)
+     * @return              the value of the key `enabled` for most restrictive prefix
+     */
+    public static boolean isEnabled(CamelContext camelContext, String... prefixes) {
+        boolean answer = true;
+
+        // Loop over all the prefixes to find out the value of the key `enabled`
+        // for the most restrictive prefix.
+        for (String prefix : prefixes) {
+            String property = prefix.endsWith(".") ? prefix + "enabled" : prefix + ".enabled";
+
+            // evaluate the value of the current prefix using the parent one as
+            // default value so if the `enabled` property is not set, the parent
+            // one is used.
+            answer = camelContext.getPropertiesComponent()
+                    .resolveProperty(property)
+                    .map(Boolean::valueOf)
+                    .orElse(answer);
+        }
+
+        return answer;
+    }
+
+    /**
+     * Base class for policies
+     */
+    private static class CamelContextAwarePolicy implements CamelContextAware {
+        private CamelContext camelContext;
+
+        @Override
+        public CamelContext getCamelContext() {
+            return this.camelContext;
+        }
+
+        @Override
+        public void setCamelContext(CamelContext camelContext) {
+            this.camelContext = camelContext;
+        }
+    }
+
+    /**
+     * A {@link ComponentCustomizer.Policy} that uses a hierarchical lists of properties to determine if customization
+     * is enabled for the given {@link org.apache.camel.Component}.
+     */
+    public static final class ComponentCustomizationEnabledPolicy
+            extends CamelContextAwarePolicy
+            implements ComponentCustomizer.Policy {
+
+        @Override
+        public boolean test(String name, Component target) {
+            return isEnabled(
+                    getCamelContext(),
+                    "camel.customizer",
+                    "camel.customizer.component",
+                    "camel.customizer.component." + name);
+        }
+    }
+
+    /**
+     * A {@link DataFormatCustomizer.Policy} that uses a hierarchical lists of properties to determine if customization
+     * is enabled for the given {@link org.apache.camel.spi.DataFormat}.
+     */
+    public static final class DataFormatCustomizationEnabledPolicy
+            extends CamelContextAwarePolicy
+            implements DataFormatCustomizer.Policy {
+
+        @Override
+        public boolean test(String name, DataFormat target) {
+            return isEnabled(
+                    getCamelContext(),
+                    "camel.customizer",
+                    "camel.customizer.dataformat",
+                    "camel.customizer.dataformat." + name);
+        }
+    }
+
+    /**
+     * A {@link LanguageCustomizer.Policy} that uses a hierarchical lists of properties to determine if customization is
+     * enabled for the given {@link org.apache.camel.spi.Language}.
+     */
+    public static final class LanguageCustomizationEnabledPolicy
+            extends CamelContextAwarePolicy
+            implements LanguageCustomizer.Policy {
+
+        @Override
+        public boolean test(String name, Language target) {
+            return isEnabled(
+                    getCamelContext(),
+                    "camel.customizer",
+                    "camel.customizer.language",
+                    "camel.customizer.language." + name);
+        }
+    }
+}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
index fc896bc..5fca48b 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
@@ -34,13 +34,11 @@ import java.util.Set;
 import java.util.TreeMap;
 
 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.BeanIntrospection;
 import org.apache.camel.spi.PropertyConfigurer;
 import org.apache.camel.spi.PropertyConfigurerGetter;
-import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.StringQuoteHelper;
 
@@ -88,233 +86,6 @@ import static org.apache.camel.util.StringHelper.startsWithIgnoreCase;
  */
 public final class PropertyBindingSupport {
 
-    /**
-     * To use a fluent builder style to configure this property binding support.
-     */
-    public static class Builder {
-
-        private CamelContext camelContext;
-        private Object target;
-        private Map<String, Object> properties;
-        private boolean removeParameters = true;
-        private boolean flattenProperties;
-        private boolean mandatory;
-        private boolean nesting = true;
-        private boolean deepNesting = true;
-        private boolean reference = true;
-        private boolean placeholder = true;
-        private boolean fluentBuilder = true;
-        private boolean allowPrivateSetter = true;
-        private boolean ignoreCase;
-        private String optionPrefix;
-        private boolean reflection = true;
-        private PropertyConfigurer configurer;
-
-        /**
-         * CamelContext to be used
-         */
-        public Builder withCamelContext(CamelContext camelContext) {
-            this.camelContext = camelContext;
-            return this;
-        }
-
-        /**
-         * Target object that should have parameters bound
-         */
-        public Builder withTarget(Object target) {
-            this.target = target;
-            return this;
-        }
-
-        /**
-         * The properties to use for binding
-         */
-        public Builder withProperties(Map<String, Object> properties) {
-            if (this.properties == null) {
-                this.properties = properties;
-            } else {
-                // there may be existing options so add those if missing
-                // we need to mutate existing as we are may be removing bound properties
-                this.properties.forEach(properties::putIfAbsent);
-                this.properties = properties;
-            }
-            return this;
-        }
-
-        /**
-         * Adds property to use for binding
-         */
-        public Builder withProperty(String key, Object value) {
-            if (this.properties == null) {
-                this.properties = new LinkedHashMap<>();
-            }
-            this.properties.put(key, value);
-            return this;
-        }
-
-        /**
-         * Whether parameters should be removed when its bound
-         */
-        public Builder withRemoveParameters(boolean removeParameters) {
-            this.removeParameters = removeParameters;
-            return this;
-        }
-
-        /**
-         * Whether properties should be flattened (when properties is a map of maps).
-         */
-        public Builder withFlattenProperties(boolean flattenProperties) {
-            this.flattenProperties = flattenProperties;
-            return this;
-        }
-
-        /**
-         * Whether all parameters should be mandatory and successfully bound
-         */
-        public Builder withMandatory(boolean mandatory) {
-            this.mandatory = mandatory;
-            return this;
-        }
-
-        /**
-         * Whether nesting is in use
-         */
-        public Builder withNesting(boolean nesting) {
-            this.nesting = nesting;
-            return this;
-        }
-
-        /**
-         * Whether deep nesting is in use, where Camel will attempt to walk as deep as possible by creating new objects
-         * in the OGNL graph if a property has a setter and the object can be created from a default no-arg constructor.
-         */
-        public Builder withDeepNesting(boolean deepNesting) {
-            this.deepNesting = deepNesting;
-            return this;
-        }
-
-        /**
-         * Whether reference parameter (syntax starts with #) is in use
-         */
-        public Builder withReference(boolean reference) {
-            this.reference = reference;
-            return this;
-        }
-
-        /**
-         * Whether to use Camels property placeholder to resolve placeholders on keys and values
-         */
-        public Builder withPlaceholder(boolean placeholder) {
-            this.placeholder = placeholder;
-            return this;
-        }
-
-        /**
-         * Whether fluent builder is allowed as a valid getter/setter
-         */
-        public Builder withFluentBuilder(boolean fluentBuilder) {
-            this.fluentBuilder = fluentBuilder;
-            return this;
-        }
-
-        /**
-         * Whether properties should be filtered by prefix. * Note that the prefix is removed from the key before the
-         * property is bound.
-         */
-        public Builder withAllowPrivateSetter(boolean allowPrivateSetter) {
-            this.allowPrivateSetter = allowPrivateSetter;
-            return this;
-        }
-
-        /**
-         * Whether to ignore case in the property names (keys).
-         */
-        public Builder withIgnoreCase(boolean ignoreCase) {
-            this.ignoreCase = ignoreCase;
-            return this;
-        }
-
-        /**
-         * Whether properties should be filtered by prefix. Note that the prefix is removed from the key before the
-         * property is bound.
-         */
-        public Builder withOptionPrefix(String optionPrefix) {
-            this.optionPrefix = optionPrefix;
-            return this;
-        }
-
-        /**
-         * Whether to use the configurer to configure the properties.
-         */
-        public Builder withConfigurer(PropertyConfigurer configurer) {
-            this.configurer = configurer;
-            return this;
-        }
-
-        /**
-         * Whether to allow using reflection (when there is no configurer available).
-         */
-        public Builder withReflection(boolean reflection) {
-            this.reflection = reflection;
-            return this;
-        }
-
-        /**
-         * Binds the properties to the target object, and removes the property that was bound from properties.
-         *
-         * @return true if one or more properties was bound
-         */
-        public boolean bind() {
-            // mandatory parameters
-            org.apache.camel.util.ObjectHelper.notNull(camelContext, "camelContext");
-            org.apache.camel.util.ObjectHelper.notNull(target, "target");
-
-            if (properties == null || properties.isEmpty()) {
-                return false;
-            }
-
-            return doBindProperties(camelContext, target, removeParameters ? properties : new HashMap<>(properties),
-                    optionPrefix, ignoreCase, removeParameters, flattenProperties, mandatory,
-                    nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer);
-        }
-
-        /**
-         * Binds the properties to the target object, and removes the property that was bound from properties.
-         *
-         * @param  camelContext the camel context
-         * @param  target       the target object
-         * @param  properties   the properties where the bound properties will be removed from
-         * @return              true if one or more properties was bound
-         */
-        public boolean bind(CamelContext camelContext, Object target, Map<String, Object> properties) {
-            CamelContext context = camelContext != null ? camelContext : this.camelContext;
-            Object obj = target != null ? target : this.target;
-            Map<String, Object> prop = properties != null ? properties : this.properties;
-
-            return doBindProperties(context, obj, removeParameters ? prop : new HashMap<>(prop),
-                    optionPrefix, ignoreCase, removeParameters, flattenProperties, mandatory,
-                    nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer);
-        }
-
-        /**
-         * Binds the property to the target object.
-         *
-         * @param  camelContext the camel context
-         * @param  target       the target object
-         * @param  key          the property key
-         * @param  value        the property value
-         * @return              true if the property was bound
-         */
-        public boolean bind(CamelContext camelContext, Object target, String key, Object value) {
-            Map<String, Object> properties = new HashMap<>(1);
-            properties.put(key, value);
-
-            return doBindProperties(camelContext, target, properties, optionPrefix, ignoreCase, true, false, mandatory,
-                    nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer);
-        }
-
-    }
-
     private PropertyBindingSupport() {
     }
 
@@ -322,21 +93,6 @@ public final class PropertyBindingSupport {
         return new Builder();
     }
 
-    @FunctionalInterface
-    public interface OnAutowiring {
-
-        /**
-         * Callback when a property was autowired on a bean
-         *
-         * @param target       the targeted bean
-         * @param propertyName the name of the property
-         * @param propertyType the type of the property
-         * @param value        the property value
-         */
-        void onAutowire(Object target, String propertyName, Class propertyType, Object value);
-
-    }
-
     /**
      * This will discover all the properties on the target, and automatic bind the properties that are null by looking
      * up in the registry to see if there is a single instance of the same type as the property. This is used for
@@ -391,38 +147,18 @@ public final class PropertyBindingSupport {
         Map<String, Object> properties = new LinkedHashMap<>();
 
         // if there a configurer
-        PropertyConfigurer configurer = null;
-        PropertyConfigurerGetter getter;
-        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();
-            if (target instanceof ExtendedCamelContext) {
-                // special for camel context itself as we have an extended configurer
-                name = ExtendedCamelContext.class.getName();
-            }
-
-            if (isNotEmpty(name)) {
-                // see if there is a configurer for it
-                configurer = camelContext.adapt(ExtendedCamelContext.class).getConfigurerResolver()
-                        .resolvePropertyConfigurer(name, camelContext);
-            }
-        }
+        PropertyConfigurer configurer = PropertyConfigurerHelper.resolvePropertyConfigurer(camelContext, target);
 
         // use configurer to get all the current options and its values
         Map<String, Object> getterAllOption = null;
         if (configurer instanceof PropertyConfigurerGetter) {
-            getter = (PropertyConfigurerGetter) configurer;
-            final PropertyConfigurerGetter lambdaGetter = getter;
+            final PropertyConfigurerGetter getter = (PropertyConfigurerGetter) configurer;
             final Object lambdaTarget = target;
             getterAllOption = getter.getAllOptions(target);
             getterAllOption.forEach((key, type) -> {
                 // we only need the complex types
                 if (isComplexUserType((Class) type)) {
-                    Object value = lambdaGetter.getOptionValue(lambdaTarget, key, true);
+                    Object value = getter.getOptionValue(lambdaTarget, key, true);
                     properties.put(key, value);
                 }
             });
@@ -570,139 +306,6 @@ public final class PropertyBindingSupport {
     }
 
     /**
-     * Used for making it easier to support using option prefix in property binding and to remove the bound properties
-     * from the input map.
-     */
-    private static class OptionPrefixMap extends LinkedHashMap<String, Object> {
-
-        private final String optionPrefix;
-        private final Map<String, Object> originalMap;
-
-        public OptionPrefixMap(Map<String, Object> map, String optionPrefix) {
-            this.originalMap = map;
-            this.optionPrefix = optionPrefix;
-            // copy from original map into our map without the option prefix
-            map.forEach((k, v) -> {
-                if (startsWithIgnoreCase(k, optionPrefix)) {
-                    put(k.substring(optionPrefix.length()), v);
-                } else if (startsWithIgnoreCase(k, "?" + optionPrefix)) {
-                    put(k.substring(optionPrefix.length() + 1), v);
-                }
-            });
-        }
-
-        @Override
-        public Object remove(Object key) {
-            // we only need to care about the remove method,
-            // so we can remove the corresponding key from the original map
-            Set<String> toBeRemoved = new HashSet<>();
-            originalMap.forEach((k, v) -> {
-                if (startsWithIgnoreCase(k, optionPrefix)) {
-                    toBeRemoved.add(k);
-                } else if (startsWithIgnoreCase(k, "?" + optionPrefix)) {
-                    toBeRemoved.add(k);
-                }
-            });
-            toBeRemoved.forEach(originalMap::remove);
-
-            return super.remove(key);
-        }
-
-    }
-
-    /**
-     * Used for flatten properties when they are a map of maps
-     */
-    private static class FlattenMap extends LinkedHashMap<String, Object> {
-
-        private final Map<String, Object> originalMap;
-
-        public FlattenMap(Map<String, Object> map) {
-            this.originalMap = map;
-            flatten("", originalMap);
-        }
-
-        @SuppressWarnings("unchecked")
-        private void flatten(String prefix, Map<?, Object> map) {
-            for (Map.Entry<?, Object> entry : map.entrySet()) {
-                String key = entry.getKey().toString();
-                boolean optional = key.startsWith("?");
-                if (optional) {
-                    key = key.substring(1);
-                }
-                Object value = entry.getValue();
-                String keyPrefix = (optional ? "?" : "") + (prefix.isEmpty() ? key : prefix + "." + key);
-                if (value instanceof Map) {
-                    flatten(keyPrefix, (Map<?, Object>) value);
-                } else {
-                    put(keyPrefix, value);
-                }
-            }
-        }
-
-        @Override
-        public Object remove(Object key) {
-            // we only need to care about the remove method,
-            // so we can remove the corresponding key from the original map
-
-            // walk key with dots to remove right node
-            String[] parts = key.toString().split("\\.");
-            Map map = originalMap;
-            for (int i = 0; i < parts.length; i++) {
-                String part = parts[i];
-                Object obj = map.get(part);
-                if (i == parts.length - 1) {
-                    map.remove(part);
-                } else if (obj instanceof Map) {
-                    map = (Map) obj;
-                }
-            }
-
-            // remove empty middle maps
-            Object answer = super.remove(key);
-            if (super.isEmpty()) {
-                originalMap.clear();
-            }
-            return answer;
-        }
-
-    }
-
-    /**
-     * Used for sorting the property keys when doing property binding. We need to sort the keys in a specific order so
-     * we process the binding in a way that allows us to walk down the OGNL object graph and build empty nodes on the
-     * fly, and as well handle map/list and array types as well.
-     */
-    private static final class PropertyBindingKeyComparator implements Comparator<String> {
-
-        private final Map<String, Object> map;
-
-        private PropertyBindingKeyComparator(Map<String, Object> map) {
-            this.map = map;
-        }
-
-        @Override
-        public int compare(String o1, String o2) {
-            // 1) sort by nested level (shortest OGNL graph first)
-            int n1 = StringHelper.countChar(o1, '.');
-            int n2 = StringHelper.countChar(o2, '.');
-            if (n1 != n2) {
-                return Integer.compare(n1, n2);
-            }
-            // 2) sort by reference (as it may refer to other beans in the OGNL graph)
-            Object v1 = map.get(o1);
-            Object v2 = map.get(o2);
-            boolean ref1 = v1 instanceof String && ((String) v1).startsWith("#");
-            boolean ref2 = v2 instanceof String && ((String) v2).startsWith("#");
-            if (ref1 != ref2) {
-                return Boolean.compare(ref1, ref2);
-            }
-            // 3) sort by name
-            return o1.compareTo(o2);
-        }
-    }
-
-    /**
      * Binds the properties with the given prefix to the target object, and removes the property that was bound from
      * properties. Note that the prefix is removed from the key before the property is bound.
      *
@@ -798,8 +401,7 @@ public final class PropertyBindingSupport {
 
         if (configurer == null) {
             // do we have a configurer by any chance
-            configurer = camelContext.adapt(ExtendedCamelContext.class).getConfigurerResolver()
-                    .resolvePropertyConfigurer(newClass.getName(), camelContext);
+            configurer = PropertyConfigurerHelper.resolvePropertyConfigurer(camelContext, newClass);
         }
 
         // we should only walk and create OGNL path for the middle graph
@@ -862,11 +464,9 @@ public final class PropertyBindingSupport {
                     Class<?> collectionType = (Class<?>) ((PropertyConfigurerGetter) configurer)
                             .getCollectionValueType(newTarget, undashKey(key), ignoreCase);
                     if (collectionType != null) {
-                        configurer = camelContext.adapt(ExtendedCamelContext.class).getConfigurerResolver()
-                                .resolvePropertyConfigurer(collectionType.getName(), camelContext);
+                        configurer = PropertyConfigurerHelper.resolvePropertyConfigurer(camelContext, collectionType);
                     } else {
-                        configurer = camelContext.adapt(ExtendedCamelContext.class).getConfigurerResolver()
-                                .resolvePropertyConfigurer(prop.getClass().getName(), camelContext);
+                        configurer = PropertyConfigurerHelper.resolvePropertyConfigurer(camelContext, prop.getClass());
                     }
                 }
                 // prepare for next iterator
@@ -2052,4 +1652,379 @@ public final class PropertyBindingSupport {
         return key;
     }
 
+    @FunctionalInterface
+    public interface OnAutowiring {
+
+        /**
+         * Callback when a property was autowired on a bean
+         *
+         * @param target       the targeted bean
+         * @param propertyName the name of the property
+         * @param propertyType the type of the property
+         * @param value        the property value
+         */
+        void onAutowire(Object target, String propertyName, Class propertyType, Object value);
+
+    }
+
+    /**
+     * To use a fluent builder style to configure this property binding support.
+     */
+    public static class Builder {
+
+        private CamelContext camelContext;
+        private Object target;
+        private Map<String, Object> properties;
+        private boolean removeParameters = true;
+        private boolean flattenProperties;
+        private boolean mandatory;
+        private boolean nesting = true;
+        private boolean deepNesting = true;
+        private boolean reference = true;
+        private boolean placeholder = true;
+        private boolean fluentBuilder = true;
+        private boolean allowPrivateSetter = true;
+        private boolean ignoreCase;
+        private String optionPrefix;
+        private boolean reflection = true;
+        private PropertyConfigurer configurer;
+
+        /**
+         * CamelContext to be used
+         */
+        public Builder withCamelContext(CamelContext camelContext) {
+            this.camelContext = camelContext;
+            return this;
+        }
+
+        /**
+         * Target object that should have parameters bound
+         */
+        public Builder withTarget(Object target) {
+            this.target = target;
+            return this;
+        }
+
+        /**
+         * The properties to use for binding
+         */
+        public Builder withProperties(Map<String, Object> properties) {
+            if (this.properties == null) {
+                this.properties = properties;
+            } else {
+                // there may be existing options so add those if missing
+                // we need to mutate existing as we are may be removing bound properties
+                this.properties.forEach(properties::putIfAbsent);
+                this.properties = properties;
+            }
+            return this;
+        }
+
+        /**
+         * Adds property to use for binding
+         */
+        public Builder withProperty(String key, Object value) {
+            if (this.properties == null) {
+                this.properties = new LinkedHashMap<>();
+            }
+            this.properties.put(key, value);
+            return this;
+        }
+
+        /**
+         * Whether parameters should be removed when its bound
+         */
+        public Builder withRemoveParameters(boolean removeParameters) {
+            this.removeParameters = removeParameters;
+            return this;
+        }
+
+        /**
+         * Whether properties should be flattened (when properties is a map of maps).
+         */
+        public Builder withFlattenProperties(boolean flattenProperties) {
+            this.flattenProperties = flattenProperties;
+            return this;
+        }
+
+        /**
+         * Whether all parameters should be mandatory and successfully bound
+         */
+        public Builder withMandatory(boolean mandatory) {
+            this.mandatory = mandatory;
+            return this;
+        }
+
+        /**
+         * Whether nesting is in use
+         */
+        public Builder withNesting(boolean nesting) {
+            this.nesting = nesting;
+            return this;
+        }
+
+        /**
+         * Whether deep nesting is in use, where Camel will attempt to walk as deep as possible by creating new objects
+         * in the OGNL graph if a property has a setter and the object can be created from a default no-arg constructor.
+         */
+        public Builder withDeepNesting(boolean deepNesting) {
+            this.deepNesting = deepNesting;
+            return this;
+        }
+
+        /**
+         * Whether reference parameter (syntax starts with #) is in use
+         */
+        public Builder withReference(boolean reference) {
+            this.reference = reference;
+            return this;
+        }
+
+        /**
+         * Whether to use Camels property placeholder to resolve placeholders on keys and values
+         */
+        public Builder withPlaceholder(boolean placeholder) {
+            this.placeholder = placeholder;
+            return this;
+        }
+
+        /**
+         * Whether fluent builder is allowed as a valid getter/setter
+         */
+        public Builder withFluentBuilder(boolean fluentBuilder) {
+            this.fluentBuilder = fluentBuilder;
+            return this;
+        }
+
+        /**
+         * Whether properties should be filtered by prefix. * Note that the prefix is removed from the key before the
+         * property is bound.
+         */
+        public Builder withAllowPrivateSetter(boolean allowPrivateSetter) {
+            this.allowPrivateSetter = allowPrivateSetter;
+            return this;
+        }
+
+        /**
+         * Whether to ignore case in the property names (keys).
+         */
+        public Builder withIgnoreCase(boolean ignoreCase) {
+            this.ignoreCase = ignoreCase;
+            return this;
+        }
+
+        /**
+         * Whether properties should be filtered by prefix. Note that the prefix is removed from the key before the
+         * property is bound.
+         */
+        public Builder withOptionPrefix(String optionPrefix) {
+            this.optionPrefix = optionPrefix;
+            return this;
+        }
+
+        /**
+         * Whether to use the configurer to configure the properties.
+         */
+        public Builder withConfigurer(PropertyConfigurer configurer) {
+            this.configurer = configurer;
+            return this;
+        }
+
+        /**
+         * Whether to allow using reflection (when there is no configurer available).
+         */
+        public Builder withReflection(boolean reflection) {
+            this.reflection = reflection;
+            return this;
+        }
+
+        /**
+         * Binds the properties to the target object, and removes the property that was bound from properties.
+         *
+         * @return true if one or more properties was bound
+         */
+        public boolean bind() {
+            // mandatory parameters
+            org.apache.camel.util.ObjectHelper.notNull(camelContext, "camelContext");
+            org.apache.camel.util.ObjectHelper.notNull(target, "target");
+
+            if (properties == null || properties.isEmpty()) {
+                return false;
+            }
+
+            return doBindProperties(camelContext, target, removeParameters ? properties : new HashMap<>(properties),
+                    optionPrefix, ignoreCase, removeParameters, flattenProperties, mandatory,
+                    nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer);
+        }
+
+        /**
+         * Binds the properties to the target object, and removes the property that was bound from properties.
+         *
+         * @param  camelContext the camel context
+         * @param  target       the target object
+         * @param  properties   the properties where the bound properties will be removed from
+         * @return              true if one or more properties was bound
+         */
+        public boolean bind(CamelContext camelContext, Object target, Map<String, Object> properties) {
+            CamelContext context = camelContext != null ? camelContext : this.camelContext;
+            Object obj = target != null ? target : this.target;
+            Map<String, Object> prop = properties != null ? properties : this.properties;
+
+            return doBindProperties(context, obj, removeParameters ? prop : new HashMap<>(prop),
+                    optionPrefix, ignoreCase, removeParameters, flattenProperties, mandatory,
+                    nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer);
+        }
+
+        /**
+         * Binds the property to the target object.
+         *
+         * @param  camelContext the camel context
+         * @param  target       the target object
+         * @param  key          the property key
+         * @param  value        the property value
+         * @return              true if the property was bound
+         */
+        public boolean bind(CamelContext camelContext, Object target, String key, Object value) {
+            Map<String, Object> properties = new HashMap<>(1);
+            properties.put(key, value);
+
+            return doBindProperties(camelContext, target, properties, optionPrefix, ignoreCase, true, false, mandatory,
+                    nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer);
+        }
+
+    }
+
+    /**
+     * Used for making it easier to support using option prefix in property binding and to remove the bound properties
+     * from the input map.
+     */
+    private static class OptionPrefixMap extends LinkedHashMap<String, Object> {
+
+        private final String optionPrefix;
+        private final Map<String, Object> originalMap;
+
+        public OptionPrefixMap(Map<String, Object> map, String optionPrefix) {
+            this.originalMap = map;
+            this.optionPrefix = optionPrefix;
+            // copy from original map into our map without the option prefix
+            map.forEach((k, v) -> {
+                if (startsWithIgnoreCase(k, optionPrefix)) {
+                    put(k.substring(optionPrefix.length()), v);
+                } else if (startsWithIgnoreCase(k, "?" + optionPrefix)) {
+                    put(k.substring(optionPrefix.length() + 1), v);
+                }
+            });
+        }
+
+        @Override
+        public Object remove(Object key) {
+            // we only need to care about the remove method,
+            // so we can remove the corresponding key from the original map
+            Set<String> toBeRemoved = new HashSet<>();
+            originalMap.forEach((k, v) -> {
+                if (startsWithIgnoreCase(k, optionPrefix)) {
+                    toBeRemoved.add(k);
+                } else if (startsWithIgnoreCase(k, "?" + optionPrefix)) {
+                    toBeRemoved.add(k);
+                }
+            });
+            toBeRemoved.forEach(originalMap::remove);
+
+            return super.remove(key);
+        }
+
+    }
+
+    /**
+     * Used for flatten properties when they are a map of maps
+     */
+    private static class FlattenMap extends LinkedHashMap<String, Object> {
+
+        private final Map<String, Object> originalMap;
+
+        public FlattenMap(Map<String, Object> map) {
+            this.originalMap = map;
+            flatten("", originalMap);
+        }
+
+        @SuppressWarnings("unchecked")
+        private void flatten(String prefix, Map<?, Object> map) {
+            for (Map.Entry<?, Object> entry : map.entrySet()) {
+                String key = entry.getKey().toString();
+                boolean optional = key.startsWith("?");
+                if (optional) {
+                    key = key.substring(1);
+                }
+                Object value = entry.getValue();
+                String keyPrefix = (optional ? "?" : "") + (prefix.isEmpty() ? key : prefix + "." + key);
+                if (value instanceof Map) {
+                    flatten(keyPrefix, (Map<?, Object>) value);
+                } else {
+                    put(keyPrefix, value);
+                }
+            }
+        }
+
+        @Override
+        public Object remove(Object key) {
+            // we only need to care about the remove method,
+            // so we can remove the corresponding key from the original map
+
+            // walk key with dots to remove right node
+            String[] parts = key.toString().split("\\.");
+            Map map = originalMap;
+            for (int i = 0; i < parts.length; i++) {
+                String part = parts[i];
+                Object obj = map.get(part);
+                if (i == parts.length - 1) {
+                    map.remove(part);
+                } else if (obj instanceof Map) {
+                    map = (Map) obj;
+                }
+            }
+
+            // remove empty middle maps
+            Object answer = super.remove(key);
+            if (super.isEmpty()) {
+                originalMap.clear();
+            }
+            return answer;
+        }
+
+    }
+
+    /**
+     * Used for sorting the property keys when doing property binding. We need to sort the keys in a specific order so
+     * we process the binding in a way that allows us to walk down the OGNL object graph and build empty nodes on the
+     * fly, and as well handle map/list and array types as well.
+     */
+    private static final class PropertyBindingKeyComparator implements Comparator<String> {
+
+        private final Map<String, Object> map;
+
+        private PropertyBindingKeyComparator(Map<String, Object> map) {
+            this.map = map;
+        }
+
+        @Override
+        public int compare(String o1, String o2) {
+            // 1) sort by nested level (shortest OGNL graph first)
+            int n1 = StringHelper.countChar(o1, '.');
+            int n2 = StringHelper.countChar(o2, '.');
+            if (n1 != n2) {
+                return Integer.compare(n1, n2);
+            }
+            // 2) sort by reference (as it may refer to other beans in the OGNL graph)
+            Object v1 = map.get(o1);
+            Object v2 = map.get(o2);
+            boolean ref1 = v1 instanceof String && ((String) v1).startsWith("#");
+            boolean ref2 = v2 instanceof String && ((String) v2).startsWith("#");
+            if (ref1 != ref2) {
+                return Boolean.compare(ref1, ref2);
+            }
+            // 3) sort by name
+            return o1.compareTo(o2);
+        }
+    }
+
 }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/PropertyConfigurerHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/PropertyConfigurerHelper.java
index 97b7ee2..b04397c 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/PropertyConfigurerHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/PropertyConfigurerHelper.java
@@ -17,8 +17,10 @@
 package org.apache.camel.support;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
 import org.apache.camel.ExtendedCamelContext;
-import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.spi.PropertyConfigurer;
+import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.ObjectHelper;
 
 /**
@@ -39,13 +41,55 @@ public final class PropertyConfigurerHelper {
      * @param  target  the target object for which we need a {@link org.apache.camel.spi.PropertyConfigurer}
      * @return         the resolved configurer, or <tt>null</tt> if no configurer could be found
      */
-    public static GeneratedPropertyConfigurer resolvePropertyConfigurer(CamelContext context, Object target) {
+    public static PropertyConfigurer resolvePropertyConfigurer(CamelContext context, Object target) {
         ObjectHelper.notNull(target, "target");
         ObjectHelper.notNull(context, "context");
 
+        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();
+            if (target instanceof ExtendedCamelContext) {
+                // special for camel context itself as we have an extended configurer
+                name = ExtendedCamelContext.class.getName();
+            }
+
+            // see if there is a configurer for it
+            configurer = context.adapt(ExtendedCamelContext.class)
+                    .getConfigurerResolver()
+                    .resolvePropertyConfigurer(name, context);
+        }
+
+        return configurer;
+    }
+
+    /**
+     * Resolves the given configurer.
+     *
+     * @param  context    the camel context
+     * @param  targetType the target object type for which we need a {@link org.apache.camel.spi.PropertyConfigurer}
+     * @return            the resolved configurer, or <tt>null</tt> if no configurer could be found
+     */
+    public static PropertyConfigurer resolvePropertyConfigurer(CamelContext context, Class<?> targetType) {
+        ObjectHelper.notNull(targetType, "targetType");
+        ObjectHelper.notNull(context, "context");
+
+        String name = targetType.getName();
+        if (ExtendedCamelContext.class.isAssignableFrom(targetType)) {
+            // special for camel context itself as we have an extended configurer
+            name = ExtendedCamelContext.class.getName();
+        }
+
+        // see if there is a configurer for it
         return context.adapt(ExtendedCamelContext.class)
                 .getConfigurerResolver()
-                .resolvePropertyConfigurer(target.getClass().getName(), context);
+                .resolvePropertyConfigurer(name, context);
     }
 
     /**
@@ -60,7 +104,7 @@ public final class PropertyConfigurerHelper {
         ObjectHelper.notNull(target, "target");
         ObjectHelper.notNull(context, "context");
 
-        GeneratedPropertyConfigurer configurer = resolvePropertyConfigurer(context, target);
+        PropertyConfigurer configurer = resolvePropertyConfigurer(context, target);
         if (type.isInstance(configurer)) {
             return type.cast(configurer);
         }
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/CollectionHelper.java b/core/camel-util/src/main/java/org/apache/camel/util/CollectionHelper.java
index 39ccc3d..89dc186 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/CollectionHelper.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/CollectionHelper.java
@@ -164,6 +164,7 @@ public final class CollectionHelper {
     /**
      * Build a map from varargs.
      */
+    @SuppressWarnings("unchecked")
     public static <K, V> Map<K, V> mapOf(Supplier<Map<K, V>> creator, K key, V value, Object... keyVals) {
         Map<K, V> map = creator.get();
         map.put(key, value);


[camel] 03/03: CAMEL-14672: add migration instruction

Posted by lb...@apache.org.
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

commit 3267b2f0961d2edf7d95db57e16372324be53e43
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Fri Sep 4 14:34:25 2020 +0200

    CAMEL-14672: add migration instruction
---
 .../ROOT/pages/camel-3x-upgrade-guide-3_6.adoc     | 86 ++++++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_6.adoc b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_6.adoc
index c8df30f..07a38bb 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_6.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_6.adoc
@@ -14,3 +14,89 @@ signatures in the Camel components which may change some of the existing singatu
 === Camel Karaf
 
 The following features has been removed due they become not compatible with OSGi: `camel-atmosphere-websocket`.
+
+=== Customizers
+
+The customizer, which are objects used to configure some of the Camel services such as component, language ad data formats, that were previously limited to Camel Spring Boot, are now consistently used across runtimes. 
+To make that possible, the interfaces have been changed and they do not use generics anymore.
+
+Impacted interfaces:
+
+* org.apache.camel.spi.ComponentCustomizer
+* org.apache.camel.spi.LanguageCustomizer
+* org.apache.camel.spi.DataFormatCustomizer
+
+As example the signature of the `ComponentCustomizer` interface in Camel 3.5 is:
+
+[source,java]
+----
+@FunctionalInterface
+public interface ComponentCustomizer<T extends Component> {
+    /**
+     * Customize the specified {@link Component}.
+     *
+     * @param component the component to customize
+     */
+    void customize(T component);
+}
+----
+
+And below the Camel 3.6 version:
+
+[source,java]
+----
+@FunctionalInterface
+public interface ComponentCustomizer {
+    /**
+     * Customize the specified {@link Component}.
+     *
+     * @param name   the unique name of the component
+     * @param target the component to configure
+     */
+    void configure(String name, Component target);
+
+    /**
+     * Checks whether this customizer should be applied to the given {@link Component}.
+     *
+     * @param  name   the unique name of the component
+     * @param  target the component to configure
+     * @return        <tt>true</tt> if the customizer should be applied
+     */
+    default boolean isEnabled(String name, Component target) {
+        return true;
+    }
+
+    @Override
+    default int getOrder() {
+        return 0;
+    }
+}
+----
+
+As the customizer are now taken into account as part of the standard lifecycle of the `CamelContext`, to programmatically configure a component, it is enough to register the appropriate customizer in the `Registry` as example:
+
+[source,java]
+----
+public class Application {
+
+    public static void main(String args) throws Exception {
+        Main main = new Main();
+        main..addConfigurationClass(MyConfiguration.class);
+        main.run(args);
+    }
+
+    public static class MyConfiguration {
+        @BindToRegistry
+        public ComponentCustomizer logCustomizer() {
+            // Use a fluent Component Customizer builder to ease the process of creating an customizer.
+            return ComponentCustomizer.builder(LogComponent.class)
+                    .build(component -> component.setExchangeFormatter(new DefaultExchangeFormatter()));
+        }
+    }
+}
+----
+
+[NOTE]
+====
+As coneguence of this change, the Camel Spring Boot starters have been ameneded to use Customizers instead of creating instances of components, languages or data formats.
+====


[camel] 02/03: CAMEL-14672: cleanup component resolution

Posted by lb...@apache.org.
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

commit d96c2c85cfa09bb5aec6f3cbcc4af59a2bc95707
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Wed Sep 2 11:24:49 2020 +0200

    CAMEL-14672: cleanup component resolution
---
 .../apache/camel/impl/engine/AbstractCamelContext.java | 18 +++++-------------
 .../camel/impl/engine/DefaultComponentResolver.java    |  7 -------
 2 files changed, 5 insertions(+), 20 deletions(-)

diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index 5a6a30f..960a946 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -611,7 +611,11 @@ public abstract class AbstractCamelContext extends BaseService
                 // See https://issues.apache.org/jira/browse/CAMEL-11225
                 componentsInCreation.get().add(name);
 
-                component = getComponentResolver().resolveComponent(name, getCamelContextReference());
+                component = ResolverHelper.lookupComponentInRegistryWithFallback(getCamelContextReference(), name);
+                if (component == null) {
+                    component = getComponentResolver().resolveComponent(name, getCamelContextReference());
+                }
+
                 if (component != null) {
                     component.setCamelContext(getCamelContextReference());
                     component.build();
@@ -640,18 +644,6 @@ public abstract class AbstractCamelContext extends BaseService
         }
     }
 
-    public Component resolveComponent(String name) {
-        Component answer = hasComponent(name);
-        if (answer == null) {
-            try {
-                answer = getComponentResolver().resolveComponent(name, this);
-            } catch (Exception e) {
-                throw new RuntimeCamelException("Cannot resolve component: " + name, e);
-            }
-        }
-        return answer;
-    }
-
     // Endpoint Management Methods
     // -----------------------------------------------------------------------
 
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultComponentResolver.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultComponentResolver.java
index c4c4371..c46c22d 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultComponentResolver.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultComponentResolver.java
@@ -24,7 +24,6 @@ import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.NoFactoryAvailableException;
 import org.apache.camel.spi.ComponentResolver;
 import org.apache.camel.spi.FactoryFinder;
-import org.apache.camel.support.ResolverHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,12 +42,6 @@ public class DefaultComponentResolver implements ComponentResolver {
 
     @Override
     public Component resolveComponent(String name, CamelContext context) {
-        // lookup in registry first
-        Component componentReg = ResolverHelper.lookupComponentInRegistryWithFallback(context, name);
-        if (componentReg != null) {
-            return componentReg;
-        }
-
         // not in registry then use component factory
         Class<?> type;
         try {