Fix #617 Registerable and discoverable Camel services
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/BuildProcessor.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/BuildProcessor.java
index 594ec28..63c6e8d 100644
--- a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/BuildProcessor.java
+++ b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/BuildProcessor.java
@@ -21,7 +21,6 @@
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -54,8 +53,11 @@
 import org.apache.camel.quarkus.core.CamelProducers;
 import org.apache.camel.quarkus.core.CamelRecorder;
 import org.apache.camel.quarkus.core.CoreAttachmentsRecorder;
+import org.apache.camel.quarkus.core.FastFactoryFinderResolver.Builder;
 import org.apache.camel.quarkus.core.Flags;
 import org.apache.camel.quarkus.core.UploadAttacher;
+import org.apache.camel.quarkus.core.deployment.CamelServicePatternBuildItem.CamelServiceDestination;
+import org.apache.camel.quarkus.core.deployment.util.PathFilter;
 import org.apache.camel.quarkus.support.common.CamelCapabilities;
 import org.apache.camel.spi.TypeConverterLoader;
 import org.apache.camel.spi.TypeConverterRegistry;
@@ -125,6 +127,68 @@
                     new CamelServiceFilterBuildItem(CamelServiceFilter.forService("properties-component-factory")));
         }
 
+        @BuildStep
+        void coreServicePatterns(BuildProducer<CamelServicePatternBuildItem> services) {
+
+            services.produce(new CamelServicePatternBuildItem(
+                    CamelServiceDestination.REGISTRY,
+                    true,
+                    "META-INF/services/org/apache/camel/component/*",
+                    "META-INF/services/org/apache/camel/language/*",
+                    "META-INF/services/org/apache/camel/dataformat/*"));
+
+            services.produce(new CamelServicePatternBuildItem(
+                    CamelServiceDestination.DISCOVERY,
+                    true,
+                    "META-INF/services/org/apache/camel/*",
+                    "META-INF/services/org/apache/camel/management/*",
+                    "META-INF/services/org/apache/camel/model/*",
+                    "META-INF/services/org/apache/camel/configurer/*"));
+        }
+
+        @BuildStep
+        void userServicePatterns(
+                CamelConfig camelConfig,
+                BuildProducer<CamelServicePatternBuildItem> services) {
+
+            camelConfig.service.discovery.includePatterns.ifPresent(list -> services.produce(new CamelServicePatternBuildItem(
+                    CamelServiceDestination.DISCOVERY,
+                    true,
+                    list)));
+
+            camelConfig.service.discovery.excludePatterns.ifPresent(list -> services.produce(new CamelServicePatternBuildItem(
+                    CamelServiceDestination.DISCOVERY,
+                    false,
+                    list)));
+
+            camelConfig.service.registry.includePatterns.ifPresent(list -> services.produce(new CamelServicePatternBuildItem(
+                    CamelServiceDestination.REGISTRY,
+                    true,
+                    list)));
+
+            camelConfig.service.registry.excludePatterns.ifPresent(list -> services.produce(new CamelServicePatternBuildItem(
+                    CamelServiceDestination.REGISTRY,
+                    false,
+                    list)));
+        }
+
+        @BuildStep
+        void camelServices(
+                ApplicationArchivesBuildItem applicationArchives,
+                List<CamelServicePatternBuildItem> servicePatterns,
+                BuildProducer<CamelServiceBuildItem> camelServices) {
+
+            final PathFilter pathFilter = servicePatterns.stream()
+                    .filter(patterns -> patterns.getDestination() == CamelServiceDestination.DISCOVERY)
+                    .collect(
+                            PathFilter.Builder::new,
+                            (bldr, patterns) -> bldr.patterns(patterns.isInclude(), patterns.getPatterns()),
+                            PathFilter.Builder::combine)
+                    .build();
+            CamelSupport.services(applicationArchives, pathFilter)
+                    .forEach(camelServices::produce);
+        }
+
         /*
          * Discover {@link TypeConverterLoader}.
          */
@@ -145,8 +209,8 @@
             // account even if it should not.
             //
             // TODO: we could add a filter to discard AnnotationTypeConverterLoader but maybe we should introduce
