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

quantranhong1999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 791bb3a664ec4da6353717c014a6a155ce3e046a
Author: Quan Tran <[email protected]>
AuthorDate: Fri Jul 10 15:49:15 2026 +0700

    JAMES-4210 [FIX] Reuse protocol default SASL factories for explicit 
configuration
    
    Resolve explicitly configured SASL factory names against protocol-provided 
defaults before falling back to Guice loading.
    
    This enables constructor-configured factories such as SMTP LOGIN and 
preserves protocol-specific PLAIN policies for explicit configuration. Support 
both simple and fully qualified factory names.
---
 .../james/utils/GuiceSaslMechanismResolver.java    | 18 ++++++-
 .../utils/GuiceSaslMechanismResolverTest.java      | 63 ++++++++++++++++++++++
 2 files changed, 80 insertions(+), 1 deletion(-)

diff --git 
a/server/container/guice/common/src/main/java/org/apache/james/utils/GuiceSaslMechanismResolver.java
 
b/server/container/guice/common/src/main/java/org/apache/james/utils/GuiceSaslMechanismResolver.java
index c34d2e5804..0bc526a2c9 100644
--- 
a/server/container/guice/common/src/main/java/org/apache/james/utils/GuiceSaslMechanismResolver.java
+++ 
b/server/container/guice/common/src/main/java/org/apache/james/utils/GuiceSaslMechanismResolver.java
@@ -54,7 +54,7 @@ public class GuiceSaslMechanismResolver {
             ImmutableList<SaslMechanismFactory> factories = 
configuredFactoryClassNames.isEmpty()
                 ? enabledDefaultFactories
                 : configuredFactoryClassNames.stream()
-                    .map(Throwing.function(this::instantiateFactory))
+                    .map(Throwing.function(className -> 
resolveConfiguredFactory(className, enabledDefaultFactories)))
                     .collect(ImmutableList.toImmutableList());
 
             return factories.stream()
@@ -75,6 +75,22 @@ public class GuiceSaslMechanismResolver {
         }
     }
 
+    private SaslMechanismFactory resolveConfiguredFactory(String className, 
ImmutableList<SaslMechanismFactory> enabledDefaultFactories) throws 
ConfigurationException {
+        return enabledDefaultFactories.stream()
+            .filter(factory -> matchesCanonicalName(className, factory) || 
matchesSimpleName(className, factory))
+            .findFirst()
+            .orElseGet(Throwing.supplier(() -> instantiateFactory(className)));
+    }
+
+    private boolean matchesCanonicalName(String className, 
SaslMechanismFactory factory) {
+        return className.equals(factory.getClass().getCanonicalName());
+    }
+
+    private boolean matchesSimpleName(String className, SaslMechanismFactory 
factory) {
+        return !className.contains(".")
+            && className.equals(factory.getClass().getSimpleName());
+    }
+
     private SaslMechanismFactory instantiateFactory(String className) throws 
ConfigurationException {
         try {
             return factoryLoader.instantiate(new ClassName(className));
diff --git 
a/server/container/guice/common/src/test/java/org/apache/james/utils/GuiceSaslMechanismResolverTest.java
 
b/server/container/guice/common/src/test/java/org/apache/james/utils/GuiceSaslMechanismResolverTest.java
index e3551ee400..83b1fb3813 100644
--- 
a/server/container/guice/common/src/test/java/org/apache/james/utils/GuiceSaslMechanismResolverTest.java
+++ 
b/server/container/guice/common/src/test/java/org/apache/james/utils/GuiceSaslMechanismResolverTest.java
@@ -103,6 +103,38 @@ class GuiceSaslMechanismResolverTest {
             .containsExactly("EXTERNAL-FAKE");
     }
 
+    @Test
+    void 
resolveShouldReuseDefaultFactoryMatchingConfiguredSimpleNameWithoutGuiceLoadingIt()
 throws Exception {
+        // GIVEN a configured factory simple name matching a protocol-provided 
default factory
+        GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new 
FailingGuiceLoader());
+
+        // WHEN resolving it
+        ImmutableList<SaslMechanism> mechanisms = 
testee.resolve(ImmutableList.of(ConstructorOnlySaslMechanismFactory.class.getSimpleName()),
+            ImmutableList.of(new ConstructorOnlySaslMechanismFactory("LOGIN")),
+            EMPTY_CONFIGURATION);
+
+        // THEN the default factory instance is reused instead of 
Guice-loading a fresh standalone factory
+        assertThat(mechanisms)
+            .extracting(SaslMechanism::name)
+            .containsExactly("LOGIN");
+    }
+
+    @Test
+    void 
resolveShouldReuseDefaultFactoryMatchingConfiguredFullyQualifiedNameWithoutGuiceLoadingIt()
 throws Exception {
+        // GIVEN a configured factory FQCN matching a protocol-provided 
default factory
+        GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new 
FailingGuiceLoader());
+
+        // WHEN resolving it
+        ImmutableList<SaslMechanism> mechanisms = 
testee.resolve(ImmutableList.of(ConstructorOnlySaslMechanismFactory.class.getCanonicalName()),
+            ImmutableList.of(new ConstructorOnlySaslMechanismFactory("LOGIN")),
+            EMPTY_CONFIGURATION);
+
+        // THEN the default factory instance is reused before falling back to 
Guice loading
+        assertThat(mechanisms)
+            .extracting(SaslMechanism::name)
+            .containsExactly("LOGIN");
+    }
+
     @Test
     void 
resolveShouldCreateConfiguredFactoriesFromCurrentServerConfiguration() throws 
Exception {
         // GIVEN two server configurations using the same configured SASL 
factory
@@ -177,6 +209,19 @@ class GuiceSaslMechanismResolverTest {
         return serverConfiguration -> new 
FixedNameSaslMechanism(mechanismName);
     }
 
+    private static class ConstructorOnlySaslMechanismFactory implements 
SaslMechanismFactory {
+        private final String mechanismName;
+
+        private ConstructorOnlySaslMechanismFactory(String mechanismName) {
+            this.mechanismName = mechanismName;
+        }
+
+        @Override
+        public SaslMechanism create(HierarchicalConfiguration<ImmutableNode> 
serverConfiguration) {
+            return new FixedNameSaslMechanism(mechanismName);
+        }
+    }
+
     private static class ReflectionGuiceLoader implements GuiceLoader {
         @Override
         public <T> T instantiate(ClassName className) throws 
ClassNotFoundException {
@@ -194,6 +239,24 @@ class GuiceSaslMechanismResolverTest {
         }
     }
 
+    private static class FailingGuiceLoader extends ReflectionGuiceLoader {
+        @Override
+        public <T> InvocationPerformer<T> withNamingSheme(NamingScheme 
namingSheme) {
+            return new FailingInvocationPerformer<>();
+        }
+    }
+
+    private static class FailingInvocationPerformer<T> extends 
ReflectionInvocationPerformer<T> {
+        private FailingInvocationPerformer() {
+            super(NamingScheme.IDENTITY);
+        }
+
+        @Override
+        public T instantiate(ClassName className) {
+            throw new AssertionError("Default factory match should not 
instantiate " + className.getName());
+        }
+    }
+
     private static class ReflectionInvocationPerformer<T> implements 
GuiceLoader.InvocationPerformer<T> {
         private final NamingScheme namingScheme;
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to