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 3e65db32ae35eeb8748d381bba27594863edc9a3
Author: Luca Burgazzoli <lburgazz...@gmail.com>
AuthorDate: Wed Mar 3 18:43:55 2021 +0100

    CAMEL-16285: Extensible ResourceHelper
---
 .../org/apache/camel/resource-resolver/bean        |   2 +
 .../apache/camel/language/bean/BeanLanguage.java   |   1 +
 .../camel/language/bean/BeanResourceResolver.java  |  96 +++++++
 .../camel/spi/annotations/ResourceResolver.java}   |  35 +--
 .../org/apache/camel/ExtendedCamelContext.java     |  11 +
 .../main/java/org/apache/camel/spi/Resource.java   |  67 ++---
 .../java/org/apache/camel/spi/ResourceLoader.java} |  39 ++-
 .../org/apache/camel/spi/ResourceResolver.java     |  45 ++++
 .../org/apache/camel/resource-resolver/classpath   |   2 +
 .../org/apache/camel/resource-resolver/file        |   2 +
 .../org/apache/camel/resource-resolver/http        |   2 +
 .../org/apache/camel/resource-resolver/ref         |   2 +
 .../camel/impl/engine/AbstractCamelContext.java    |  22 ++
 .../engine/DefaultPackageScanResourceResolver.java |  53 ++--
 .../apache/camel/impl/engine/DefaultResource.java  |  69 -----
 .../camel/impl/engine/DefaultResourceLoader.java   | 133 ++++++++++
 .../impl/engine/DefaultResourceResolvers.java      | 281 +++++++++++++++++++++
 .../camel/impl/engine/SimpleCamelContext.java      |  12 +
 .../camel/impl/ExtendedCamelContextConfigurer.java |   6 +
 .../camel/impl/lw/LightweightCamelContext.java     |  11 +
 .../impl/lw/LightweightRuntimeCamelContext.java    |  11 +
 .../ValidatorEndpointClearCachedSchemaTest.java    |  10 +-
 .../ValidatorWithResourceResolverRouteTest.java    |   2 +-
 .../apache/camel/urlhandler/custom/Handler.java    |  29 ++-
 .../org/apache/camel/urlhandler/pd/Handler.java    | 117 ++++-----
 .../org/apache/camel/util/ResourceHelperTest.java  |  87 +------
 .../org/apache/camel/util/ResourceLoaderTest.java  | 207 +++++++++++++++
 .../org/apache/camel/support/ResourceHelper.java   | 200 +++++----------
 .../camel/support/ResourceResolverSupport.java     |  83 ++++++
 .../dsl/java/joor/JavaRoutesBuilderLoaderTest.java |   8 +-
 .../apache/camel/dsl/xml/io/XmlLoadRestTest.java   |   7 +-
 .../org/apache/camel/dsl/xml/io/XmlLoadTest.java   |   4 +-
 .../dsl/xml/io/XmlRoutesBuilderLoaderTest.java     |   7 +-
 .../camel/dsl/xml/jaxb/JaxbXmlLoadRestTest.java    |   7 +-
 .../apache/camel/dsl/xml/jaxb/JaxbXmlLoadTest.java |   4 +-
 .../xml/jaxb/JaxbXmlRoutesBuilderLoaderTest.java   |   7 +-
 .../camel/dsl/yaml/support/YamlTestSupport.groovy  |   5 +-
 .../camel/maven/packaging/SpiGeneratorMojo.java    |   7 +-
 .../camel/spi/annotations/ResourceResolver.java    |  35 +--
 39 files changed, 1188 insertions(+), 540 deletions(-)

diff --git 
a/components/camel-bean/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/bean
 
b/components/camel-bean/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/bean
new file mode 100644
index 0000000..65ea78f
--- /dev/null
+++ 
b/components/camel-bean/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/bean
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.language.bean.BeanResourceResolver
diff --git 
a/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanLanguage.java
 
b/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanLanguage.java
index 6c7b515..3a5745d 100644
--- 
a/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanLanguage.java
+++ 
b/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanLanguage.java
@@ -49,6 +49,7 @@ import org.apache.camel.util.URISupport;
  */
 @org.apache.camel.spi.annotations.Language("bean")
 public class BeanLanguage extends LanguageSupport implements 