-            //       a marker interface like StaticTypeConverterLoader for loaders that do not require to perform
-            //       any discovery at runtime.
+            // a marker interface like StaticTypeConverterLoader for loaders that do not require to perform
+            // any discovery at runtime.
             //
             for (ApplicationArchive archive : applicationArchives.getAllApplicationArchives()) {
                 Path path = archive.getArchiveRoot().resolve(BaseTypeConverterRegistry.META_INF_SERVICES_TYPE_CONVERTER_LOADER);
@@ -182,14 +246,26 @@
         CamelRegistryBuildItem registry(
                 CamelRecorder recorder,
                 RecorderContext recorderContext,
+                CamelConfig camelConfig,
                 ApplicationArchivesBuildItem applicationArchives,
                 ContainerBeansBuildItem containerBeans,
                 List<CamelBeanBuildItem> registryItems,
-                List<CamelServiceFilterBuildItem> serviceFilters) {
+                List<CamelServiceFilterBuildItem> serviceFilters,
+                List<CamelServicePatternBuildItem> servicePatterns) {
 
             final RuntimeValue<org.apache.camel.spi.Registry> registry = recorder.createRegistry();
 
-            CamelSupport.services(applicationArchives)
+            final PathFilter pathFilter = servicePatterns.stream()
+                    .filter(patterns -> patterns.getDestination() == CamelServiceDestination.REGISTRY)
+                    .collect(
+                            PathFilter.Builder::new,
+                            (builder, patterns) -> builder.patterns(patterns.isInclude(), patterns.getPatterns()),
+                            PathFilter.Builder::combine)
+                    .include(camelConfig.service.registry.includePatterns)
+                    .exclude(camelConfig.service.registry.excludePatterns)
+                    .build();
+
+            CamelSupport.services(applicationArchives, pathFilter)
                     .filter(si -> !containerBeans.getBeans().contains(si))
                     .filter(si -> {
                         //
@@ -268,6 +344,7 @@
                 CamelTypeConverterRegistryBuildItem typeConverterRegistry,
                 CamelModelJAXBContextFactoryBuildItem contextFactory,
                 CamelRoutesLoaderBuildItems.Xml xmlLoader,
+                CamelFactoryFinderResolverBuildItem factoryFinderResolverBuildItem,
                 BeanContainerBuildItem beanContainer) {
 
             RuntimeValue<CamelContext> context = recorder.createContext(
@@ -275,6 +352,7 @@
                     typeConverterRegistry.getRegistry(),
                     contextFactory.getContextFactory(),
                     xmlLoader.getLoader(),
+                    factoryFinderResolverBuildItem.getFactoryFinderResolver(),
                     beanContainer.getValue());
 
             return new CamelContextBuildItem(context);
@@ -312,6 +390,27 @@
 
             return new CamelRuntimeRegistryBuildItem(registry.getRegistry());
         }
+
+        @Record(ExecutionTime.STATIC_INIT)
+        @BuildStep
+        CamelFactoryFinderResolverBuildItem factoryFinderResolver(
+                RecorderContext recorderContext,
+                CamelRecorder recorder,
+                List<CamelServiceBuildItem> camelServices) {
+
+            RuntimeValue<Builder> builder = recorder.factoryFinderResolverBuilder();
+
+            camelServices.stream()
+                    .forEach(service -> {
+                        recorder.factoryFinderResolverEntry(
+                                builder,
+                                service.path.toString(),
+                                recorderContext.classProxy(service.type));
+                    });
+
+            return new CamelFactoryFinderResolverBuildItem(recorder.factoryFinderResolver(builder));
+        }
+
     }
 
     /*
@@ -343,10 +442,10 @@
                     // public and non-abstract
                     .filter(ci -> ((ci.flags() & (Modifier.ABSTRACT | Modifier.PUBLIC)) == Modifier.PUBLIC))
                     .map(ClassInfo::name)
-                    .filter(dotName -> CamelSupport.isPathIncluded(
-                            dotName.toString('/'),
-                            config.main.routesDiscovery.excludePatterns.orElse(Collections.emptyList()),
-                            config.main.routesDiscovery.includePatterns.orElse(Collections.emptyList())))
+                    .filter(new PathFilter.Builder()
+                            .exclude(config.main.routesDiscovery.excludePatterns)
+                            .include(config.main.routesDiscovery.includePatterns)
+                            .build().asDotNamePredicate())
                     .map(CamelRoutesBuilderClassBuildItem::new)
                     .collect(Collectors.toList());
         }
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelFactoryFinderResolverBuildItem.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelFactoryFinderResolverBuildItem.java
new file mode 100644
index 0000000..be9bbfa
--- /dev/null
+++ b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelFactoryFinderResolverBuildItem.java
@@ -0,0 +1,36 @@
+/*
+ * 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.quarkus.core.deployment;
+
+import io.quarkus.builder.item.SimpleBuildItem;
+import io.quarkus.runtime.RuntimeValue;
+import org.apache.camel.spi.FactoryFinderResolver;
+
+/**
+ * A {@link SimpleBuildItem} holding a {@link FastFactoryFinderResolver} {@link RuntimeValue}.
+ */
+public final class CamelFactoryFinderResolverBuildItem extends SimpleBuildItem {
+    private final RuntimeValue<FactoryFinderResolver> factoryFinderResolver;
+
+    public CamelFactoryFinderResolverBuildItem(RuntimeValue<FactoryFinderResolver> factoryFinderResolverBuilder) {
+        this.factoryFinderResolver = factoryFinderResolverBuilder;
+    }
+
+    public RuntimeValue<FactoryFinderResolver> getFactoryFinderResolver() {
+        return factoryFinderResolver;
+    }
+}
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceBuildItem.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceBuildItem.java
new file mode 100644
index 0000000..b460bf0
--- /dev/null
+++ b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceBuildItem.java
@@ -0,0 +1,90 @@
+/*
+ * 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.quarkus.core.deployment;
+
+import java.nio.file.Path;
+import java.util.Objects;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * A {@link MultiBuildItem} holding information about a service defined in a property file somewhere under
+ * {@code META-INF/services/org/apache/camel}.
+ */
+public final class CamelServiceBuildItem extends MultiBuildItem implements CamelBeanInfo {
+
+    final Path path;
+
+    final String name;
+
+    final String type;
+
+    public CamelServiceBuildItem(Path path, String type) {
+        this(path, path.getFileName().toString(), type);
+    }
+
+    public CamelServiceBuildItem(Path path, String name, String type) {
+        Objects.requireNonNull(path, "path");
+        Objects.requireNonNull(type, () -> "type for path " + path);
+        this.path = path;
+        this.name = name;
+        this.type = type;
+    }
+
+    /**
+     * @return the path of the service file like {@code META-INF/services/org/apache/camel/component/file}.
+     */
+    public Path getPath() {
+        return path;
+    }
+
+    /**
+     * @return the name under which this service will be registered in the Camel registry.
+     *         This name may or may not be the same as the last segment of {@link #path}.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @return The fully qualified class name of the service.
+     */
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        /* This must be the same as in other implementations of CamelBeanInfo */
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof CamelBeanInfo)) {
+            return false;
+        }
+        CamelBeanInfo info = (CamelBeanInfo) o;
+        return Objects.equals(getName(), info.getName()) &&
+                Objects.equals(getType(), info.getType());
+    }
+
+    @Override
+    public int hashCode() {
+        /* This must be the same as in other implementations of CamelBeanInfo */
+        return Objects.hash(getName(), getType());
+    }
+
+}
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceFilter.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceFilter.java
index 87c96a9..9e9c508 100644
--- a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceFilter.java
+++ b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceFilter.java
@@ -19,7 +19,7 @@
 import java.util.function.Predicate;
 
 @FunctionalInterface
