CAMEL-15184: PackageScanClassResolver will now skip abstract classes in its findImplementations method.
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
index 3dd25b4..04d8192 100644
--- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
@@ -292,13 +292,8 @@
         Map<String, Class<?>> result = new HashMap<>();
         Set<Class<?>> classes = getCamelContext().adapt(ExtendedCamelContext.class).getPackageScanClassResolver().findImplementations(AbstractSObjectBase.class, packages);
         for (Class<?> aClass : classes) {
-            // findImplementations also returns AbstractSObjectBase for some
-            // reason!!!
-            if (AbstractSObjectBase.class != aClass) {
-                result.put(aClass.getSimpleName(), aClass);
-            }
+            result.put(aClass.getSimpleName(), aClass);
         }
-
         return result;
     }
 
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/PackageScanClassResolver.java b/core/camel-api/src/main/java/org/apache/camel/spi/PackageScanClassResolver.java
index 26e2a79..f2d40f2 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/PackageScanClassResolver.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/PackageScanClassResolver.java
@@ -66,7 +66,7 @@
     Set<Class<?>> findAnnotated(Set<Class<? extends Annotation>> annotations, String... packageNames);
 
     /**
-     * Attempts to discover classes that are assignable to the type provided. In
+     * Attempts to discover classes that are assignable to the type provided (exclude abstract classes). In
      * the case that an interface is provided this method will collect
      * implementations. In the case of a non-interface class, subclasses will be
      * collected.
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/scan/AssignableToPackageScanFilter.java b/core/camel-base/src/main/java/org/apache/camel/impl/scan/AssignableToPackageScanFilter.java
index 7c0379b..75d9b5b 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/scan/AssignableToPackageScanFilter.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/scan/AssignableToPackageScanFilter.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.impl.scan;
 
+import java.lang.reflect.Modifier;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -26,6 +27,7 @@
  */
 public class AssignableToPackageScanFilter implements PackageScanFilter {
     private final Set<Class<?>> parents = new HashSet<>();
+    private boolean includeAbstract;
 
     public AssignableToPackageScanFilter() {
     }
@@ -42,12 +44,28 @@
         parents.add(parentType);
     }
 
+    public boolean isIncludeAbstract() {
+        return includeAbstract;
+    }
+
+    /**
+     * Whether to include abstract classes.
+     */
+    public void setIncludeAbstract(boolean includeAbstract) {
+        this.includeAbstract = includeAbstract;
+    }
+
     @Override
     public boolean matches(Class<?> type) {
         if (parents.size() > 0) {
             for (Class<?> parent : parents) {
                 if (parent.isAssignableFrom(type)) {
-                    return true;
+                    if (includeAbstract) {
+                        return true;
+                    } else {
+                        // skip abstract classes
+                        return !Modifier.isAbstract(type.getModifiers());
+                    }
                 }
             }
         }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index 994be6d..0d7c0c3 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -462,9 +462,7 @@
             String[] pkgs = mainConfigurationProperties.getPackageScanRouteBuilders().split(",");
             Set<Class<?>> set = camelContext.adapt(ExtendedCamelContext.class)
                     .getPackageScanClassResolver()
-                    .findImplementations(RoutesBuilder.class, pkgs)
-                    .stream().filter(c -> !Modifier.isAbstract(c.getModifiers()))
-                    .collect(Collectors.toSet());
+                    .findImplementations(RoutesBuilder.class, pkgs);
             for (Class<?> routeClazz : set) {
                 Object builder = camelContext.getInjector().newInstance(routeClazz);
                 if (builder instanceof RoutesBuilder) {
diff --git a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_5.adoc b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_5.adoc
index 244b43a..0c39132 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_5.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_5.adoc
@@ -14,6 +14,10 @@
 After the template has been started / used the first time, then its general configuration cannot be altered later,
 instead create a new template.
 
+=== PackageScanClassResolver
+
+The `PackageScanClassResolver` will now skip abstract classes in its `findImplementations` method.
+
 === camel-bean
 
 The `bean(class)` EIP will now lookup in the registry first whether there is a single bean instance of the given class type