PropertyConfigurer, StaticService {
+    public static final String LANGUAGE = "bean";
 
     private volatile BeanComponent beanComponent;
     private volatile ParameterMappingStrategy parameterMappingStrategy;
diff --git 
a/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanResourceResolver.java
 
b/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanResourceResolver.java
new file mode 100644
index 0000000..11c4955
--- /dev/null
+++ 
b/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanResourceResolver.java
@@ -0,0 +1,96 @@
+/*
+ * 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.language.bean;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.annotations.ResourceResolver;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.support.ResourceResolverSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ResourceResolver(BeanResourceResolver.SCHEME)
+public class BeanResourceResolver extends ResourceResolverSupport {
+    public static final String SCHEME = "bean";
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(BeanResourceResolver.class);
+
+    public BeanResourceResolver() {
+        super(SCHEME);
+    }
+
+    @Override
+    public Resource createResource(String location) {
+        final String expression = getRemaining(location);
+
+        LOGGER.trace("Creating resource from expression {}", expression);
+
+        return new Resource() {
+            @Override
+            public String getLocation() {
+                return location;
+            }
+
+            @Override
+            public boolean exists() {
+                return false;
+            }
+
+            @Override
+            public InputStream getInputStream() throws IOException {
+                final CamelContext context = getCamelContext();
+                final Exchange dummy = new DefaultExchange(context);
+
+                InputStream answer = null;
+                Object out = evaluate(dummy, expression);
+
+                if (dummy.getException() != null) {
+                    throw new IOException(
+                            "Cannot find resource: " + location + " from 
calling the bean", dummy.getException());
+                }
+
+                if (out != null) {
+                    answer = 
context.getTypeConverter().tryConvertTo(InputStream.class, dummy, out);
+                    if (answer == null) {
+                        String text = 
context.getTypeConverter().tryConvertTo(String.class, dummy, out);
+                        if (text != null) {
+                            answer = new ByteArrayInputStream(text.getBytes());
+                        }
+                    }
+                }
+
+                if (answer == null) {
+                    throw new IOException("Cannot find resource: " + location 
+ " from calling the bean");
+                }
+
+                return answer;
+            }
+        };
+    }
+
+    private Object evaluate(Exchange dummy, String expression) {
+        return getCamelContext()
+                .resolveLanguage(BeanLanguage.LANGUAGE)
+                .createExpression(expression)
+                .evaluate(dummy, Object.class);
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java 
b/core/camel-api/src/generated/java/org/apache/camel/spi/annotations/ResourceResolver.java
similarity index 51%
copy from 
core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
copy to 
core/camel-api/src/generated/java/org/apache/camel/spi/annotations/ResourceResolver.java
index 9207b0b..0ea471c 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
+++ 
b/core/camel-api/src/generated/java/org/apache/camel/spi/annotations/ResourceResolver.java
@@ -14,29 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.urlhandler.custom;
+package org.apache.camel.spi.annotations;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
-public class Handler extends URLStreamHandler {
-    @Override
-    protected URLConnection openConnection(URL u) throws IOException {
-        final String echo = u.getHost();
-        return new URLConnection(u) {
-            @Override
-            public void connect() throws IOException {
-                connected = true;
-            }
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Target({ ElementType.TYPE })
+@ServiceFactory("resource-resolver")
+public @interface ResourceResolver {
+
+    String value();
 
-            @Override
-            public InputStream getInputStream() throws IOException {
-                return new ByteArrayInputStream(echo.getBytes());
-            }
-        };
-    }
 }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java 
b/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
index c68403e..114eeab 100644
--- a/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
@@ -57,6 +57,7 @@ import org.apache.camel.spi.PackageScanResourceResolver;
 import org.apache.camel.spi.ProcessorFactory;
 import org.apache.camel.spi.ReactiveExecutor;
 import org.apache.camel.spi.Registry;
+import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.spi.RestBindingJaxbDataFormatFactory;
 import org.apache.camel.spi.RouteController;
 import org.apache.camel.spi.RouteFactory;
@@ -649,6 +650,16 @@ public interface ExtendedCamelContext extends CamelContext 
{
     void setRoutesLoader(RoutesLoader routesLoader);
 
     /**
+     * Gets the {@link ResourceLoader} to be used.
+     */
+    ResourceLoader getResourceLoader();
+
+    /**
+     * Sets a custom {@link ResourceLoader} to be used.
+     */
+    void setResourceLoader(ResourceLoader resourceLoader);
+
+    /**
      * Gets the {@link ModelToXMLDumper} to be used.
      */
     ModelToXMLDumper getModelToXMLDumper();
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/Resource.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/Resource.java
index 7e4c324..fb336c7 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/Resource.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/Resource.java
@@ -16,10 +16,11 @@
  */
 package org.apache.camel.spi;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
 
 /**
  * Describe a resource, such as a file or class path resource.
@@ -31,61 +32,33 @@ public interface Resource {
     String getLocation();
 
     /**
-     * Returns an input stream that reads from the underlying resource.
-     * </p>
-     * Each invocation must return a new {@link InputStream} instance.
+     * Whether this resource exists..
      */
-    InputStream getInputStream() throws IOException;
+    boolean exists();
 
     /**
-     * Finds a resource with a given name.
-     *
-     * @see Class#getResourceAsStream(String)
+     * The {@link URI} of the resource.
+     * </p>
+     * The default implementation creates a {@code URI} object from resource 
location.
      */
-    static Resource fromClasspath(String location) {
-        return fromClasspath(Resource.class, location);
+    default URI getURI() {
+        return URI.create(getLocation());
     }
 
     /**
-     * Finds a resource with a given name.
-     *
-     * @see Class#getResourceAsStream(String)
-     */
-    static Resource fromClasspath(Class<?> type, String location) {
-        return new Resource() {
-            @Override
-            public String getLocation() {
-                return location;
-            }
-
-            @Override
-            public InputStream getInputStream() throws IOException {
-                return type.getResourceAsStream(location);
-            }
-        };
-    }
-
-    /**
-     * Create a resource from bytes.
+     * The {@link URL} for the resource or <code>null</code> if the URL can 
not be computed.
+     * </p>
+     * The default implementation creates a {@code URI} object from resource 
location.
      */
-    static Resource fromBytes(String location, byte[] content) {
-        return new Resource() {
-            @Override
-            public String getLocation() {
-                return location;
-            }
-
-            @Override
-            public InputStream getInputStream() throws IOException {
-                return new ByteArrayInputStream(content);
-            }
-        };
+    default URL getURL() throws MalformedURLException {
+        URI uri = getURI();
+        return uri != null ? uri.toURL() : null;
     }
 
     /**
-     * Create a resource from a string.
+     * Returns an input stream that reads from the underlying resource.
+     * </p>
+     * Each invocation must return a new {@link InputStream} instance.
      */
-    static Resource fromString(String location, String content) {
-        return fromBytes(location, content.getBytes(StandardCharsets.UTF_8));
-    }
+    InputStream getInputStream() throws IOException;
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/ResourceLoader.java
similarity index 51%
copy from 
core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
copy to core/camel-api/src/main/java/org/apache/camel/spi/ResourceLoader.java
index 9207b0b..5f96beb1 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/ResourceLoader.java
@@ -14,29 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.urlhandler.custom;
+package org.apache.camel.spi;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
+import org.apache.camel.CamelContextAware;
 
-public class Handler extends URLStreamHandler {
-    @Override
-    protected URLConnection openConnection(URL u) throws IOException {
-        final String echo = u.getHost();
-        return new URLConnection(u) {
-            @Override
-            public void connect() throws IOException {
-                connected = true;
-            }
+/**
+ * SPI for loading resources.
+ */
+public interface ResourceLoader extends CamelContextAware {
+    /**
+     * Service factory key.
+     */
+    String FACTORY = "resource-loader";
 
-            @Override
-            public InputStream getInputStream() throws IOException {
-                return new ByteArrayInputStream(echo.getBytes());
-            }
-        };
-    }
+    /**
+     * Loads {@link Resource} from uri.
+     *
+     * @param  uri the location of the resource to resolve.
+     * @return     an optional {@link Resource}
+     */
+    Resource resolveResource(String uri);
 }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/ResourceResolver.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/ResourceResolver.java
new file mode 100644
index 0000000..1330a11
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/ResourceResolver.java
@@ -0,0 +1,45 @@
+/*
+ * 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.spi;
+
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.StaticService;
+
+/**
+ * SPI for loading resources.
+ */
+public interface ResourceResolver extends StaticService, CamelContextAware {
+    /**
+     * Service factory base path for scheme specific resolver.
+     */
+    String FACTORY_PATH = 
"META-INF/services/org/apache/camel/resource-resolver/";
+
+    /**
+     * The supported resource scheme.
+     * <p/>
+     * Implementations should support a single scheme only.
+     */
+    String getSupportedScheme();
+
+    /**
+     * Resolve a {@link Resource} from a give uri.
+     *
+     * @param  location the location of the resource to resolve.
+     * @return          an {@link Resource}, null if was not possible to 
resolve the resource.
+     */
+    Resource resolve(String location);
+}
diff --git 
a/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/classpath
 
b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/classpath
new file mode 100644
index 0000000..b5aa541
--- /dev/null
+++ 
b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/classpath
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.engine.DefaultResourceResolvers$ClasspathResolver
diff --git 
a/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/file
 
b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/file
new file mode 100644
index 0000000..aa1520e
--- /dev/null
+++ 
b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/file
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.engine.DefaultResourceResolvers$FileResolver
diff --git 
a/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/http
 
b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/http
new file mode 100644
index 0000000..633e677
--- /dev/null
+++ 
b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/http
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.engine.DefaultResourceResolvers$HttpResolver
diff --git 
a/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/ref
 
b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/ref
new file mode 100644
index 0000000..e2ca4d4
--- /dev/null
+++ 
b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/ref
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.engine.DefaultResourceResolvers$RefResolver
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index eab4f12..45a1a92 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -133,6 +133,7 @@ import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.ReactiveExecutor;
 import org.apache.camel.spi.Registry;
 import org.apache.camel.spi.ReifierStrategy;
+import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.spi.RestBindingJaxbDataFormatFactory;
 import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.spi.RestRegistry;
@@ -290,6 +291,7 @@ public abstract class AbstractCamelContext extends 
BaseService
     private volatile BeanProcessorFactory beanProcessorFactory;
     private volatile XMLRoutesDefinitionLoader xmlRoutesDefinitionLoader;
     private volatile RoutesLoader routesLoader;
+    private volatile ResourceLoader resourceLoader;
     private volatile ModelToXMLDumper modelToXMLDumper;
     private volatile RestBindingJaxbDataFormatFactory 
restBindingJaxbDataFormatFactory;
     private volatile RuntimeCamelCatalog runtimeCamelCatalog;
@@ -3653,6 +3655,7 @@ public abstract class AbstractCamelContext extends 
BaseService
         getUnitOfWorkFactory();
         getRouteController();
         getRoutesLoader();
+        getResourceLoader();
 
         try {
             getRestRegistryFactory();
@@ -4601,6 +4604,23 @@ public abstract class AbstractCamelContext extends 
BaseService
         this.routesLoader = doAddService(routesLoader);
     }
 
+    @Override
+    public ResourceLoader getResourceLoader() {
+        if (resourceLoader == null) {
+            synchronized (lock) {
+                if (resourceLoader == null) {
+                    setResourceLoader(createResourceLoader());
+                }
+            }
+        }
+        return resourceLoader;
+    }
+
+    @Override
+    public void setResourceLoader(ResourceLoader resourceLoader) {
+        this.resourceLoader = doAddService(resourceLoader);
+    }
+
     public ModelToXMLDumper getModelToXMLDumper() {
         if (modelToXMLDumper == null) {
             synchronized (lock) {
@@ -4860,6 +4880,8 @@ public abstract class AbstractCamelContext extends 
BaseService
 
     protected abstract RoutesLoader createRoutesLoader();
 
+    protected abstract ResourceLoader createResourceLoader();
+
     protected abstract ModelToXMLDumper createModelToXMLDumper();
 
     protected abstract RestBindingJaxbDataFormatFactory 
createRestBindingJaxbDataFormatFactory();
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultPackageScanResourceResolver.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultPackageScanResourceResolver.java
index aacd075..ea775c0 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultPackageScanResourceResolver.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultPackageScanResourceResolver.java
@@ -26,7 +26,6 @@ import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLDecoder;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -38,14 +37,15 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 
 import org.apache.camel.CamelContextAware;
+import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.NonManagedService;
 import org.apache.camel.spi.PackageScanResourceResolver;
 import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.util.AntPathMatcher;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.function.ThrowingSupplier;
 
 /**
  * Default implement of {@link 
org.apache.camel.spi.PackageScanResourceResolver}
@@ -84,15 +84,11 @@ public class DefaultPackageScanResourceResolver extends 
BasePackageScanResolver
                 findInClasspath(root, resources, subPattern);
             }
         } else {
+            final ExtendedCamelContext ecc = 
getCamelContext().adapt(ExtendedCamelContext.class);
+            final ResourceLoader loader = ecc.getResourceLoader();
+
             // its a single resource so load it directly
-            resources.add(new DefaultResource(
-                    location,
-                    new ThrowingSupplier<InputStream, IOException>() {
-                        @Override
-                        public InputStream get() throws IOException {
-                            return 
ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext(), 
location);
-                        }
-                    }));
+            resources.add(loader.resolveResource(location));
         }
     }
 
@@ -102,16 +98,11 @@ public class DefaultPackageScanResourceResolver extends 
BasePackageScanResolver
             String subPattern)
             throws Exception {
 
-        for (Path path : ResourceHelper.findInFileSystem(dir.toPath(), 
subPattern)) {
+        final ExtendedCamelContext ecc = 
getCamelContext().adapt(ExtendedCamelContext.class);
+        final ResourceLoader loader = ecc.getResourceLoader();
 
-            resources.add(new DefaultResource(
-                    path.toString(),
-                    new ThrowingSupplier<InputStream, IOException>() {
-                        @Override
-                        public InputStream get() throws IOException {
-                            return Files.newInputStream(path);
-                        }
-                    }));
+        for (Path path : ResourceHelper.findInFileSystem(dir.toPath(), 
subPattern)) {
+            resources.add(loader.resolveResource("file:" + path.toString()));
         }
     }
 
@@ -276,14 +267,10 @@ public class DefaultPackageScanResourceResolver extends 
BasePackageScanResolver
             boolean match = PATH_MATCHER.match(subPattern, shortName);
             log.debug("Found resource: {} matching pattern: {} -> {}", 
shortName, subPattern, match);
             if (match) {
-                resources.add(new DefaultResource(
-                        name,
-                        new ThrowingSupplier<InputStream, IOException>() {
-                            @Override
-                            public InputStream get() throws IOException {
-                                return 
getCamelContext().getClassResolver().loadResourceAsStream(name);
-                            }
-                        }));
+                final ExtendedCamelContext ecc = 
getCamelContext().adapt(ExtendedCamelContext.class);
+                final ResourceLoader loader = ecc.getResourceLoader();
+
+                resources.add(loader.resolveResource(name));
             }
         }
     }
@@ -324,14 +311,10 @@ public class DefaultPackageScanResourceResolver extends 
BasePackageScanResolver
                 boolean match = PATH_MATCHER.match(subPattern, name);
                 log.debug("Found resource: {} matching pattern: {} -> {}", 
name, subPattern, match);
                 if (match) {
-                    resources.add(new DefaultResource(
-                            name,
-                            new ThrowingSupplier<InputStream, IOException>() {
-                                @Override
-                                public InputStream get() throws IOException {
-                                    return new FileInputStream(file);
-                                }
-                            }));
+                    final ExtendedCamelContext ecc = 
getCamelContext().adapt(ExtendedCamelContext.class);
+                    final ResourceLoader loader = ecc.getResourceLoader();
+
+                    resources.add(loader.resolveResource("file:" + 
file.getPath()));
                 }
             }
         }
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResource.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResource.java
deleted file mode 100644
index 437a303..0000000
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResource.java
+++ /dev/null
@@ -1,69 +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.impl.engine;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Objects;
-
-import org.apache.camel.spi.Resource;
-import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.function.ThrowingSupplier;
-
-public class DefaultResource implements Resource {
-    private final String location;
-    private final ThrowingSupplier<InputStream, IOException> 
inputStreamSupplier;
-
-    public DefaultResource(String location, ThrowingSupplier<InputStream, 
IOException> inputStreamSupplier) {
-        this.location = ObjectHelper.notNull(location, "location");
-        this.inputStreamSupplier = ObjectHelper.notNull(inputStreamSupplier, 
"inputStreamSupplier");
-    }
-
-    @Override
-    public String getLocation() {
-        return location;
-    }
-
-    @Override
-    public InputStream getInputStream() throws IOException {
-        return inputStreamSupplier.get();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof Resource)) {
-            return false;
-        }
-        Resource resource = (Resource) o;
-        return getLocation().equals(resource.getLocation());
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(getLocation());
-    }
-
-    @Override
-    public String toString() {
-        return "DefaultResource{" +
-               "location='" + location + '\'' +
-               '}';
-    }
-}
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceLoader.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceLoader.java
new file mode 100644
index 0000000..4af5f3f
--- /dev/null
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceLoader.java
@@ -0,0 +1,133 @@
+/*
+ * 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.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.spi.FactoryFinder;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceLoader;
+import org.apache.camel.spi.ResourceResolver;
+import org.apache.camel.spi.RoutesBuilderLoader;
+import org.apache.camel.support.ResolverHelper;
+import org.apache.camel.support.service.ServiceHelper;
+import org.apache.camel.support.service.ServiceSupport;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+
+/**
+ * Default {@link ResourceLoader}.
+ */
+public class DefaultResourceLoader extends ServiceSupport implements 
ResourceLoader {
+    /**
+     * Prefix to use for looking up existing {@link ResourceLoader} from the 
{@link org.apache.camel.spi.Registry}.
+     */
+    public static final String RESOURCE_LOADER_KEY_PREFIX = "resource-loader-";
+
+    private final Map<String, ResourceResolver> resolvers;
+    private CamelContext camelContext;
+
+    public DefaultResourceLoader() {
+        this(null);
+    }
+
+    public DefaultResourceLoader(CamelContext camelContext) {
+        this.camelContext = camelContext;
+        this.resolvers = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public void doStop() throws Exception {
+        super.doStop();
+
+        ServiceHelper.stopService(resolvers.values());
+
+        resolvers.clear();
+    }
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    public Resource resolveResource(final String uri) {
+        ObjectHelper.notNull(uri, "Resource uri must not be null");
+
+        String location = uri;
+        String scheme = StringHelper.before(location, ":");
+        if (scheme == null) {
+            scheme = DefaultResourceResolvers.ClasspathResolver.SCHEME;
+            location = DefaultResourceResolvers.ClasspathResolver.SCHEME + ":" 
+ location;
+        }
+
+        ResourceResolver rr = getResourceResolver(scheme);
+        if (rr == null) {
+            throw new IllegalArgumentException(
+                    "Cannot find a ResourceResolver in classpath supporting 
the scheme: " + scheme);
+        }
+
+        return rr.resolve(location);
+    }
+
+    /**
+     * Looks up a {@link ResourceResolver} for the given scheme in the 
registry or fallback to a factory finder
+     * mechanism if none found.
+     *
+     * @param  scheme the file extension for which a loader should be find.
+     * @return        a {@link RoutesBuilderLoader} or <code>null</code> if 
none found.
+     */
+    private ResourceResolver getResourceResolver(final String scheme) {
+        ResourceResolver answer = 
getCamelContext().getRegistry().lookupByNameAndType(
+                RESOURCE_LOADER_KEY_PREFIX + scheme,
+                ResourceResolver.class);
+
+        if (answer == null) {
+            answer = resolvers.computeIfAbsent(scheme, this::resolveService);
+        }
+
+        return answer;
+    }
+
+    /**
+     * Looks up a {@link ResourceResolver} for the given scheme with factory 
finder.
+     *
+     * @param  scheme the file extension for which a loader should be find.
+     * @return        a {@link RoutesBuilderLoader} or <code>null</code> if 
none found.
+     */
+    private ResourceResolver resolveService(String scheme) {
+        final ExtendedCamelContext ecc = 
getCamelContext().adapt(ExtendedCamelContext.class);
+        final FactoryFinder finder = 
ecc.getBootstrapFactoryFinder(ResourceResolver.FACTORY_PATH);
+
+        ResourceResolver rr = ResolverHelper.resolveService(ecc, finder, 
scheme, ResourceResolver.class).orElse(null);
+        if (rr != null) {
+            CamelContextAware.trySetCamelContext(rr, getCamelContext());
+            ServiceHelper.startService(rr);
+        }
+
+        return rr;
+    }
+}
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceResolvers.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceResolvers.java
new file mode 100644
index 0000000..e4d298b
--- /dev/null
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceResolvers.java
@@ -0,0 +1,281 @@
+/*
+ * 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.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.annotations.ResourceResolver;
+import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.ResourceResolverSupport;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class DefaultResourceResolvers {
+    private DefaultResourceResolvers() {
+    }
+
+    @ResourceResolver(FileResolver.SCHEME)
+    public static final class FileResolver extends ResourceResolverSupport {
+        public static final String SCHEME = "file";
+        private static final Logger LOGGER = 
LoggerFactory.getLogger(FileResolver.class);
+
+        public FileResolver() {
+            super(SCHEME);
+        }
+
+        @Override
+        public Resource createResource(String location) {
+            final String remaining = getRemaining(location);
+            final Path path = Paths.get(tryDecodeUri(remaining));
+
+            LOGGER.trace("Creating resource: {} from file system", path);
+
+            return new Resource() {
+                @Override
+                public String getLocation() {
+                    return location;
+                }
+
+                @Override
+                public boolean exists() {
+                    return Files.exists(path);
+                }
+
+                @Override
+                public URI getURI() {
+                    return path.toUri();
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    if (!exists()) {
+                        throw new FileNotFoundException(path.toString() + " 
does not exists");
+                    }
+                    if (Files.isDirectory(path)) {
+                        throw new FileNotFoundException(path.toString() + " is 
a directory");
+                    }
+
+                    return Files.newInputStream(path);
+                }
+
+                @Override
+                public String toString() {
+                    return "Resource{" +
+                           "location=" + getLocation() +
+                           '}';
+                }
+            };
+        }
+    }
+
+    @ResourceResolver(HttpResolver.SCHEME)
+    public static final class HttpResolver extends ResourceResolverSupport {
+        public static final String SCHEME = "http";
+        private static final Logger LOGGER = 
LoggerFactory.getLogger(HttpResolver.class);
+
+        public HttpResolver() {
+            super(SCHEME);
+        }
+
+        @Override
+        public Resource createResource(String location) {
+            LOGGER.trace("Creating resource: {} from HTTP", location);
+
+            return new Resource() {
+                @Override
+                public String getLocation() {
+                    return location;
+                }
+
+                @Override
+                public boolean exists() {
+                    URLConnection connection = null;
+
+                    try {
+                        connection = new URL(location).openConnection();
+
+                        if (connection instanceof HttpURLConnection) {
+                            return ((HttpURLConnection) 
connection).getResponseCode() == HttpURLConnection.HTTP_OK;
+                        }
+
+                        return connection.getContentLengthLong() > 0;
+                    } catch (IOException e) {
+                        throw new IllegalArgumentException(e);
+                    } finally {
+                        // close the http connection to avoid
+                        // leaking gaps in case of an exception
+                        if (connection instanceof HttpURLConnection) {
+                            ((HttpURLConnection) connection).disconnect();
+                        }
+                    }
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    URLConnection con = new URL(location).openConnection();
+                    con.setUseCaches(false);
+
+                    try {
+                        return con.getInputStream();
+                    } catch (IOException e) {
+                        // close the http connection to avoid
+                        // leaking gaps in case of an exception
+                        if (con instanceof HttpURLConnection) {
+                            ((HttpURLConnection) con).disconnect();
+                        }
+                        throw e;
+                    }
+                }
+
+                @Override
+                public String toString() {
+                    return "Resource{" +
+                           "location=" + getLocation() +
+                           '}';
+                }
+            };
+        }
+    }
+
+    @ResourceResolver(ClasspathResolver.SCHEME)
+    public static final class ClasspathResolver extends 
ResourceResolverSupport {
+        public static final String SCHEME = "classpath";
+        private static final Logger LOGGER = 
LoggerFactory.getLogger(ClasspathResolver.class);
+
+        public ClasspathResolver() {
+            super(SCHEME);
+        }
+
+        @Override
+        public Resource createResource(String location) {
+            final String path = getPath(location);
+
+            LOGGER.trace("Creating resource: {} from classpath", path);
+
+            return new Resource() {
+                @Override
+                public String getLocation() {
+                    return location;
+                }
+
+                @Override
+                public boolean exists() {
+                    return getURI() != null;
+                }
+
+                @Override
+                public URI getURI() {
+                    URL url = getCamelContext()
+                            .adapt(ExtendedCamelContext.class)
+                            .getClassResolver()
+                            .loadResourceAsURL(path);
+
+                    try {
+                        return url != null ? url.toURI() : null;
+                    } catch (URISyntaxException e) {
+                        throw new IllegalArgumentException(e);
+                    }
+
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    return getCamelContext()
+                            .adapt(ExtendedCamelContext.class)
+                            .getClassResolver()
+                            .loadResourceAsStream(path);
+                }
+
+                @Override
+                public String toString() {
+                    return "Resource{" +
+                           "location=" + getLocation() +
+                           '}';
+                }
+            };
+        }
+
+        private String getPath(String location) {
+            String uri = StringHelper.after(location, "classpath:");
+            uri = tryDecodeUri(uri);
+            uri = FileUtil.compactPath(uri, '/');
+
+            return uri;
+        }
+    }
+
+    @ResourceResolver(RefResolver.SCHEME)
+    public static final class RefResolver extends ResourceResolverSupport {
+        public static final String SCHEME = "ref";
+        private static final Logger LOGGER = 
LoggerFactory.getLogger(RefResolver.class);
+
+        public RefResolver() {
+            super(SCHEME);
+        }
+
+        @Override
+        public Resource createResource(String location) {
+            final String key = getRemaining(location);
+            final String val = CamelContextHelper.lookup(getCamelContext(), 
key, String.class);
+
+            LOGGER.trace("Creating resource: {} from registry", key);
+
+            return new Resource() {
+                @Override
+                public String getLocation() {
+                    return location;
+                }
+
+                @Override
+                public boolean exists() {
+                    return val != null;
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    if (!exists()) {
+                        throw new IOException("There is no bean in the 
registry with name " + key + "and type String");
+                    }
+
+                    return new ByteArrayInputStream(val.getBytes());
+                }
+
+                @Override
+                public String toString() {
+                    return "Resource{" +
+                           "location=" + getLocation() +
+                           '}';
+                }
+            };
+        }
+    }
+}
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
index 2cae07b..057cd5a 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
@@ -63,6 +63,7 @@ import org.apache.camel.spi.ProcessorFactory;
 import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.ReactiveExecutor;
 import org.apache.camel.spi.Registry;
+import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.spi.RestBindingJaxbDataFormatFactory;
 import org.apache.camel.spi.RestRegistryFactory;
 import org.apache.camel.spi.RouteController;
@@ -454,6 +455,17 @@ public class SimpleCamelContext extends 
AbstractCamelContext {
     }
 
     @Override
+    protected ResourceLoader createResourceLoader() {
+        Optional<ResourceLoader> result = ResolverHelper.resolveService(
+                getCamelContextReference(),
+                getBootstrapFactoryFinder(),
+                ResourceLoader.FACTORY,
+                ResourceLoader.class);
+
+        return result.orElseGet(DefaultResourceLoader::new);
+    }
+
+    @Override
     protected ModelToXMLDumper createModelToXMLDumper() {
         Optional<ModelToXMLDumper> result = ResolverHelper.resolveService(
                 getCamelContextReference(),
diff --git 
a/core/camel-core-engine/src/generated/java/org/apache/camel/impl/ExtendedCamelContextConfigurer.java
 
b/core/camel-core-engine/src/generated/java/org/apache/camel/impl/ExtendedCamelContextConfigurer.java
index fc3b1b3..d06c43d 100644
--- 
a/core/camel-core-engine/src/generated/java/org/apache/camel/impl/ExtendedCamelContextConfigurer.java
+++ 
b/core/camel-core-engine/src/generated/java/org/apache/camel/impl/ExtendedCamelContextConfigurer.java
@@ -129,6 +129,8 @@ public class ExtendedCamelContextConfigurer extends 
org.apache.camel.support.com
         case "ReactiveExecutor": 
target.setReactiveExecutor(property(camelContext, 
org.apache.camel.spi.ReactiveExecutor.class, value)); return true;
         case "registry":
         case "Registry": target.setRegistry(property(camelContext, 
org.apache.camel.spi.Registry.class, value)); return true;
+        case "resourceloader":
+        case "ResourceLoader": target.setResourceLoader(property(camelContext, 
org.apache.camel.spi.ResourceLoader.class, value)); return true;
         case "restbindingjaxbdataformatfactory":
         case "RestBindingJaxbDataFormatFactory": 
target.setRestBindingJaxbDataFormatFactory(property(camelContext, 
org.apache.camel.spi.RestBindingJaxbDataFormatFactory.class, value)); return 
true;
         case "restconfiguration":
@@ -300,6 +302,8 @@ public class ExtendedCamelContextConfigurer extends 
org.apache.camel.support.com
         case "ReactiveExecutor": return 
org.apache.camel.spi.ReactiveExecutor.class;
         case "registry":
         case "Registry": return org.apache.camel.spi.Registry.class;
+        case "resourceloader":
+        case "ResourceLoader": return 
org.apache.camel.spi.ResourceLoader.class;
         case "restbindingjaxbdataformatfactory":
         case "RestBindingJaxbDataFormatFactory": return 
org.apache.camel.spi.RestBindingJaxbDataFormatFactory.class;
         case "restconfiguration":
@@ -472,6 +476,8 @@ public class ExtendedCamelContextConfigurer extends 
org.apache.camel.support.com
         case "ReactiveExecutor": return target.getReactiveExecutor();
         case "registry":
         case "Registry": return target.getRegistry();
+        case "resourceloader":
+        case "ResourceLoader": return target.getResourceLoader();
         case "restbindingjaxbdataformatfactory":
         case "RestBindingJaxbDataFormatFactory": return 
target.getRestBindingJaxbDataFormatFactory();
         case "restconfiguration":
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
index 5b122bd..16f4521 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
@@ -118,6 +118,7 @@ import org.apache.camel.spi.ProcessorFactory;
 import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.ReactiveExecutor;
 import org.apache.camel.spi.Registry;
+import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.spi.RestBindingJaxbDataFormatFactory;
 import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.spi.RestRegistry;
@@ -1503,6 +1504,16 @@ public class LightweightCamelContext implements 
ExtendedCamelContext, CatalogCam
     }
 
     @Override
+    public ResourceLoader getResourceLoader() {
+        return getExtendedCamelContext().getResourceLoader();
+    }
+
+    @Override
+    public void setResourceLoader(ResourceLoader resourceLoader) {
+        getExtendedCamelContext().setResourceLoader(resourceLoader);
+    }
+
+    @Override
     public void setModelToXMLDumper(ModelToXMLDumper modelToXMLDumper) {
         getExtendedCamelContext().setModelToXMLDumper(modelToXMLDumper);
     }
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
index 97dba59..afed430 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
@@ -115,6 +115,7 @@ import org.apache.camel.spi.ProcessorFactory;
 import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.ReactiveExecutor;
 import org.apache.camel.spi.Registry;
+import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.spi.RestBindingJaxbDataFormatFactory;
 import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.spi.RestRegistry;
@@ -1793,6 +1794,16 @@ public class LightweightRuntimeCamelContext implements 
ExtendedCamelContext, Cat
     }
 
     @Override
+    public ResourceLoader getResourceLoader() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setResourceLoader(ResourceLoader resourceLoader) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void registerEndpointCallback(EndpointStrategy strategy) {
         throw new UnsupportedOperationException();
     }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorEndpointClearCachedSchemaTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorEndpointClearCachedSchemaTest.java
index 22ee228..7739270 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorEndpointClearCachedSchemaTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorEndpointClearCachedSchemaTest.java
@@ -27,7 +27,8 @@ import org.apache.camel.ContextTestSupport;
 import org.apache.camel.Endpoint;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
-import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.engine.DefaultResourceLoader;
+import org.apache.camel.urlhandler.pd.Handler;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -72,10 +73,9 @@ public class ValidatorEndpointClearCachedSchemaTest extends 
ContextTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
-        String handlerPackageSystemProp = "java.protocol.handler.pkgs";
-        String customUrlHandlerPackage = "org.apache.camel.urlhandler";
-        registerSystemProperty(handlerPackageSystemProp, 
customUrlHandlerPackage, "|");
-        return new DefaultCamelContext();
+        CamelContext context = super.createCamelContext();
+        
context.getRegistry().bind(DefaultResourceLoader.RESOURCE_LOADER_KEY_PREFIX + 
"pd", new Handler());
+        return context;
     }
 
     @Override
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorWithResourceResolverRouteTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorWithResourceResolverRouteTest.java
index ed89826..bc52554 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorWithResourceResolverRouteTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorWithResourceResolverRouteTest.java
@@ -77,7 +77,7 @@ public class ValidatorWithResourceResolverRouteTest extends 
ContextTestSupport {
         // we have to do it here, because we need the context created first
         CatalogManager.getStaticManager().setIgnoreMissingProperties(true);
         CatalogResolver catalogResolver = new CatalogResolver(true);
-        URL catalogUrl = 
ResourceHelper.resolveMandatoryResourceAsUrl(context.getClassResolver(),
+        URL catalogUrl = ResourceHelper.resolveMandatoryResourceAsUrl(context,
                 "org/apache/camel/component/validator/catalog.cat");
         catalogResolver.getCatalog().parseCatalog(catalogUrl);
         LSResourceResolver resourceResolver = new 
CatalogLSResourceResolver(catalogResolver);
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java 
b/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
index 9207b0b..4d9469f 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
@@ -19,23 +19,32 @@ package org.apache.camel.urlhandler.custom;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.spi.Resource;
+import org.apache.camel.support.ResourceResolverSupport;
+
+public class Handler extends ResourceResolverSupport {
+    public Handler() {
+        super("custom");
+    }
 
-public class Handler extends URLStreamHandler {
     @Override
-    protected URLConnection openConnection(URL u) throws IOException {
-        final String echo = u.getHost();
-        return new URLConnection(u) {
+    protected Resource createResource(String location) {
+        return new Resource() {
+            @Override
+            public String getLocation() {
+                return location;
+            }
+
             @Override
-            public void connect() throws IOException {
-                connected = true;
+            public boolean exists() {
+                return true;
             }
 
             @Override
             public InputStream getInputStream() throws IOException {
-                return new ByteArrayInputStream(echo.getBytes());
+                return new 
ByteArrayInputStream(getRemaining(location).getBytes(StandardCharsets.UTF_8));
             }
         };
     }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/pd/Handler.java 
b/core/camel-core/src/test/java/org/apache/camel/urlhandler/pd/Handler.java
index 573496b..f753c92 100644
--- a/core/camel-core/src/test/java/org/apache/camel/urlhandler/pd/Handler.java
+++ b/core/camel-core/src/test/java/org/apache/camel/urlhandler/pd/Handler.java
@@ -19,11 +19,11 @@ package org.apache.camel.urlhandler.pd;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
 import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.camel.spi.Resource;
+import org.apache.camel.support.ResourceResolverSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,81 +32,58 @@ import org.slf4j.LoggerFactory;
  * not fit to the XML document. In the second call a XSD fitting to the XML 
document is returned. Used in
  * org.apache.camel.component.validator.ValidatorEndpointClearCachedSchemaTest
  */
-public class Handler extends URLStreamHandler {
-    private static int counter;
+public class Handler extends ResourceResolverSupport {
     private static final Logger LOG = LoggerFactory.getLogger(Handler.class);
 
-    private final String xsdtemplate1 = "<?xml version=\"1.0\" 
encoding=\"UTF-8\"?>\n" + //
-                                        "<xsd:schema 
targetNamespace=\"http://apache.camel.org/test\"; 
xmlns=\"http://apache.camel.org/test\"; 
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\";>"
-                                        + //
-                                        "    <xsd:complexType 
name=\"TestMessage\">" + //
-                                        "        <xsd:sequence>" + //
-                                        "            <xsd:element 
name=\"Content\" type=\"xsd:string\" />" + // //
-                                        // wrong
-                                        // element
-                                        // name
-                                        // will
-                                        // cause
-                                        // the
-                                        // validation
-                                        // to
-                                        // fail
-                                        "        </xsd:sequence>" + //
-                                        "        <xsd:attribute name=\"attr\" 
type=\"xsd:string\" default=\"xsd1\"/>" + //
-                                        "    </xsd:complexType>" + //
-                                        "    <xsd:element name=\"TestMessage\" 
type=\"TestMessage\" />" + //
-                                        "</xsd:schema>"; //
+    // wrong  element name will cause the validation to fail
+    private static final String XSD_TEMPLATE_1 = "<?xml version=\"1.0\" 
encoding=\"UTF-8\"?>\n" +
+                                                 "<xsd:schema 
targetNamespace=\"http://apache.camel.org/test\"; " +
+                                                 "            
xmlns=\"http://apache.camel.org/test\"; " +
+                                                 "            
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\";>" +
+                                                 "    <xsd:complexType 
name=\"TestMessage\">" +
+                                                 "        <xsd:sequence>" +
+                                                 "            <xsd:element 
name=\"Content\" type=\"xsd:string\" />" +
+                                                 "        </xsd:sequence>" +
+                                                 "        <xsd:attribute 
name=\"attr\" type=\"xsd:string\" default=\"xsd1\"/>" +
+                                                 "    </xsd:complexType>" +
+                                                 "    <xsd:element 
name=\"TestMessage\" type=\"TestMessage\" />" +
+                                                 "</xsd:schema>";
 
-    private final String xsdtemplate2 = xsdtemplate1.replace("\"Content\"", 
"\"MessageContent\""); // correct
-    // element
-    // name
-    // -->
-    // validation
-    // will
-    // be
-    // correct
+    // correct element name, the validation will be corerct
+    private static final String XSD_TEMPLATE_2 = 
XSD_TEMPLATE_1.replace("\"Content\"", "\"MessageContent\"");
 
-    private byte[] xsd1 = xsdtemplate1.getBytes(StandardCharsets.UTF_8);
-    private byte[] xsd2 = xsdtemplate2.getBytes(StandardCharsets.UTF_8);
+    private final AtomicInteger counter;
 
-    @Override
-    protected URLConnection openConnection(URL u) throws IOException {
-        if (getCounter() == 0) {
-            LOG.info("resolved XSD1");
-            incrementCounter();
-            return new URLConnection(u) {
-                @Override
-                public void connect() throws IOException {
-                    connected = true;
-                }
-
-                @Override
-                public InputStream getInputStream() throws IOException {
-                    return new ByteArrayInputStream(xsd1);
-                }
-            };
-        } else {
-            LOG.info("resolved XSD2");
-            incrementCounter();
-            return new URLConnection(u) {
-                @Override
-                public void connect() throws IOException {
-                    connected = true;
-                }
+    public Handler() {
+        super("pd");
 
-                @Override
-                public InputStream getInputStream() throws IOException {
-                    return new ByteArrayInputStream(xsd2);
-                }
-            };
-        }
+        this.counter = new AtomicInteger();
     }
 
-    public static synchronized void incrementCounter() {
-        counter++;
-    }
+    @Override
+    protected Resource createResource(String location) {
+        return new Resource() {
+            @Override
+            public String getLocation() {
+                return location;
+            }
+
+            @Override
+            public boolean exists() {
+                return true;
+            }
 
-    public static synchronized int getCounter() {
-        return counter;
+            @Override
+            public InputStream getInputStream() throws IOException {
+                if (counter.getAndIncrement() == 0) {
+                    LOG.info("resolved XSD1");
+                    return new 
ByteArrayInputStream(XSD_TEMPLATE_1.getBytes(StandardCharsets.UTF_8));
+                } else {
+                    LOG.info("resolved XSD2");
+
+                    return new 
ByteArrayInputStream(XSD_TEMPLATE_2.getBytes(StandardCharsets.UTF_8));
+                }
+            }
+        };
     }
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/util/ResourceHelperTest.java 
b/core/camel-core/src/test/java/org/apache/camel/util/ResourceHelperTest.java
index 9b7351e..7668d3a 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/util/ResourceHelperTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/util/ResourceHelperTest.java
@@ -20,7 +20,6 @@ import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.net.URL;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -28,14 +27,18 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.TestSupport;
-import org.apache.camel.converter.IOConverter;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.spi.Registry;
 import org.apache.camel.support.DefaultRegistry;
 import org.apache.camel.support.ResourceHelper;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  *
@@ -211,7 +214,8 @@ public class ResourceHelperTest extends TestSupport {
             ResourceHelper.resolveMandatoryResourceAsInputStream(context, 
"classpath:notfound.txt");
             fail("Should not find file");
         } catch (FileNotFoundException e) {
-            assertEquals("Cannot find resource: classpath:notfound.txt in 
classpath for URI: classpath:notfound.txt",
+            assertEquals(
+                    "Cannot find resource: classpath:notfound.txt for URI: 
classpath:notfound.txt",
                     e.getMessage());
         }
 
@@ -223,8 +227,7 @@ public class ResourceHelperTest extends TestSupport {
         CamelContext context = new DefaultCamelContext();
         context.start();
 
-        URL url = 
ResourceHelper.resolveMandatoryResourceAsUrl(context.getClassResolver(),
-                "file:src/test/resources/log4j2.properties");
+        URL url = ResourceHelper.resolveMandatoryResourceAsUrl(context, 
"file:src/test/resources/log4j2.properties");
         assertNotNull(url);
 
         String text = context.getTypeConverter().convertTo(String.class, url);
@@ -239,7 +242,7 @@ public class ResourceHelperTest extends TestSupport {
         CamelContext context = new DefaultCamelContext();
         context.start();
 
-        URL url = 
ResourceHelper.resolveMandatoryResourceAsUrl(context.getClassResolver(), 
"classpath:log4j2.properties");
+        URL url = ResourceHelper.resolveMandatoryResourceAsUrl(context, 
"classpath:log4j2.properties");
         assertNotNull(url);
 
         String text = context.getTypeConverter().convertTo(String.class, url);
@@ -250,72 +253,6 @@ public class ResourceHelperTest extends TestSupport {
     }
 
     @Test
-    public void testLoadCustomUrlasInputStream() throws Exception {
-        CamelContext context = new DefaultCamelContext();
-        context.start();
-
-        String handlerPackageSystemProp = "java.protocol.handler.pkgs";
-        String customUrlHandlerPackage = "org.apache.camel.urlhandler";
-
-        registerSystemProperty(handlerPackageSystemProp, 
customUrlHandlerPackage, "|");
-
-        InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(context, "custom://hello");
-        assertNotNull(is);
-
-        assertEquals("hello", IOConverter.toString(IOHelper.buffered(new 
InputStreamReader(is, "UTF-8"))));
-
-        context.stop();
-    }
-
-    @Test
-    public void testLoadCustomUrlasInputStreamFail() throws Exception {
-        CamelContext context = new DefaultCamelContext();
-        context.start();
-
-        try {
-            InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(context, "custom://hello");
-            assertNotNull(is);
-        } catch (Exception e) {
-            assertEquals("unknown protocol: custom", e.getMessage());
-        }
-
-        context.stop();
-    }
-
-    @Test
-    public void testLoadCustomUrl() throws Exception {
-        CamelContext context = new DefaultCamelContext();
-        context.start();
-
-        String handlerPackageSystemProp = "java.protocol.handler.pkgs";
-        String customUrlHandlerPackage = "org.apache.camel.urlhandler";
-        registerSystemProperty(handlerPackageSystemProp, 
customUrlHandlerPackage, "|");
-
-        URL url = 
ResourceHelper.resolveResourceAsUrl(context.getClassResolver(), 
"custom://hello");
-        assertNotNull(url);
-
-        String text = context.getTypeConverter().convertTo(String.class, url);
-        assertNotNull(text);
-        assertTrue(text.contains("hello"));
-
-        context.stop();
-    }
-
-    @Test
-    public void testLoadCustomUrlFail() throws Exception {
-        CamelContext context = new DefaultCamelContext();
-        context.start();
-
-        try {
-            ResourceHelper.resolveResourceAsUrl(context.getClassResolver(), 
"custom://hello");
-        } catch (Exception e) {
-            assertEquals("unknown protocol: custom", e.getMessage());
-        }
-
-        context.stop();
-    }
-
-    @Test
     public void testIsHttp() throws Exception {
         assertFalse(ResourceHelper.isHttpUri("direct:foo"));
         assertFalse(ResourceHelper.isHttpUri(""));
@@ -342,8 +279,8 @@ public class ResourceHelperTest extends TestSupport {
         assertEquals("file:", ResourceHelper.getScheme("file:myfile.txt"));
         assertEquals("classpath:", 
ResourceHelper.getScheme("classpath:myfile.txt"));
         assertEquals("http:", ResourceHelper.getScheme("http:www.foo.com"));
-        assertEquals(null, ResourceHelper.getScheme("www.foo.com"));
-        assertEquals(null, ResourceHelper.getScheme("myfile.txt"));
+        assertNull(ResourceHelper.getScheme("www.foo.com"));
+        assertNull(ResourceHelper.getScheme("myfile.txt"));
     }
 
     @Test
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/util/ResourceLoaderTest.java 
b/core/camel-core/src/test/java/org/apache/camel/util/ResourceLoaderTest.java
new file mode 100644
index 0000000..89c084e
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/util/ResourceLoaderTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.camel.TestSupport;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.spi.Resource;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.util.FileUtil.copyFile;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ResourceLoaderTest extends TestSupport {
+
+    @Test
+    public void testLoadFile() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource = 
context.getResourceLoader().resolveResource("file:src/test/resources/log4j2.properties");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String text = context.getTypeConverter().convertTo(String.class, 
is);
+            assertNotNull(text);
+            assertTrue(text.contains("rootLogger"));
+        }
+    }
+
+    @Test
+    public void testLoadFileWithSpace() throws Exception {
+        createDirectory("target/data/my space");
+        copyFile(new File("src/test/resources/log4j2.properties"), new 
File("target/data/my space/log4j2.properties"));
+
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource = 
context.getResourceLoader().resolveResource("file:target/data/my%20space/log4j2.properties");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String text = context.getTypeConverter().convertTo(String.class, 
is);
+            assertNotNull(text);
+            assertTrue(text.contains("rootLogger"));
+        }
+    }
+
+    @Test
+    public void testLoadClasspath() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource = 
context.getResourceLoader().resolveResource("classpath:log4j2.properties");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String text = context.getTypeConverter().convertTo(String.class, 
is);
+            assertNotNull(text);
+            assertTrue(text.contains("rootLogger"));
+        }
+    }
+
+    @Test
+    public void testLoadClasspathDefault() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource = 
context.getResourceLoader().resolveResource("log4j2.properties");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String text = context.getTypeConverter().convertTo(String.class, 
is);
+            assertNotNull(text);
+            assertTrue(text.contains("rootLogger"));
+        }
+    }
+
+    @Test
+    public void testLoadRegistry() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind("myBean", "This is a log4j logging 
configuration file");
+
+        Resource resource = 
context.getResourceLoader().resolveResource("ref:myBean");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String text = context.getTypeConverter().convertTo(String.class, 
is);
+            assertNotNull(text);
+            assertTrue(text.contains("log4j"));
+        }
+    }
+
+    @Test
+    public void testLoadBeanDoubleColon() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind("myBean", new 
AtomicReference<InputStream>(new ByteArrayInputStream("a".getBytes())));
+
+        Resource resource = 
context.getResourceLoader().resolveResource("bean:myBean::get");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String text = context.getTypeConverter().convertTo(String.class, 
is);
+            assertNotNull(text);
+            assertEquals("a", text);
+        }
+    }
+
+    @Test
+    public void testLoadBeanDoubleColonLong() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind("my.company.MyClass",
+                new AtomicReference<InputStream>(new 
ByteArrayInputStream("a".getBytes())));
+
+        Resource resource = 
context.getResourceLoader().resolveResource("bean:my.company.MyClass::get");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String text = context.getTypeConverter().convertTo(String.class, 
is);
+            assertNotNull(text);
+            assertEquals("a", text);
+        }
+    }
+
+    @Test
+    public void testLoadBeanDot() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.getRegistry().bind("myBean", new 
AtomicReference<InputStream>(new ByteArrayInputStream("a".getBytes())));
+
+        Resource resource = 
context.getResourceLoader().resolveResource("bean:myBean.get");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String text = context.getTypeConverter().convertTo(String.class, 
is);
+            assertNotNull(text);
+            assertEquals("a", text);
+        }
+    }
+
+    @Test
+    public void testLoadFileNotFound() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource = 
context.getResourceLoader().resolveResource("file:src/test/resources/notfound.txt");
+
+        assertFalse(resource.exists());
+    }
+
+    @Test
+    public void testLoadClasspathNotFound() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource = 
context.getResourceLoader().resolveResource("classpath:notfound.txt");
+
+        assertFalse(resource.exists());
+    }
+
+    @Test
+    public void testLoadFileAsUrl() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource = 
context.getResourceLoader().resolveResource("file:src/test/resources/log4j2.properties");
+
+        URL url = resource.getURI().toURL();
+        assertNotNull(url);
+
+        String text = context.getTypeConverter().convertTo(String.class, url);
+        assertNotNull(text);
+        assertTrue(text.contains("rootLogger"));
+
+        context.stop();
+    }
+
+    @Test
+    public void testLoadClasspathAsUrl() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource = 
context.getResourceLoader().resolveResource("classpath:log4j2.properties");
+
+        URL url = resource.getURI().toURL();
+        assertNotNull(url);
+
+        String text = context.getTypeConverter().convertTo(String.class, url);
+        assertNotNull(text);
+        assertTrue(text.contains("rootLogger"));
+
+        context.stop();
+    }
+}
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/ResourceHelper.java 
b/core/camel-support/src/main/java/org/apache/camel/support/ResourceHelper.java
index 76a4aaa..11d549d 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/ResourceHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/ResourceHelper.java
@@ -18,17 +18,16 @@ package org.apache.camel.support;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
-import java.net.URLConnection;
 import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.LinkedHashSet;