-public interface CamelServiceFilter extends Predicate<CamelServiceInfo> {
+public interface CamelServiceFilter extends Predicate<CamelServiceBuildItem> {
     String CAMEL_SERVICE_BASE_PATH = "META-INF/services/org/apache/camel";
 
     static CamelServiceFilter forPathEndingWith(String path) {
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceInfo.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceInfo.java
deleted file mode 100644
index 8c46e13..0000000
--- a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServiceInfo.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.quarkus.core.deployment;
-
-import java.nio.file.Path;
-import java.util.Objects;
-
-/**
- * Utility class to describe a camel service which is a result of reading
- * services from resources belonging to META-INF/services/org/apache/camel.
- */
-public class CamelServiceInfo implements CamelBeanInfo {
-    /**
-     * The path of the service file like META-INF/services/org/apache/camel/component/file.
-     */
-    public final Path path;
-
-    /**
-     * The name of the service entry which is derived from the service path. As example the
-     * name for a service with path <code>META-INF/services/org/apache/camel/component/file</code>
-     * will be <code>file</code>
-     */
-    public final String name;
-
-    /**
-     * The full qualified class name of the service.
-     */
-    public final String type;
-
-    public CamelServiceInfo(Path path, String type) {
-        this(path, path.getFileName().toString(), type);
-    }
-
-    public CamelServiceInfo(Path path, String name, String type) {
-        this.path = path;
-        this.name = name;
-        this.type = type;
-    }
-
-    @Override
-    public String getName() {
-        return this.name;
-    }
-
-    @Override
-    public String getType() {
-        return this.type;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof CamelBeanInfo)) {
-            return false;
-        }
-        CamelBeanInfo info = (CamelBeanInfo) o;
-        return Objects.equals(getName(), info.getName()) &&
-                Objects.equals(getType(), info.getType());
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(getName(), getType());
-    }
-
-    @Override
-    public String toString() {
-        return "ServiceInfo{"
-                + "path='" + path.toString() + '\''
-                + ", name=" + name
-                + ", type=" + type
-                + '}';
-    }
-}
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServicePatternBuildItem.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServicePatternBuildItem.java
new file mode 100644
index 0000000..906b21e
--- /dev/null
+++ b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelServicePatternBuildItem.java
@@ -0,0 +1,84 @@
+/*
+ * 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.quarkus.core.deployment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * A {@link MultiBuildItem} holding a collection of path patterns to select files under
+ * {@code META-INF/services/org/apache/camel} which define discoverable Camel services.
+ */
+public final class CamelServicePatternBuildItem extends MultiBuildItem {
+
+    /**
+     * Where a Camel service should be further processed
+     */
+    public enum CamelServiceDestination {
+        /** Service marked with {@link #DISCOVERY} should be made discoverable via FactoryFinder mechanism */
+        DISCOVERY,
+        /** Service marked with {@link #DISCOVERY} should be registered in the Camel registry */
+        REGISTRY
+    }
+
+    public CamelServicePatternBuildItem(CamelServiceDestination destination, boolean include,
+            String... patterns) {
+        this(destination, include, Arrays.asList(patterns));
+    }
+
+    public CamelServicePatternBuildItem(CamelServiceDestination destination, boolean include,
+            Collection<String> patterns) {
+        this.destination = destination;
+        this.include = include;
+        this.patterns = Collections.unmodifiableList(new ArrayList<>(patterns));
+    }
+
+    private final CamelServiceDestination destination;
+
+    private final boolean include;
+
+    private final List<String> patterns;
+
+    /**
+     * @return a {@link CamelServiceDestination} that says where this service should be further processed. See
+     *         {@link CamelServiceDestination} and its members.
+     */
+    public CamelServiceDestination getDestination() {
+        return destination;
+    }
+
+    /**
+     * @return {@code true} if the {@link #patterns} should be interpreted as includes; otherwise the {@link #patterns}
+     *         should be interpreted as excludes
+     */
+    public boolean isInclude() {
+        return include;
+    }
+
+    /**
+     * @return a {@link List} or Ant-like path patterns. By convention these
+     */
+    public List<String> getPatterns() {
+        return patterns;
+    }
+
+}
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelSupport.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelSupport.java
index f8f7d10..296229d 100644
--- a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelSupport.java
+++ b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelSupport.java
@@ -22,19 +22,15 @@
 import java.lang.reflect.Modifier;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.AbstractMap;
 import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
-import org.apache.camel.util.AntPathMatcher;
-import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.quarkus.core.deployment.util.PathFilter;
 import org.jboss.jandex.ClassInfo;
 
 public final class CamelSupport {
@@ -60,29 +56,6 @@
         }
     }
 
-    public static boolean isPathIncluded(String path, Collection<String> excludePatterns, Collection<String> includePatterns) {
-        final AntPathMatcher matcher = new AntPathMatcher();
-
-        if (ObjectHelper.isEmpty(excludePatterns) && ObjectHelper.isEmpty(includePatterns)) {
-            return true;
-        }
-
-        // same logic as  org.apache.camel.main.DefaultRoutesCollector so exclude
-        // take precedence over include
-        for (String part : excludePatterns) {
-            if (matcher.match(part.trim(), path)) {
-                return false;
-            }
-        }
-        for (String part : includePatterns) {
-            if (matcher.match(part.trim(), path)) {
-                return true;
-            }
-        }
-
-        return ObjectHelper.isEmpty(includePatterns);
-    }
-
     public static Stream<Path> resources(ApplicationArchivesBuildItem archives, String path) {
         return archives.getAllApplicationArchives().stream()
                 .map(arch -> arch.getArchiveRoot().resolve(path))
@@ -91,29 +64,22 @@
                 .filter(Files::isRegularFile);
     }
 
