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 d39a9fc1e2242d1cb32cf3ca011dd544910e0793 Author: Quan Tran <[email protected]> AuthorDate: Tue Jul 7 09:21:36 2026 +0700 JAMES-4210 Resolve configured SASL factories with Guice Add shared Guice resolution for auth.saslMechanisms, supporting built-in simple names, external FQCNs, server-specific factory creation, duplicate mechanism deduplication, and configured order preservation. --- server/container/guice/common/pom.xml | 8 + .../james/modules/SaslMechanismFactories.java | 31 +++ .../james/utils/GuiceSaslMechanismResolver.java | 89 ++++++++ .../TestingDefaultPackageSaslMechanismFactory.java | 33 +++ .../ConfigurableFakeSaslMechanismFactory.java | 32 +++ .../utils/ExternalFakeSaslMechanismFactory.java | 32 +++ .../apache/james/utils/FixedNameSaslMechanism.java | 69 ++++++ .../utils/GuiceSaslMechanismResolverTest.java | 242 +++++++++++++++++++++ 8 files changed, 536 insertions(+) diff --git a/server/container/guice/common/pom.xml b/server/container/guice/common/pom.xml index c6a56f7028..f8001ee29e 100644 --- a/server/container/guice/common/pom.xml +++ b/server/container/guice/common/pom.xml @@ -74,6 +74,10 @@ <groupId>${james.groupId}</groupId> <artifactId>james-server-guice-netty</artifactId> </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>james-server-guice-utils</artifactId> + </dependency> <dependency> <groupId>${james.groupId}</groupId> <artifactId>james-server-mailrepository-memory</artifactId> @@ -126,6 +130,10 @@ <artifactId>testing-base</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>${james.protocols.groupId}</groupId> + <artifactId>protocols-api</artifactId> + </dependency> <dependency> <groupId>com.github.stefanbirkner</groupId> <artifactId>system-lambda</artifactId> diff --git a/server/container/guice/common/src/main/java/org/apache/james/modules/SaslMechanismFactories.java b/server/container/guice/common/src/main/java/org/apache/james/modules/SaslMechanismFactories.java new file mode 100644 index 0000000000..ad86c882d7 --- /dev/null +++ b/server/container/guice/common/src/main/java/org/apache/james/modules/SaslMechanismFactories.java @@ -0,0 +1,31 @@ +/**************************************************************** + * 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.james.modules; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; + +import com.google.inject.BindingAnnotation; + +@BindingAnnotation +@Retention(RUNTIME) +public @interface SaslMechanismFactories { +} 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 new file mode 100644 index 0000000000..c34d2e5804 --- /dev/null +++ b/server/container/guice/common/src/main/java/org/apache/james/utils/GuiceSaslMechanismResolver.java @@ -0,0 +1,89 @@ +/**************************************************************** + * 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.james.utils; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.function.Function; +import java.util.stream.Collectors; + +import jakarta.inject.Inject; + +import org.apache.commons.configuration2.HierarchicalConfiguration; +import org.apache.commons.configuration2.ex.ConfigurationException; +import org.apache.commons.configuration2.tree.ImmutableNode; +import org.apache.james.protocols.api.sasl.SaslMechanism; +import org.apache.james.protocols.api.sasl.SaslMechanismFactory; + +import com.github.fge.lambdas.Throwing; +import com.google.common.collect.ImmutableList; + +public class GuiceSaslMechanismResolver { + private static final NamingScheme SASL_FACTORY_NAMING_SCHEME = + new NamingScheme.OptionalPackagePrefix(PackageName.of("org.apache.james.protocols.sasl")); + + private final GuiceLoader.InvocationPerformer<SaslMechanismFactory> factoryLoader; + + @Inject + public GuiceSaslMechanismResolver(GuiceLoader guiceLoader) { + this.factoryLoader = guiceLoader.withNamingSheme(SASL_FACTORY_NAMING_SCHEME); + } + + public ImmutableList<SaslMechanism> resolve(Collection<String> configuredFactoryClassNames, + ImmutableList<SaslMechanismFactory> enabledDefaultFactories, + HierarchicalConfiguration<ImmutableNode> serverConfiguration) throws ConfigurationException { + try { + ImmutableList<SaslMechanismFactory> factories = configuredFactoryClassNames.isEmpty() + ? enabledDefaultFactories + : configuredFactoryClassNames.stream() + .map(Throwing.function(this::instantiateFactory)) + .collect(ImmutableList.toImmutableList()); + + return factories.stream() + .map(Throwing.function(factory -> factory.create(serverConfiguration))) + .collect(Collectors.toMap( + mechanism -> normalize(mechanism.name()), + Function.identity(), + (first, second) -> first, + LinkedHashMap::new)) + .values() + .stream() + .collect(ImmutableList.toImmutableList()); + } catch (RuntimeException e) { + if (e.getCause() instanceof ConfigurationException configurationException) { + throw configurationException; + } + throw e; + } + } + + private SaslMechanismFactory instantiateFactory(String className) throws ConfigurationException { + try { + return factoryLoader.instantiate(new ClassName(className)); + } catch (ClassNotFoundException | RuntimeException e) { + throw new ConfigurationException("Can not load SASL mechanism factory " + className, e); + } + } + + private String normalize(String mechanismName) { + return mechanismName.toUpperCase(Locale.US); + } +} diff --git a/server/container/guice/common/src/test/java/org/apache/james/protocols/sasl/TestingDefaultPackageSaslMechanismFactory.java b/server/container/guice/common/src/test/java/org/apache/james/protocols/sasl/TestingDefaultPackageSaslMechanismFactory.java new file mode 100644 index 0000000000..850ed4c1ce --- /dev/null +++ b/server/container/guice/common/src/test/java/org/apache/james/protocols/sasl/TestingDefaultPackageSaslMechanismFactory.java @@ -0,0 +1,33 @@ +/**************************************************************** + * 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.james.protocols.sasl; + +import org.apache.commons.configuration2.HierarchicalConfiguration; +import org.apache.commons.configuration2.tree.ImmutableNode; +import org.apache.james.protocols.api.sasl.SaslMechanism; +import org.apache.james.protocols.api.sasl.SaslMechanismFactory; +import org.apache.james.utils.FixedNameSaslMechanism; + +public class TestingDefaultPackageSaslMechanismFactory implements SaslMechanismFactory { + @Override + public SaslMechanism create(HierarchicalConfiguration<ImmutableNode> serverConfiguration) { + return new FixedNameSaslMechanism("DEFAULT"); + } +} diff --git a/server/container/guice/common/src/test/java/org/apache/james/utils/ConfigurableFakeSaslMechanismFactory.java b/server/container/guice/common/src/test/java/org/apache/james/utils/ConfigurableFakeSaslMechanismFactory.java new file mode 100644 index 0000000000..c1f59a2eef --- /dev/null +++ b/server/container/guice/common/src/test/java/org/apache/james/utils/ConfigurableFakeSaslMechanismFactory.java @@ -0,0 +1,32 @@ +/**************************************************************** + * 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.james.utils; + +import org.apache.commons.configuration2.HierarchicalConfiguration; +import org.apache.commons.configuration2.tree.ImmutableNode; +import org.apache.james.protocols.api.sasl.SaslMechanism; +import org.apache.james.protocols.api.sasl.SaslMechanismFactory; + +public class ConfigurableFakeSaslMechanismFactory implements SaslMechanismFactory { + @Override + public SaslMechanism create(HierarchicalConfiguration<ImmutableNode> serverConfiguration) { + return new FixedNameSaslMechanism(serverConfiguration.getString("auth.example.realm")); + } +} diff --git a/server/container/guice/common/src/test/java/org/apache/james/utils/ExternalFakeSaslMechanismFactory.java b/server/container/guice/common/src/test/java/org/apache/james/utils/ExternalFakeSaslMechanismFactory.java new file mode 100644 index 0000000000..d8607ac7a2 --- /dev/null +++ b/server/container/guice/common/src/test/java/org/apache/james/utils/ExternalFakeSaslMechanismFactory.java @@ -0,0 +1,32 @@ +/**************************************************************** + * 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.james.utils; + +import org.apache.commons.configuration2.HierarchicalConfiguration; +import org.apache.commons.configuration2.tree.ImmutableNode; +import org.apache.james.protocols.api.sasl.SaslMechanism; +import org.apache.james.protocols.api.sasl.SaslMechanismFactory; + +public class ExternalFakeSaslMechanismFactory implements SaslMechanismFactory { + @Override + public SaslMechanism create(HierarchicalConfiguration<ImmutableNode> serverConfiguration) { + return new FixedNameSaslMechanism("EXTERNAL-FAKE"); + } +} diff --git a/server/container/guice/common/src/test/java/org/apache/james/utils/FixedNameSaslMechanism.java b/server/container/guice/common/src/test/java/org/apache/james/utils/FixedNameSaslMechanism.java new file mode 100644 index 0000000000..9b82df2cb2 --- /dev/null +++ b/server/container/guice/common/src/test/java/org/apache/james/utils/FixedNameSaslMechanism.java @@ -0,0 +1,69 @@ +/**************************************************************** + * 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.james.utils; + +import org.apache.james.protocols.api.sasl.SaslAuthenticator; +import org.apache.james.protocols.api.sasl.SaslExchange; +import org.apache.james.protocols.api.sasl.SaslFailure; +import org.apache.james.protocols.api.sasl.SaslInitialRequest; +import org.apache.james.protocols.api.sasl.SaslMechanism; +import org.apache.james.protocols.api.sasl.SaslStep; + +public class FixedNameSaslMechanism implements SaslMechanism { + private final String name; + + public FixedNameSaslMechanism(String name) { + this.name = name; + } + + @Override + public String name() { + return name; + } + + @Override + public SaslExchange start(SaslInitialRequest request, SaslAuthenticator authenticator) { + return new FixedStepExchange(); + } + + private record FixedStepExchange() implements SaslExchange { + @Override + public SaslStep firstStep() { + return failure(); + } + + @Override + public SaslStep onResponse(byte[] clientResponse) { + return failure(); + } + + @Override + public void abort() { + } + + @Override + public void close() { + } + + private SaslStep failure() { + return new SaslStep.Failure(SaslFailure.malformed("not implemented")); + } + } +} 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 new file mode 100644 index 0000000000..e3551ee400 --- /dev/null +++ b/server/container/guice/common/src/test/java/org/apache/james/utils/GuiceSaslMechanismResolverTest.java @@ -0,0 +1,242 @@ +/**************************************************************** + * 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.james.utils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.lang.reflect.InvocationTargetException; +import java.util.Optional; + +import org.apache.commons.configuration2.BaseHierarchicalConfiguration; +import org.apache.commons.configuration2.HierarchicalConfiguration; +import org.apache.commons.configuration2.ex.ConfigurationException; +import org.apache.commons.configuration2.tree.ImmutableNode; +import org.apache.james.protocols.api.sasl.SaslMechanism; +import org.apache.james.protocols.api.sasl.SaslMechanismFactory; +import org.junit.jupiter.api.Test; + +import com.google.common.collect.ImmutableList; +import com.google.inject.Module; + +class GuiceSaslMechanismResolverTest { + private static final HierarchicalConfiguration<ImmutableNode> EMPTY_CONFIGURATION = new BaseHierarchicalConfiguration(); + + @Test + void resolveShouldUseEnabledDefaultFactoriesWhenNoFactoryClassIsConfigured() throws Exception { + // GIVEN an absent auth.saslMechanisms configuration and an ordered default factory list + GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new ReflectionGuiceLoader()); + + // WHEN resolving mechanisms for this server + ImmutableList<SaslMechanism> mechanisms = testee.resolve(ImmutableList.of(), + ImmutableList.of(factory("PLAIN"), factory("OAUTHBEARER")), + EMPTY_CONFIGURATION); + + // THEN defaults are used in their declared order + assertThat(mechanisms) + .extracting(SaslMechanism::name) + .containsExactly("PLAIN", "OAUTHBEARER"); + } + + @Test + void resolveShouldResolveSimpleFactoryNameFromDefaultSaslPackage() throws Exception { + // GIVEN a configured built-in SASL factory simple name + GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new ReflectionGuiceLoader()); + + // WHEN resolving it + ImmutableList<SaslMechanism> mechanisms = testee.resolve(ImmutableList.of("TestingDefaultPackageSaslMechanismFactory"), + ImmutableList.of(), + EMPTY_CONFIGURATION); + + // THEN the factory is loaded from org.apache.james.protocols.sasl + assertThat(mechanisms) + .extracting(SaslMechanism::name) + .containsExactly("DEFAULT"); + } + + @Test + void resolveShouldResolveFullyQualifiedFactoryName() throws Exception { + // GIVEN a configured extension factory FQCN + GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new ReflectionGuiceLoader()); + + // WHEN resolving it + ImmutableList<SaslMechanism> mechanisms = testee.resolve(ImmutableList.of(ExternalFakeSaslMechanismFactory.class.getCanonicalName()), + ImmutableList.of(), + EMPTY_CONFIGURATION); + + // THEN the extension factory is loaded directly + assertThat(mechanisms) + .extracting(SaslMechanism::name) + .containsExactly("EXTERNAL-FAKE"); + } + + @Test + void resolveShouldUseConfiguredFactoriesInsteadOfDefaults() throws Exception { + // GIVEN both defaults and an explicit auth.saslMechanisms configuration + GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new ReflectionGuiceLoader()); + + // WHEN resolving mechanisms + ImmutableList<SaslMechanism> mechanisms = testee.resolve(ImmutableList.of(ExternalFakeSaslMechanismFactory.class.getCanonicalName()), + ImmutableList.of(factory("DEFAULT")), + EMPTY_CONFIGURATION); + + // THEN the configured list replaces defaults + assertThat(mechanisms) + .extracting(SaslMechanism::name) + .containsExactly("EXTERNAL-FAKE"); + } + + @Test + void resolveShouldCreateConfiguredFactoriesFromCurrentServerConfiguration() throws Exception { + // GIVEN two server configurations using the same configured SASL factory + BaseHierarchicalConfiguration firstConfiguration = new BaseHierarchicalConfiguration(); + firstConfiguration.addProperty("auth.example.realm", "FIRST"); + BaseHierarchicalConfiguration secondConfiguration = new BaseHierarchicalConfiguration(); + secondConfiguration.addProperty("auth.example.realm", "SECOND"); + GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new ReflectionGuiceLoader()); + + // WHEN resolving the same configured factory for each server + SaslMechanism firstMechanism = testee.resolve(ImmutableList.of(ConfigurableFakeSaslMechanismFactory.class.getCanonicalName()), + ImmutableList.of(), + firstConfiguration) + .getFirst(); + SaslMechanism secondMechanism = testee.resolve(ImmutableList.of(ConfigurableFakeSaslMechanismFactory.class.getCanonicalName()), + ImmutableList.of(), + secondConfiguration) + .getFirst(); + + // THEN each mechanism is created from that server's configuration, not from a global singleton + assertThat(firstMechanism.name()).isEqualTo("FIRST"); + assertThat(secondMechanism.name()).isEqualTo("SECOND"); + } + + @Test + void resolveShouldPreserveConfiguredOrderForDistinctMechanisms() throws Exception { + // GIVEN several distinct SASL mechanism factories in a configured order + GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new ReflectionGuiceLoader()); + + // WHEN resolving them + ImmutableList<SaslMechanism> mechanisms = testee.resolve(ImmutableList.of(), + ImmutableList.of(factory("FIRST"), factory("SECOND"), factory("THIRD")), + EMPTY_CONFIGURATION); + + // THEN the resolved mechanisms keep the configured order + assertThat(mechanisms) + .extracting(SaslMechanism::name) + .containsExactly("FIRST", "SECOND", "THIRD"); + } + + @Test + void resolveShouldDeduplicateMechanismNamesCaseInsensitively() throws Exception { + // GIVEN two factories returning the same SASL mechanism name with different case + GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new ReflectionGuiceLoader()); + + // WHEN resolving both factories + ImmutableList<SaslMechanism> mechanisms = testee.resolve(ImmutableList.of(), + ImmutableList.of(factory("DUPLICATE"), factory("duplicate")), + EMPTY_CONFIGURATION); + + // THEN first occurrence wins and order remains stable + assertThat(mechanisms) + .extracting(SaslMechanism::name) + .containsExactly("DUPLICATE"); + } + + @Test + void resolveShouldFailWhenConfiguredFactoryClassDoesNotExist() { + // GIVEN a resolver used for configured SASL mechanism factory entries + GuiceSaslMechanismResolver testee = new GuiceSaslMechanismResolver(new ReflectionGuiceLoader()); + + // WHEN resolving an unknown factory class name + // THEN startup wiring can fail fast with the configured entry in the error + assertThatThrownBy(() -> testee.resolve(ImmutableList.of("MissingSaslMechanismFactory"), + ImmutableList.of(), + EMPTY_CONFIGURATION)) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("MissingSaslMechanismFactory"); + } + + private static SaslMechanismFactory factory(String mechanismName) { + return serverConfiguration -> new FixedNameSaslMechanism(mechanismName); + } + + private static class ReflectionGuiceLoader implements GuiceLoader { + @Override + public <T> T instantiate(ClassName className) throws ClassNotFoundException { + return this.<T>withNamingSheme(NamingScheme.IDENTITY).instantiate(className); + } + + @Override + public <T> InvocationPerformer<T> withNamingSheme(NamingScheme namingSheme) { + return new ReflectionInvocationPerformer<>(namingSheme); + } + + @Override + public <T> InvocationPerformer<T> withChildModule(Module childModule) { + return new ReflectionInvocationPerformer<>(NamingScheme.IDENTITY); + } + } + + private static class ReflectionInvocationPerformer<T> implements GuiceLoader.InvocationPerformer<T> { + private final NamingScheme namingScheme; + + private ReflectionInvocationPerformer(NamingScheme namingScheme) { + this.namingScheme = namingScheme; + } + + @Override + public T instantiate(ClassName className) throws ClassNotFoundException { + try { + return locateClass(className).getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new ClassNotFoundException(className.getName(), e); + } + } + + @Override + public Class<T> locateClass(ClassName className) throws ClassNotFoundException { + Optional<Class<T>> locatedClass = namingScheme.toFullyQualifiedClassNames(className) + .map(FullyQualifiedClassName::getName) + .map(this::tryLocateClass) + .flatMap(Optional::stream) + .findFirst(); + return locatedClass.orElseThrow(() -> new ClassNotFoundException(className.getName())); + } + + @Override + public GuiceLoader.InvocationPerformer<T> withChildModule(Module childModule) { + return new ReflectionInvocationPerformer<>(namingScheme); + } + + @Override + public GuiceLoader.InvocationPerformer<T> withNamingSheme(NamingScheme namingSheme) { + return new ReflectionInvocationPerformer<>(namingSheme); + } + + @SuppressWarnings("unchecked") + private Optional<Class<T>> tryLocateClass(String className) { + try { + return Optional.of((Class<T>) Class.forName(className)); + } catch (ClassNotFoundException e) { + return Optional.empty(); + } + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