@@ -39,12 +38,12 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.camel.CamelContext;
-import org.apache.camel.Exchange;
-import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.util.AntPathMatcher;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.URISupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -110,38 +109,10 @@ public final class ResourceHelper {
      * @throws java.io.IOException is thrown if the resource file could not be 
found or loaded as {@link InputStream}
      */
     public static InputStream 
resolveMandatoryResourceAsInputStream(CamelContext camelContext, String uri) 
throws IOException {
-        if (uri.startsWith("ref:")) {
-            String ref = uri.substring(4);
-            String value = CamelContextHelper.mandatoryLookup(camelContext, 
ref, String.class);
-            return new ByteArrayInputStream(value.getBytes());
-        } else if (uri.startsWith("bean:")) {
-            String bean = uri.substring(5);
-            Exchange dummy = new DefaultExchange(camelContext);
-            Object out = 
camelContext.resolveLanguage("bean").createExpression(bean).evaluate(dummy, 
Object.class);
-            if (dummy.getException() != null) {
-                IOException io = new IOException("Cannot find resource: " + 
uri + " from calling the bean");
-                io.initCause(dummy.getException());
-                throw io;
-            }
-            if (out != null) {
-                InputStream is = 
camelContext.getTypeConverter().tryConvertTo(InputStream.class, dummy, out);
-                if (is == null) {
-                    String text = 
camelContext.getTypeConverter().tryConvertTo(String.class, dummy, out);
-                    if (text != null) {
-                        return new ByteArrayInputStream(text.getBytes());
-                    }
-                } else {
-                    return is;
-                }
-            } else {
-                throw new IOException("Cannot find resource: " + uri + " from 
calling the bean");
-            }
-        }
-
-        InputStream is = 
resolveResourceAsInputStream(camelContext.getClassResolver(), uri);
+        InputStream is = resolveResourceAsInputStream(camelContext, uri);
         if (is == null) {
             String resolvedName = resolveUriPath(uri);
-            throw new FileNotFoundException("Cannot find resource: " + 
resolvedName + " in classpath for URI: " + uri);
+            throw new FileNotFoundException("Cannot find resource: " + 
resolvedName + " for URI: " + uri);
         } else {
             return is;
         }
@@ -159,96 +130,25 @@ public final class ResourceHelper {
      * @throws java.io.IOException is thrown if error loading the resource
      */
     public static InputStream resolveResourceAsInputStream(CamelContext 
camelContext, String uri) throws IOException {
-        if (uri.startsWith("ref:")) {
-            String ref = uri.substring(4);
-            String value = CamelContextHelper.mandatoryLookup(camelContext, 
ref, String.class);
-            return new ByteArrayInputStream(value.getBytes());
-        } else if (uri.startsWith("bean:")) {
-            String bean = uri.substring(5);
-            Exchange dummy = new DefaultExchange(camelContext);
-            Object out = 
camelContext.resolveLanguage("bean").createExpression(bean).evaluate(dummy, 
Object.class);
-            if (dummy.getException() != null) {
-                IOException io = new IOException("Cannot find resource: " + 
uri + " from calling the bean");
-                io.initCause(dummy.getException());
-                throw io;
-            }
-            if (out != null) {
-                InputStream is = 
camelContext.getTypeConverter().tryConvertTo(InputStream.class, dummy, out);
-                if (is == null) {
-                    String text = 
camelContext.getTypeConverter().tryConvertTo(String.class, dummy, out);
-                    if (text != null) {
-                        return new ByteArrayInputStream(text.getBytes());
-                    }
-                } else {
-                    return is;
-                }
-            } else {
-                throw new IOException("Cannot find resource: " + uri + " from 
calling the bean");
-            }
-        }
-        return resolveResourceAsInputStream(camelContext.getClassResolver(), 
uri);
-    }
+        final ExtendedCamelContext ecc = 
camelContext.adapt(ExtendedCamelContext.class);
+        final ResourceLoader loader = ecc.getResourceLoader();
+        final Resource resource = loader.resolveResource(uri);
 
-    /**
-     * Resolves the resource.
-     * <p/>
-     * If possible recommended to use {@link 
#resolveMandatoryResourceAsUrl(org.apache.camel.spi.ClassResolver, String)}
-     *
-     * @param  classResolver       the class resolver to load the resource 
from the classpath
-     * @param  uri                 URI of the resource
-     * @return                     the resource as an {@link InputStream}. 
Remember to close this stream after usage. Or
-     *                             <tt>null</tt> if not found.
-     * @throws java.io.IOException is thrown if error loading the resource
-     */
-    public static InputStream resolveResourceAsInputStream(ClassResolver 
classResolver, String uri) throws IOException {
-        if (uri.startsWith("file:")) {
-            uri = StringHelper.after(uri, "file:");
-            uri = tryDecodeUri(uri);
-            LOG.trace("Loading resource: {} from file system", uri);
-            return new FileInputStream(uri);
-        } else if (uri.startsWith("http:")) {
-            URL url = new URL(uri);
-            LOG.trace("Loading resource: {} from HTTP", uri);
-            URLConnection con = url.openConnection();
-            con.setUseCaches(false);
-            try {
-                return con.getInputStream();
-            } catch (IOException e) {
-                // close the http connection to avoid
-                // leaking gaps in case of an exception
-                if (con instanceof HttpURLConnection) {
-                    ((HttpURLConnection) con).disconnect();
-                }
-                throw e;
-            }
-        } else if (uri.startsWith("classpath:")) {
-            uri = StringHelper.after(uri, "classpath:");
-            uri = tryDecodeUri(uri);
-        } else if (uri.contains(":")) {
-            LOG.trace("Loading resource: {} with UrlHandler for protocol {}", 
uri, uri.split(":")[0]);
-            URL url = new URL(uri);
-            URLConnection con = url.openConnection();
-            return con.getInputStream();
-        }
-
-        // load from classpath by default
-        String resolvedName = resolveUriPath(uri);
-        LOG.trace("Loading resource: {} from classpath", resolvedName);
-        return classResolver.loadResourceAsStream(resolvedName);
+        return resource.getInputStream();
     }
 
     /**
      * Resolves the mandatory resource.
      *
-     * @param  classResolver                  the class resolver to load the 
resource from the classpath
+     * @param  camelContext                   the camel context
      * @param  uri                            uri of the resource
      * @return                                the resource as an {@link 
java.net.URL}.
      * @throws java.io.FileNotFoundException  is thrown if the resource file 
could not be found
      * @throws java.net.MalformedURLException if the URI is malformed
      */
-    public static URL resolveMandatoryResourceAsUrl(ClassResolver 
classResolver, String uri)
+    public static URL resolveMandatoryResourceAsUrl(CamelContext camelContext, 
String uri)
             throws FileNotFoundException, MalformedURLException {
-        URL url = resolveResourceAsUrl(classResolver, uri);
+        URL url = resolveResourceAsUrl(camelContext, uri);
         if (url == null) {
             String resolvedName = resolveUriPath(uri);
             throw new FileNotFoundException("Cannot find resource: " + 
resolvedName + " in classpath for URI: " + uri);
@@ -260,37 +160,17 @@ public final class ResourceHelper {
     /**
      * Resolves the resource.
      *
-     * @param  classResolver                  the class resolver to load the 
resource from the classpath
+     * @param  camelContext                   the camel context
      * @param  uri                            uri of the resource
      * @return                                the resource as an {@link 
java.net.URL}. Or <tt>null</tt> if not found.
      * @throws java.net.MalformedURLException if the URI is malformed
      */
-    public static URL resolveResourceAsUrl(ClassResolver classResolver, String 
uri) throws MalformedURLException {
-        if (uri.startsWith("file:")) {
-            // check if file exists first
-            String name = StringHelper.after(uri, "file:");
-            uri = tryDecodeUri(uri);
-            LOG.trace("Loading resource: {} from file system", uri);
-            File file = new File(name);
-            if (!file.exists()) {
-                return null;
-            }
-            return new URL(uri);
-        } else if (uri.startsWith("http:")) {
-            LOG.trace("Loading resource: {} from HTTP", uri);
-            return new URL(uri);
-        } else if (uri.startsWith("classpath:")) {
-            uri = StringHelper.after(uri, "classpath:");
-            uri = tryDecodeUri(uri);
-        } else if (uri.contains(":")) {
-            LOG.trace("Loading resource: {} with UrlHandler for protocol {}", 
uri, uri.split(":")[0]);
-            return new URL(uri);
-        }
+    public static URL resolveResourceAsUrl(CamelContext camelContext, String 
uri) throws MalformedURLException {
+        final ExtendedCamelContext ecc = 
camelContext.adapt(ExtendedCamelContext.class);
+        final ResourceLoader loader = ecc.getResourceLoader();
+        final Resource resource = loader.resolveResource(uri);
 
-        // load from classpath by default
-        String resolvedName = resolveUriPath(uri);
-        LOG.trace("Loading resource: {} from classpath", resolvedName);
-        return classResolver.loadResourceAsURL(resolvedName);
+        return resource.getURL();
     }
 
     /**
@@ -393,4 +273,44 @@ public final class ResourceHelper {
                     .collect(Collectors.toCollection(LinkedHashSet::new));
         }
     }
+
+    /**
+     * Create a {@link Resource} from bytes.
+     *
+     * @param  location a virtual location
+     * @param  content  the resource content
+     * @return          a resource wrapping the given byte array
+     */
+    public static Resource fromBytes(String location, byte[] content) {
+        return new Resource() {
+            @Override
+            public String getLocation() {
+                return location;
+            }
+
+            @Override
+            public boolean exists() {
+                return true;
+            }
+
+            @Override
+            public InputStream getInputStream() throws IOException {
+                return new ByteArrayInputStream(content);
+            }
+        };
+    }
+
+    /**
+     * Create a {@link Resource} from a {@link String}.
+     * </p>
+     * The implementation delegates to {@link #fromBytes(String, byte[])} by 
encoding the string as bytes with
+     * {@link String#getBytes(Charset)} and {@link StandardCharsets#UTF_8} as 
charset.
+     *
+     * @param  location a virtual location
+     * @param  content  the resource content
+     * @return          a resource wrapping the given {@link String}
+     */
+    public static Resource fromString(String location, String content) {
+        return fromBytes(location, content.getBytes(StandardCharsets.UTF_8));
+    }
 }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/ResourceResolverSupport.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/ResourceResolverSupport.java
new file mode 100644
index 0000000..9968641
--- /dev/null
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/ResourceResolverSupport.java
@@ -0,0 +1,83 @@
+/*
+ * 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.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceResolver;
+import org.apache.camel.support.service.ServiceSupport;
+import org.apache.camel.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base class for {@link ResourceResolver} implementations.
+ */
+public abstract class ResourceResolverSupport extends ServiceSupport 
implements ResourceResolver {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ResourceResolverSupport.class);
+
+    private final String scheme;
+    private CamelContext camelContext;
+
+    protected ResourceResolverSupport(String scheme) {
+        this.scheme = scheme;
+    }
+
+    public String getSupportedScheme() {
+        return scheme;
+    }
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    public Resource resolve(String location) {
+        if (!location.startsWith(getSupportedScheme() + ":")) {
+            throw new IllegalArgumentException("Unsupported scheme: " + 
location);
+        }
+
+        return createResource(location);
+    }
+
+    protected abstract Resource createResource(String location);
+
+    protected String tryDecodeUri(String uri) {
+        try {
+            // try to decode as the uri may contain %20 for spaces etc
+            uri = URLDecoder.decode(uri, StandardCharsets.UTF_8.name());
+        } catch (Exception e) {
+            LOGGER.trace("Error URL decoding uri using UTF-8 encoding: {}. 
This exception is ignored.", uri);
+            // ignore
+        }
+
+        return uri;
+    }
+
+    protected String getRemaining(String location) {
+        return StringHelper.after(location, getSupportedScheme() + ":");
+    }
+}
diff --git 
a/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoaderTest.java
 
b/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoaderTest.java
index f14d635..20803af 100644
--- 
a/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoaderTest.java
+++ 
b/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoaderTest.java
@@ -44,7 +44,7 @@ public class JavaRoutesBuilderLoaderTest {
     })
     void testLoadRoutes(String location) throws Exception {
         try (DefaultCamelContext context = new DefaultCamelContext()) {
-            Resource resource = 
Resource.fromClasspath(JavaRoutesBuilderLoaderTest.class, location);
+            Resource resource = 
context.getResourceLoader().resolveResource(location);
             Collection<RoutesBuilder> builders = 
context.getRoutesLoader().findRoutesBuilders(resource);
 
             assertThat(builders).hasSize(1);
@@ -68,7 +68,7 @@ public class JavaRoutesBuilderLoaderTest {
         final String location = "/routes/MyRoutesWithNestedClass.java";
 
         try (DefaultCamelContext context = new DefaultCamelContext()) {
-            Resource resource = 
Resource.fromClasspath(JavaRoutesBuilderLoaderTest.class, location);
+            Resource resource = 
context.getResourceLoader().resolveResource(location);
             Collection<RoutesBuilder> builders = 
context.getRoutesLoader().findRoutesBuilders(resource);
 
             assertThat(builders).hasSize(1);
@@ -94,7 +94,7 @@ public class JavaRoutesBuilderLoaderTest {
         final String location = "/routes/MyRoutesWithRestConfiguration.java";
 
         try (DefaultCamelContext context = new DefaultCamelContext()) {
-            Resource resource = 
Resource.fromClasspath(JavaRoutesBuilderLoaderTest.class, location);
+            Resource resource = 
context.getResourceLoader().resolveResource(location);
             Collection<RoutesBuilder> builders = 
context.getRoutesLoader().findRoutesBuilders(resource);
 
             assertThat(builders).hasSize(1);
@@ -113,7 +113,7 @@ public class JavaRoutesBuilderLoaderTest {
         final String location = "/routes/MyRoutesWithModel.java";
 
         try (DefaultCamelContext context = new DefaultCamelContext()) {
-            Resource resource = 
Resource.fromClasspath(JavaRoutesBuilderLoaderTest.class, location);
+            Resource resource = 
context.getResourceLoader().resolveResource(location);
             Collection<RoutesBuilder> builders = 
context.getRoutesLoader().findRoutesBuilders(resource);
 
             assertThat(builders).hasSize(1);
diff --git 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadRestTest.java
 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadRestTest.java
index 3d14a68..76a20c4 100644
--- 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadRestTest.java
+++ 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadRestTest.java
@@ -16,11 +16,11 @@
  */
 package org.apache.camel.dsl.xml.io;
 
+import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.component.rest.DummyRestConsumerFactory;
 import org.apache.camel.impl.DefaultCamelContext;
-import org.apache.camel.model.LoadRouteFromXmlTest;
 import org.apache.camel.spi.Resource;
 import org.junit.jupiter.api.Test;
 
@@ -57,7 +57,10 @@ public class XmlLoadRestTest {
             foo.assertIsSatisfied();
 
             // load rest from XML and add them to the existing camel context
-            Resource resource = 
Resource.fromClasspath(LoadRouteFromXmlTest.class, "barRest.xml");
+            ExtendedCamelContext ecc = 
context.adapt(ExtendedCamelContext.class);
+            Resource resource = ecc.getResourceLoader().resolveResource(
+                    "/org/apache/camel/dsl/xml/io/barRest.xml");
+
             context.getRoutesLoader().loadRoutes(resource);
 
             assertEquals(2, context.getRoutes().size());
diff --git 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadTest.java
 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadTest.java
index 2819445..a147e47 100644
--- 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadTest.java
+++ 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadTest.java
@@ -20,7 +20,6 @@ import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.impl.DefaultCamelContext;
-import org.apache.camel.model.LoadRouteFromXmlTest;
 import org.apache.camel.spi.Resource;
 import org.junit.jupiter.api.Test;
 
@@ -54,7 +53,8 @@ public class XmlLoadTest {
             // START SNIPPET: e1
             // load route from XML and add them to the existing camel context
             ExtendedCamelContext ecc = 
context.adapt(ExtendedCamelContext.class);
-            Resource resource = 
Resource.fromClasspath(LoadRouteFromXmlTest.class, "barRoute.xml");
+            Resource resource = ecc.getResourceLoader().resolveResource(
+                    "/org/apache/camel/dsl/xml/io/barRoute.xml");
 
             ecc.getRoutesLoader().loadRoutes(resource);
 
diff --git 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoaderTest.java
 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoaderTest.java
index c1fe3e7..ef73bee 100644
--- 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoaderTest.java
+++ 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoaderTest.java
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.xml.io;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.spi.Resource;
+import org.apache.camel.support.ResourceHelper;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -38,7 +39,7 @@ public class XmlRoutesBuilderLoaderTest {
                          + "   </route>"
                          + "</routes>";
 
-        Resource resource = Resource.fromString("in-memory.xml", content);
+        Resource resource = ResourceHelper.fromString("in-memory.xml", 
content);
         RouteBuilder builder = (RouteBuilder) new 
XmlRoutesBuilderLoader().loadRoutesBuilder(resource);
         builder.setContext(new DefaultCamelContext());
         builder.configure();
@@ -57,7 +58,7 @@ public class XmlRoutesBuilderLoaderTest {
                          + "  </rest>"
                          + "</rests>";
 
-        Resource resource = Resource.fromString("in-memory.xml", content);
+        Resource resource = ResourceHelper.fromString("in-memory.xml", 
content);
         RouteBuilder builder = (RouteBuilder) new 
XmlRoutesBuilderLoader().loadRoutesBuilder(resource);
         builder.setContext(new DefaultCamelContext());
         builder.configure();
@@ -79,7 +80,7 @@ public class XmlRoutesBuilderLoaderTest {
                          + "  </routeTemplate>"
                          + "</routeTemplates>";
 
-        Resource resource = Resource.fromString("in-memory.xml", content);
+        Resource resource = ResourceHelper.fromString("in-memory.xml", 
content);
         RouteBuilder builder = (RouteBuilder) new 
XmlRoutesBuilderLoader().loadRoutesBuilder(resource);
         builder.setContext(new DefaultCamelContext());
         builder.configure();
diff --git 
a/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlLoadRestTest.java
 
b/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlLoadRestTest.java
index c4476e9..1d00d22 100644
--- 
a/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlLoadRestTest.java
+++ 
b/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlLoadRestTest.java
@@ -16,11 +16,11 @@
  */
 package org.apache.camel.dsl.xml.jaxb;
 
+import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.component.rest.DummyRestConsumerFactory;
 import org.apache.camel.impl.DefaultCamelContext;
-import org.apache.camel.model.LoadRouteFromXmlTest;
 import org.apache.camel.spi.Resource;
 import org.junit.jupiter.api.Test;
 
@@ -57,7 +57,10 @@ public class JaxbXmlLoadRestTest {
             foo.assertIsSatisfied();
 
             // load rest from XML and add them to the existing camel context
-            Resource resource = 
Resource.fromClasspath(LoadRouteFromXmlTest.class, "barRest.xml");
+            ExtendedCamelContext ecc = 
context.adapt(ExtendedCamelContext.class);
+            Resource resource = ecc.getResourceLoader().resolveResource(
+                    "/org/apache/camel/dsl/xml/jaxb/barRest.xml");
+
             context.getRoutesLoader().loadRoutes(resource);
 
             assertEquals(2, context.getRoutes().size());
diff --git 
a/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlLoadTest.java
 
b/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlLoadTest.java
index bc02eee..dbe7210 100644
--- 
a/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlLoadTest.java
+++ 
b/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlLoadTest.java
@@ -20,7 +20,6 @@ import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.impl.DefaultCamelContext;
-import org.apache.camel.model.LoadRouteFromXmlTest;
 import org.apache.camel.spi.Resource;
 import org.junit.jupiter.api.Test;
 
@@ -54,7 +53,8 @@ public class JaxbXmlLoadTest {
             // START SNIPPET: e1
             // load route from XML and add them to the existing camel context
             ExtendedCamelContext ecc = 
context.adapt(ExtendedCamelContext.class);
-            Resource resource = 
Resource.fromClasspath(LoadRouteFromXmlTest.class, "barRoute.xml");
+            Resource resource = ecc.getResourceLoader().resolveResource(
+                    "/org/apache/camel/dsl/xml/jaxb/barRoute.xml");
 
             ecc.getRoutesLoader().loadRoutes(resource);
 
diff --git 
a/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlRoutesBuilderLoaderTest.java
 
b/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlRoutesBuilderLoaderTest.java
index 6804f53..7b7b4c4 100644
--- 
a/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlRoutesBuilderLoaderTest.java
+++ 
b/dsl/camel-xml-jaxb-dsl/src/test/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlRoutesBuilderLoaderTest.java
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.xml.jaxb;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.spi.Resource;
+import org.apache.camel.support.ResourceHelper;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -38,7 +39,7 @@ public class JaxbXmlRoutesBuilderLoaderTest {
                          + "   </route>"
                          + "</routes>";
 
-        Resource resource = Resource.fromString("in-memory.xml", content);
+        Resource resource = ResourceHelper.fromString("in-memory.xml", 
content);
 
         JaxbXmlRoutesBuilderLoader loader = new JaxbXmlRoutesBuilderLoader();
         loader.setCamelContext(new DefaultCamelContext());
@@ -61,7 +62,7 @@ public class JaxbXmlRoutesBuilderLoaderTest {
                          + "  </rest>"
                          + "</rests>";
 
-        Resource resource = Resource.fromString("in-memory.xml", content);
+        Resource resource = ResourceHelper.fromString("in-memory.xml", 
content);
 
         JaxbXmlRoutesBuilderLoader loader = new JaxbXmlRoutesBuilderLoader();
         loader.setCamelContext(new DefaultCamelContext());
@@ -87,7 +88,7 @@ public class JaxbXmlRoutesBuilderLoaderTest {
                          + "  </routeTemplate>"
                          + "</routeTemplates>";
 
-        Resource resource = Resource.fromString("in-memory.xml", content);
+        Resource resource = ResourceHelper.fromString("in-memory.xml", 
content);
 
         JaxbXmlRoutesBuilderLoader loader = new JaxbXmlRoutesBuilderLoader();
         loader.setCamelContext(new DefaultCamelContext());
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/YamlTestSupport.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/YamlTestSupport.groovy
index f982c17..5427bc7 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/YamlTestSupport.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/YamlTestSupport.groovy
@@ -27,6 +27,7 @@ import org.apache.camel.component.mock.MockEndpoint
 import org.apache.camel.impl.DefaultCamelContext
 import org.apache.camel.spi.HasCamelContext
 import org.apache.camel.spi.Resource
+import org.apache.camel.support.ResourceHelper
 import spock.lang.AutoCleanup
 import spock.lang.Specification
 
@@ -62,7 +63,7 @@ class YamlTestSupport extends Specification implements 
HasCamelContext {
 
         loadRoutes(
             resources.collect {
-                it -> Resource.fromString("route-${index++}.yaml", 
it.stripIndent())
+                it -> ResourceHelper.fromString("route-${index++}.yaml", 
it.stripIndent())
             }
         )
     }
@@ -89,7 +90,7 @@ class YamlTestSupport extends Specification implements 
HasCamelContext {
     }
 
     static Resource asResource(String location, String content) {
-        return Resource.fromString(
+        return ResourceHelper.fromString(
                 location.endsWith('.yaml') ? location : location + '.yaml',
                 content.stripIndent()
         )
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpiGeneratorMojo.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpiGeneratorMojo.java
index db785ca..3e23588 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpiGeneratorMojo.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpiGeneratorMojo.java
@@ -127,8 +127,11 @@ public class SpiGeneratorMojo extends 
AbstractGeneratorMojo {
             }
             DotName sfaName = sfa.target().asClass().name();
             for (AnnotationInstance annotation : 
index.getAnnotations(sfaName)) {
-                if (annotation.target().kind() != Kind.CLASS
-                        || annotation.target().asClass().nestingType() != 
NestingType.TOP_LEVEL) {
+                if (annotation.target().kind() != Kind.CLASS) {
+                    continue;
+                }
+                if (annotation.target().asClass().nestingType() != 
NestingType.TOP_LEVEL
+                        && annotation.target().asClass().nestingType() != 
NestingType.INNER) {
                     continue;
                 }
                 String className = 
annotation.target().asClass().name().toString();
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java 
b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/ResourceResolver.java
similarity index 51%
copy from 
core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
copy to 
tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/ResourceResolver.java
index 9207b0b..0ea471c 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/urlhandler/custom/Handler.java
+++ 
b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/ResourceResolver.java
@@ -14,29 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.urlhandler.custom;
+package org.apache.camel.spi.annotations;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
-public class Handler extends URLStreamHandler {
-    @Override
-    protected URLConnection openConnection(URL u) throws IOException {
-        final String echo = u.getHost();
-        return new URLConnection(u) {
-            @Override
-            public void connect() throws IOException {
-                connected = true;
-            }
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Target({ ElementType.TYPE })
+@ServiceFactory("resource-resolver")
+public @interface ResourceResolver {
+
+    String value();
 
-            @Override
-            public InputStream getInputStream() throws IOException {
-                return new ByteArrayInputStream(echo.getBytes());
-            }
-        };
-    }
 }

Reply via email to