-    public static Stream<CamelServiceInfo> services(ApplicationArchivesBuildItem applicationArchivesBuildItem) {
-        return CamelSupport.resources(applicationArchivesBuildItem, CamelSupport.CAMEL_SERVICE_BASE_PATH)
-                .map(CamelSupport::services)
-                .flatMap(Collection::stream);
+    public static Stream<CamelServiceBuildItem> services(ApplicationArchivesBuildItem archives, PathFilter pathFilter) {
+        return resources(archives, CAMEL_SERVICE_BASE_PATH)
+                .filter(pathFilter.asPathPredicate())
+                .map(path -> new AbstractMap.SimpleImmutableEntry<>(path, readProperties(path)))
+                .filter(entry -> entry.getValue().getProperty("class") != null)
+                .map(entry -> new CamelServiceBuildItem(entry.getKey(), entry.getValue().getProperty("class")));
     }
 
-    private static List<CamelServiceInfo> services(Path p) {
-        List<CamelServiceInfo> answer = new ArrayList<>();
-
-        try (InputStream is = Files.newInputStream(p)) {
-            Properties props = new Properties();
-            props.load(is);
-            for (Map.Entry<Object, Object> entry : props.entrySet()) {
-                String k = entry.getKey().toString();
-                if (k.equals("class")) {
-                    answer.add(new CamelServiceInfo(p, entry.getValue().toString()));
-                }
-            }
-        } catch (Exception e) {
-            throw new RuntimeException(e);
+    private static Properties readProperties(Path path) {
+        try (InputStream in = Files.newInputStream(path)) {
+            final Properties result = new Properties();
+            result.load(in);
+            return result;
+        } catch (IOException e) {
+            throw new RuntimeException("Could not read " + path, e);
         }
-
-        return answer;
     }
 
     @SafeVarargs
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/NativeImageProcessor.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/NativeImageProcessor.java
index 9c9f346..034b191 100644
--- a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/NativeImageProcessor.java
+++ b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/NativeImageProcessor.java
@@ -16,14 +16,9 @@
  */
 package org.apache.camel.quarkus.core.deployment;
 
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
-import java.util.Properties;
 import java.util.stream.Collectors;
 
 import io.quarkus.deployment.annotations.BuildProducer;
@@ -41,6 +36,7 @@
 import org.apache.camel.Producer;
 import org.apache.camel.TypeConverter;
 import org.apache.camel.quarkus.core.Flags;
+import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.ExchangeFormatter;
 import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.ScheduledPollConsumerScheduler;
@@ -69,7 +65,8 @@
                 CamelContext.class,
                 StreamCachingStrategy.class,
                 StreamCachingStrategy.SpoolUsedHeapMemoryLimit.class,
-                PropertiesComponent.class);
+                PropertiesComponent.class,
+                DataFormat.class);
 
         @BuildStep
         void reflectiveItems(
@@ -125,36 +122,24 @@
         }
 
         @BuildStep
-        void resourcesAndServices(
+        void resources(
                 ApplicationArchivesBuildItem applicationArchivesBuildItem,
-                BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
                 BuildProducer<NativeImageResourceBuildItem> resource) {
 
             CamelSupport.resources(applicationArchivesBuildItem, "META-INF/maven/org.apache.camel/camel-base")
                     .forEach(p -> resource.produce(new NativeImageResourceBuildItem(p.toString().substring(1))));
-            CamelSupport.resources(applicationArchivesBuildItem, CamelSupport.CAMEL_SERVICE_BASE_PATH)
-                    .forEach(path -> addCamelService(path, reflectiveClass, resource));
         }
 
-        static void addCamelService(
-                Path p,
-                BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
-                BuildProducer<NativeImageResourceBuildItem> resource) {
-            try (InputStream is = Files.newInputStream(p)) {
-                Properties props = new Properties();
-                props.load(is);
-                for (Map.Entry<Object, Object> entry : props.entrySet()) {
-                    String k = entry.getKey().toString();
-                    if (k.equals("class")) {
-                        reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, entry.getValue().toString()));
-                    } else if (k.endsWith(".class")) {
-                        reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, entry.getValue().toString()));
-                        resource.produce(new NativeImageResourceBuildItem(p.toString().substring(1)));
-                    }
-                }
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
+        @BuildStep
+        void camelServices(
+                List<CamelServiceBuildItem> camelServices,
+                BuildProducer<ReflectiveClassBuildItem> reflectiveClass) {
+
+            camelServices.stream()
+                    .forEach(service -> {
+                        reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, service.type));
+                    });
+
         }
 
     }
diff --git a/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/util/PathFilter.java b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/util/PathFilter.java
new file mode 100644
index 0000000..4c6772d
--- /dev/null
+++ b/extensions/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/util/PathFilter.java
@@ -0,0 +1,156 @@
+/*
+ * 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.quarkus.core.deployment.util;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+import org.apache.camel.util.AntPathMatcher;
+import org.apache.camel.util.ObjectHelper;
+import org.jboss.jandex.DotName;
+
+/**
+ * A utility able to filter resource paths using Ant-like includes and excludes.
+ */
+public class PathFilter {
+    private final AntPathMatcher matcher = new AntPathMatcher();
+    private final List<String> includePatterns;
+    private final List<String> excludePatterns;
+    private final Predicate<String> stringPredicate;
+
+    PathFilter(List<String> includePatterns, List<String> excludePatterns) {
+        this.includePatterns = includePatterns;
+        this.excludePatterns = excludePatterns;
+
+        if (ObjectHelper.isEmpty(excludePatterns) && ObjectHelper.isEmpty(includePatterns)) {
+            this.stringPredicate = path -> true;
+        } else {
+            this.stringPredicate = path -> {
+                path = sanitize(path);
+                // same logic as  org.apache.camel.main.DefaultRoutesCollector so exclude
+                // take precedence over include
+                for (String part : excludePatterns) {
+                    if (matcher.match(part, path)) {
+                        return false;
+                    }
+                }
+                for (String part : includePatterns) {
+                    if (matcher.match(part, path)) {
+                        return true;
+                    }
+                }
+                return ObjectHelper.isEmpty(includePatterns);
+            };
+        }
+        ;
+    }
+
+    public Predicate<String> asStringPredicate() {
+        return stringPredicate;
+    }
+
+    public Predicate<DotName> asDotNamePredicate() {
+        if (ObjectHelper.isEmpty(excludePatterns) && ObjectHelper.isEmpty(includePatterns)) {
+            return dotName -> true;
+        } else {
+            return dotName -> stringPredicate.test(dotName.toString().replace('.', '/'));
+        }
+    }
+
+    public Predicate<Path> asPathPredicate() {
+        if (ObjectHelper.isEmpty(excludePatterns) && ObjectHelper.isEmpty(includePatterns)) {
+            return path -> true;
+        } else {
+            return path -> stringPredicate.test(sanitize(path.toString()));
+        }
+    }
+
+    static String sanitize(String path) {
+        path = path.trim();
+        return (!path.isEmpty() && path.charAt(0) == '/')
+                ? path.substring(1)
+                : path;
+    }
+
+    public static class Builder {
+        private List<String> includePatterns = new ArrayList<String>();
+        private List<String> excludePatterns = new ArrayList<String>();
+
+        public Builder patterns(boolean isInclude, Collection<String> patterns) {
+            if (isInclude) {
+                include(patterns);
+            } else {
+                exclude(patterns);
+            }
+            return this;
+        }
+
+        public Builder include(String pattern) {
+            includePatterns.add(sanitize(pattern));
+            return this;
+        }
+
+        public Builder include(Collection<String> patterns) {
+            patterns.stream().map(PathFilter::sanitize).forEach(includePatterns::add);
+            return this;
+        }
+
+        public Builder include(Optional<? extends Collection<String>> patterns) {
+            patterns.ifPresent(ps -> include(ps));
+            return this;
+        }
+
+        public Builder exclude(String pattern) {
+            excludePatterns.add(sanitize(pattern));
+            return this;
+        }
+
+        public Builder exclude(Collection<String> patterns) {
+            patterns.stream().map(PathFilter::sanitize).forEach(excludePatterns::add);
+            return this;
+        }
+
+        public Builder exclude(Optional<? extends Collection<String>> patterns) {
+            patterns.ifPresent(ps -> exclude(ps));
+            return this;
+        }
+
+        public Builder combine(Builder other) {
+            includePatterns.addAll(other.includePatterns);
+            excludePatterns.addAll(other.excludePatterns);
+            return this;
+        }
+
+        /**
+         * @throws NullPointerException if this method is called more than once for the same {@link Builder} instance.
+         * @return                      a new {@link PathFilter}
+         */
+        public PathFilter build() {
+            final List<String> incl = includePatterns;
+            includePatterns = null; // avoid leaking the collection trough reuse of the builder
+            final List<String> excl = excludePatterns;
+            excludePatterns = null; // avoid leaking the collection trough reuse of the builder
+            return new PathFilter(incl, excl);
+        }
+
+    }
+
+}
diff --git a/extensions/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/CamelSupportTest.java b/extensions/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/CamelSupportTest.java
deleted file mode 100644
index a93d05f..0000000
--- a/extensions/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/CamelSupportTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.quarkus.core.deployment;
-
-import java.util.Arrays;
-
-import org.junit.jupiter.api.Test;
-
-import static org.apache.camel.quarkus.core.deployment.CamelSupport.isPathIncluded;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class CamelSupportTest {
-
-    @Test
-    public void testPathFiltering() {
-        assertFalse(isPathIncluded(
-                "org/acme/MyClass",
-                Arrays.asList("org/**"),
-                Arrays.asList()));
-        assertFalse(isPathIncluded(
-                "org/acme/MyClass",
-                Arrays.asList("org/**"),
-                Arrays.asList("org/**", "org/acme/MyClass")));
-        assertFalse(isPathIncluded(
-                "org/acme/MyClass",
-                Arrays.asList("org/acme/M*"),
-                Arrays.asList("org/acme/MyClass")));
-        assertFalse(isPathIncluded(
-                "org/acme/MyClass",
-                Arrays.asList(),
-                Arrays.asList("org/acme/A*")));
-
-        assertTrue(isPathIncluded(
-                "org/acme/MyClass",
-                Arrays.asList(),
-                Arrays.asList()));
-        assertTrue(isPathIncluded(
-                "org/acme/MyClass",
-                Arrays.asList(),
-                Arrays.asList("org/**")));
-        assertTrue(isPathIncluded(
-                "org/acme/MyClass",
-                Arrays.asList("org/acme/A*"),
-                Arrays.asList("org/acme/MyClass")));
-    }
-}
diff --git a/extensions/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/util/PathFilterTest.java b/extensions/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/util/PathFilterTest.java
new file mode 100644
index 0000000..2956860
--- /dev/null
+++ b/extensions/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/util/PathFilterTest.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.quarkus.core.deployment.util;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.jboss.jandex.DotName;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class PathFilterTest {
+
+    @Test
+    public void stringFilter() {
+        assertFalse(isPathIncluded(
+                "org/acme/MyClass",
+                Arrays.asList("org/**"),
+                Arrays.asList()));
+        assertFalse(isPathIncluded(
+                "org/acme/MyClass",
+                Arrays.asList("org/**"),
+                Arrays.asList("org/**", "org/acme/MyClass")));
+        assertFalse(isPathIncluded(
+                "org/acme/MyClass",
+                Arrays.asList("org/acme/M*"),
+                Arrays.asList("org/acme/MyClass")));
+        assertFalse(isPathIncluded(
+                "org/acme/MyClass",
+                Arrays.asList(),
+                Arrays.asList("org/acme/A*")));
+
+        assertTrue(isPathIncluded(
+                "org/acme/MyClass",
+                Arrays.asList(),
+                Arrays.asList()));
+        assertTrue(isPathIncluded(
+                "org/acme/MyClass",
+                Arrays.asList(),
+                Arrays.asList("org/**")));
+        assertTrue(isPathIncluded(
+                "org/acme/MyClass",
+                Arrays.asList("org/acme/A*"),
+                Arrays.asList("org/acme/MyClass")));
+    }
+
+    @Test
+    public void pathFilter() {
+        Predicate<Path> predicate = new PathFilter.Builder()
+                .include("/foo/bar/*")
+                .include("moo/mar/*")
+                .exclude("/foo/baz/*")
+                .exclude("moo/maz/*")
+                .build().asPathPredicate();
+        assertTrue(predicate.test(Paths.get("/foo/bar/file")));
+        assertTrue(predicate.test(Paths.get("foo/bar/file")));
+        assertFalse(predicate.test(Paths.get("/foo/baz/file")));
+        assertFalse(predicate.test(Paths.get("foo/baz/file")));
+
+        assertTrue(predicate.test(Paths.get("/moo/mar/file")));
+        assertTrue(predicate.test(Paths.get("moo/mar/file")));
+        assertFalse(predicate.test(Paths.get("/moo/marz/file")));
+        assertFalse(predicate.test(Paths.get("moo/maz/file")));
+    }
+
+    @Test
+    public void dotNameFilter() {
+        Predicate<DotName> predicate = new PathFilter.Builder()
+                .include("foo/bar/*")
+                .exclude("foo/baz/*")
+                .build().asDotNamePredicate();
+        assertTrue(predicate.test(DotName.createSimple("foo.bar.Class")));
+        assertFalse(predicate.test(DotName.createSimple("foo.baz.Class")));
+    }
+
+    static boolean isPathIncluded(String path, List<String> excludePatterns, List<String> includePatterns) {
+        return new PathFilter(includePatterns, excludePatterns).asStringPredicate().test(path);
+    }
+
+}
diff --git a/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelConfig.java b/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelConfig.java
index d10f6f5..b564d8f 100644
--- a/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelConfig.java
+++ b/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelConfig.java
@@ -32,6 +32,12 @@
     @ConfigItem
     public MainConfig main;
 
+    /**
+     * Build time configuration options for Camel services.
+     */
+    @ConfigItem
+    public ServiceConfig service;
+
     @ConfigGroup
     public static class MainConfig {
         /**
@@ -87,4 +93,85 @@
         @ConfigItem
         public Optional<List<String>> includePatterns;
     }
+
+    @ConfigGroup
+    public static class ServiceConfig {
+
+        /**
+         * Build time configuration related to discoverability of Camel services via the
+         * {@code org.apache.camel.spi.FactoryFinder} mechanism
+         */
+        @ConfigItem
+        public ServiceDiscoveryConfig discovery;
+
+        /** Build time configuration related to registering of Camel services to the Camel registry */
+        @ConfigItem
+        public ServiceRegistryConfig registry;
+    }
+
+    @ConfigGroup
+    public static class ServiceDiscoveryConfig {
+
+        /**
+         * A comma-separated list of Ant-path style patterns to match Camel service definition files in the classpath.
+         * The services defined in the matching files will <strong>not<strong> be discoverable via the
+         * {@code org.apache.camel.spi.FactoryFinder} mechanism.
+         * <p>
+         * The excludes have higher precedence than includes. The excludes defined here can also be used to veto the
+         * discoverability of services included by Camel Quarkus extensions.
+         * <p>
+         * Example values:
+         * <code>META-INF/services/org/apache/camel/foo/&#42;,META-INF/services/org/apache/camel/foo/&#42;&#42;/bar</code>
+         */
+        @ConfigItem
+        public Optional<List<String>> excludePatterns;
+
+        /**
+         * A comma-separated list of Ant-path style patterns to match Camel service definition files in the classpath.
+         * The services defined in the matching files will be discoverable via the
+         * {@code org.apache.camel.spi.FactoryFinder} mechanism unless the given file is excluded via
+         * {@code exclude-patterns}.
+         * <p>
+         * Note that Camel Quarkus extensions may include some services by default. The services selected here added
+         * to those services and the exclusions defined in {@code exclude-patterns} are applied to the union set.
+         * <p>
+         * Example values:
+         * <code>META-INF/services/org/apache/camel/foo/&#42;,META-INF/services/org/apache/camel/foo/&#42;&#42;/bar</code>
+         */
+        @ConfigItem
+        public Optional<List<String>> includePatterns;
+    }
+
+    @ConfigGroup
+    public static class ServiceRegistryConfig {
+
+        /**
+         * A comma-separated list of Ant-path style patterns to match Camel service definition files in the classpath.
+         * The services defined in the matching files will <strong>not<strong> be added to Camel registry during
+         * application's static initialization.
+         * <p>
+         * The excludes have higher precedence than includes. The excludes defined here can also be used to veto the
+         * registration of services included by Camel Quarkus extensions.
+         * <p>
+         * Example values:
+         * <code>META-INF/services/org/apache/camel/foo/&#42;,META-INF/services/org/apache/camel/foo/&#42;&#42;/bar</code>
+         */
+        @ConfigItem
+        public Optional<List<String>> excludePatterns;
+
+        /**
+         * A comma-separated list of Ant-path style patterns to match Camel service definition files in the classpath.
+         * The services defined in the matching files will be added to Camel registry during application's static
+         * initialization unless the given file is excluded via {@code exclude-patterns}.
+         * <p>
+         * Note that Camel Quarkus extensions may include some services by default. The services selected here added
+         * to those services and the exclusions defined in {@code exclude-patterns} are applied to the union set.
+         * <p>
+         * Example values:
+         * <code>META-INF/services/org/apache/camel/foo/&#42;,META-INF/services/org/apache/camel/foo/&#42;&#42;/bar</code>
+         */
+        @ConfigItem
+        public Optional<List<String>> includePatterns;
+    }
+
 }
diff --git a/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRecorder.java b/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRecorder.java
index ab2d8c5..5a5824d 100644
--- a/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRecorder.java
+++ b/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRecorder.java
@@ -22,8 +22,10 @@
 import org.apache.camel.CamelContext;
 import org.apache.camel.model.ValidateDefinition;
 import org.apache.camel.model.validator.PredicateValidatorDefinition;
+import org.apache.camel.quarkus.core.FastFactoryFinderResolver.Builder;
 import org.apache.camel.reifier.ProcessorReifier;
 import org.apache.camel.reifier.validator.ValidatorReifier;
+import org.apache.camel.spi.FactoryFinderResolver;
 import org.apache.camel.spi.ModelJAXBContextFactory;
 import org.apache.camel.spi.Registry;
 import org.apache.camel.spi.TypeConverterLoader;
@@ -58,8 +60,9 @@
             RuntimeValue<TypeConverterRegistry> typeConverterRegistry,
             RuntimeValue<ModelJAXBContextFactory> contextFactory,
             RuntimeValue<XmlRoutesLoader> xmlLoader,
+            RuntimeValue<FactoryFinderResolver> factoryFinderResolver,
             BeanContainer beanContainer) {
-        FastCamelContext context = new FastCamelContext();
+        FastCamelContext context = new FastCamelContext(factoryFinderResolver.getValue());
         context.setRegistry(registry.getValue());
         context.setTypeConverterRegistry(typeConverterRegistry.getValue());
         context.setLoadTypeConverters(false);
@@ -121,4 +124,16 @@
     public RuntimeValue<RegistryRoutesLoader> newDefaultRegistryRoutesLoader() {
         return new RuntimeValue<>(new RegistryRoutesLoaders.Default());
     }
+
+    public RuntimeValue<Builder> factoryFinderResolverBuilder() {
+        return new RuntimeValue<>(new FastFactoryFinderResolver.Builder());
+    }
+
+    public void factoryFinderResolverEntry(RuntimeValue<Builder> builder, String resourcePath, Class<?> cl) {
+        builder.getValue().entry(resourcePath, cl);
+    }
+
+    public RuntimeValue<FactoryFinderResolver> factoryFinderResolver(RuntimeValue<Builder> builder) {
+        return new RuntimeValue<>(builder.getValue().build());
+    }
 }
diff --git a/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastCamelContext.java b/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastCamelContext.java
index e864c84..8e8ff03 100644
--- a/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastCamelContext.java
+++ b/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastCamelContext.java
@@ -42,7 +42,6 @@
 import org.apache.camel.impl.engine.DefaultCamelContextNameStrategy;
 import org.apache.camel.impl.engine.DefaultClassResolver;
 import org.apache.camel.impl.engine.DefaultEndpointRegistry;
-import org.apache.camel.impl.engine.DefaultFactoryFinderResolver;
 import org.apache.camel.impl.engine.DefaultInflightRepository;
 import org.apache.camel.impl.engine.DefaultInjector;
 import org.apache.camel.impl.engine.DefaultMessageHistoryFactory;
@@ -109,9 +108,9 @@
 public class FastCamelContext extends AbstractCamelContext {
     private Model model;
 
-    public FastCamelContext() {
+    public FastCamelContext(FactoryFinderResolver factoryFinderResolver) {
         super(false);
-        setInitialization(Initialization.Eager);
+        setFactoryFinderResolver(factoryFinderResolver);
         setTracing(Boolean.FALSE);
         setDebugging(Boolean.FALSE);
         setMessageHistory(Boolean.FALSE);
@@ -238,7 +237,8 @@
 
     @Override
     protected FactoryFinderResolver createFactoryFinderResolver() {
-        return new DefaultFactoryFinderResolver();
+        throw new UnsupportedOperationException(
+                "FactoryFinderResolver should have been set in the FastCamelContext constructor");
     }
 
     @Override
diff --git a/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastFactoryFinderResolver.java b/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastFactoryFinderResolver.java
new file mode 100644
index 0000000..0c8f7db
--- /dev/null
+++ b/extensions/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastFactoryFinderResolver.java
@@ -0,0 +1,147 @@
+/*
+ * 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.quarkus.core;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.camel.impl.engine.DefaultFactoryFinderResolver;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.spi.FactoryFinder;
+import org.apache.camel.spi.FactoryFinderResolver;
+import org.apache.camel.support.ObjectHelper;
+import org.jboss.logging.Logger;
+
+/**
+ * A build time assembled {@link FactoryFinderResolver}.
+ */
+public class FastFactoryFinderResolver extends DefaultFactoryFinderResolver {
+    private static final Logger LOG = Logger.getLogger(FastFactoryFinderResolver.class);
+    private final Map<String, Class<?>> classMap;
+
+    FastFactoryFinderResolver(Map<String, Class<?>> classMap) {
+        this.classMap = classMap;
+    }
+
+    @Override
+    public FactoryFinder resolveFactoryFinder(ClassResolver classResolver, String resourcePath) {
+        return new FastFactoryFinder(resourcePath);
+    }
+
+    static String mapKey(String resourcePath, String prefix, String key) {
+        final int len = resourcePath.length() + (prefix == null ? 0 : prefix.length()) + key.length() + 1;
+        final StringBuilder sb = new StringBuilder(len);
+        if (resourcePath.startsWith("/")) {
+            sb.append(resourcePath, 1, resourcePath.length());
+        } else {
+            sb.append(resourcePath);
+        }
+        if (!resourcePath.endsWith("/")) {
+            sb.append("/");
+        }
+        if (prefix != null) {
+            sb.append(prefix);
+        }
+        sb.append(key);
+        return sb.toString();
+    }
+
+    public static class Builder {
+        private Map<String, Class<?>> classMap = new HashMap<>();
+
+        public Builder entry(String resourcePath, Class<?> cl) {
+            if (resourcePath.startsWith("/")) {
+                resourcePath = resourcePath.substring(1);
+            }
+            classMap.put(resourcePath, cl);
+            return this;
+        }
+
+        public FastFactoryFinderResolver build() {
+            Map<String, Class<?>> cm = classMap;
+            classMap = null; // make sure the classMap does not leak through re-using the builder
+
+            if (LOG.isDebugEnabled()) {
+                cm.entrySet().forEach(
+                        e -> LOG.debugf("FactoryFinder entry " + e.getKey() + ": " + e.getValue().getName()));
+            }
+
+            return new FastFactoryFinderResolver(cm);
+        }
+    }
+
+    public class FastFactoryFinder implements FactoryFinder {
+
+        private final String path;
+
+        FastFactoryFinder(String resourcePath) {
+            this.path = resourcePath;
+        }
+
+        @Override
+        public String getResourcePath() {
+            return path;
+        }
+
+        @Override
+        public Optional<Object> newInstance(String key) {
+            return Optional.ofNullable(doNewInstance(key, null));
+        }
+
+        @Override
+        public <T> Optional<T> newInstance(String key, Class<T> type) {
+            Object obj = doNewInstance(key, null);
+            return Optional.ofNullable(type.cast(obj));
+        }
+
+        @Override
+        public Optional<Class<?>> findClass(String key) {
+            return findClass(key, null);
+        }
+
+        @Override
+        public Optional<Class<?>> findClass(String key, String propertyPrefix) {
+            final String mapKey = mapKey(path, propertyPrefix, key);
+            final Class<?> cl = classMap.get(mapKey);
+            if (cl == null) {
+                LOG.warnf("Could not find a non-optional class for key %s", mapKey);
+            }
+            return Optional.ofNullable(cl);
+        }
+
+        @Override
+        public Optional<Class<?>> findClass(String key, String propertyPrefix, Class<?> clazz) {
+            // Just ignore clazz which is only useful for OSGiFactoryFinder
+            return findClass(key, propertyPrefix);
+        }
+
+        @Override
+        public Optional<Class<?>> findOptionalClass(String key, String propertyPrefix) {
+            final String mapKey = mapKey(path, propertyPrefix, key);
+            LOG.tracef("Found an optional class for key %s: %s", mapKey);
+            return Optional.ofNullable(classMap.get(mapKey));
+        }
+
+        private Object doNewInstance(String key, String propertyPrefix) {
+            Optional<Class<?>> clazz = findClass(key, propertyPrefix);
+            return clazz.map(ObjectHelper::newInstance).orElse(null);
+        }
+
+    }
+
+}
diff --git a/extensions/file/deployment/src/main/java/org/apache/camel/quarkus/component/file/deployment/FileProcessor.java b/extensions/file/deployment/src/main/java/org/apache/camel/quarkus/component/file/deployment/FileProcessor.java
index 8a38547..3739d70 100644
--- a/extensions/file/deployment/src/main/java/org/apache/camel/quarkus/component/file/deployment/FileProcessor.java
+++ b/extensions/file/deployment/src/main/java/org/apache/camel/quarkus/component/file/deployment/FileProcessor.java
@@ -20,6 +20,7 @@
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
 import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.strategy.GenericFileProcessStrategyFactory;
 
 class FileProcessor {
 
@@ -32,6 +33,8 @@
 
     @BuildStep
     ReflectiveClassBuildItem registerForReflection() {
-        return new ReflectiveClassBuildItem(true, false, GenericFile.class);
+        return new ReflectiveClassBuildItem(true, false,
+                GenericFile.class,
+                GenericFileProcessStrategyFactory.class);
     }
 }
diff --git a/extensions/hystrix/deployment/src/main/java/org/apache/camel/quarkus/component/hystrix/deployment/HystrixProcessor.java b/extensions/hystrix/deployment/src/main/java/org/apache/camel/quarkus/component/hystrix/deployment/HystrixProcessor.java
index e0ecd6d..acd7554 100644
--- a/extensions/hystrix/deployment/src/main/java/org/apache/camel/quarkus/component/hystrix/deployment/HystrixProcessor.java
+++ b/extensions/hystrix/deployment/src/main/java/org/apache/camel/quarkus/component/hystrix/deployment/HystrixProcessor.java
@@ -19,7 +19,6 @@
 import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
-import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.NativeImageSystemPropertyBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
 import org.apache.camel.model.CircuitBreakerDefinition;
@@ -38,7 +37,6 @@
 
     @BuildStep
     void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
-            BuildProducer<NativeImageResourceBuildItem> resource,
             BuildProducer<NativeImageSystemPropertyBuildItem> systemProperty) {
 
         reflectiveClass.produce(new ReflectiveClassBuildItem(true, true,
@@ -47,8 +45,6 @@
                 CircuitBreakerDefinition.class,
                 OnFallbackDefinition.class));
 
-        resource.produce(new NativeImageResourceBuildItem("META-INF/services/org/apache/camel/model/CircuitBreakerDefinition"));
-
         // Force RxJava to not use Unsafe API
         systemProperty.produce(new NativeImageSystemPropertyBuildItem("rx.unsafe-disable", "true"));
     }
diff --git a/integration-tests/dozer/src/main/resources/application.properties b/integration-tests/dozer/src/main/resources/application.properties
index f0917e9..9540e06 100644
--- a/integration-tests/dozer/src/main/resources/application.properties
+++ b/integration-tests/dozer/src/main/resources/application.properties
@@ -15,6 +15,8 @@
 ## limitations under the License.
 ## ---------------------------------------------------------------------------
 
+#quarkus.log.category."org.apache.camel.quarkus.core.FastFactoryFinderResolver".level = TRACE
+
 #
 # Quarkus - Camel Dozer
 #