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

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

commit 897debed28864e03088a7ec4f6bd78b0c2cbb08c
Author: Benoit Tellier <[email protected]>
AuthorDate: Thu Sep 19 14:45:47 2019 +0700

    JAMES-2886 Generify Guice loader
---
 server/container/guice/guice-utils/pom.xml         |  20 ++++
 .../java/org/apache/james/utils/ClassName.java     |  68 ++++++++++++++
 .../apache/james/utils/ExtendedClassLoader.java    |   4 +-
 .../james/utils/FullyQualifiedClassName.java}      |  41 +++++----
 .../java/org/apache/james/utils/NamingScheme.java} |  38 +++-----
 .../java/org/apache/james/utils/PackageName.java   |  80 ++++++++++++++++
 .../java/org/apache/james/utils/ClassNameTest.java | 102 +++++++++++++++++++++
 .../james/utils/FullyQualifiedClassNameTest.java}  |  54 ++++++-----
 .../org/apache/james/utils/NamingSchemeTest.java   |  78 ++++++++++++++++
 .../org/apache/james/utils/PackageNameTest.java    |  72 +++++++++++++++
 .../org/apache/james/utils/GuiceGenericLoader.java |  30 +++---
 .../org/apache/james/utils/GuiceMailetLoader.java  |  10 +-
 .../org/apache/james/utils/GuiceMatcherLoader.java |  10 +-
 13 files changed, 511 insertions(+), 96 deletions(-)

diff --git a/server/container/guice/guice-utils/pom.xml 
b/server/container/guice/guice-utils/pom.xml
index 7948e4e..4b8b922 100644
--- a/server/container/guice/guice-utils/pom.xml
+++ b/server/container/guice/guice-utils/pom.xml
@@ -47,6 +47,26 @@
             <artifactId>throwing-lambdas</artifactId>
         </dependency>
         <dependency>
+            <groupId>nl.jqno.equalsverifier</groupId>
+            <artifactId>equalsverifier</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
diff --git 
a/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/ClassName.java
 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/ClassName.java
new file mode 100644
index 0000000..83217e8
--- /dev/null
+++ 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/ClassName.java
@@ -0,0 +1,68 @@
+/****************************************************************
+ * 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.Objects;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+
+public class ClassName {
+    private final String name;
+
+    public ClassName(String name) {
+        Preconditions.checkNotNull(name);
+        Preconditions.checkArgument(!name.isEmpty(), "A class name can not be 
empty");
+        
Preconditions.checkArgument(!name.startsWith(String.valueOf(PackageName.PART_SEPARATOR)),
 "A class name can not start with '.'");
+        
Preconditions.checkArgument(!name.endsWith(String.valueOf(PackageName.PART_SEPARATOR)),
 "A class name can not end with '.'");
+        Splitter.on(PackageName.PART_SEPARATOR)
+            .split(name)
+            .forEach(part -> Preconditions.checkArgument(!part.isEmpty(), 
"Package part can not be empty within a class name"));
+
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    FullyQualifiedClassName appendPackage(PackageName aPackage) {
+        return new FullyQualifiedClassName(aPackage.getName() + "." + name);
+    }
+
+    FullyQualifiedClassName asFullyQualified() {
+        return new FullyQualifiedClassName(name);
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof ClassName) {
+            ClassName className = (ClassName) o;
+
+            return Objects.equals(this.name, className.name);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(name);
+    }
+}
diff --git 
a/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/ExtendedClassLoader.java
 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/ExtendedClassLoader.java
index 5b98666..5602a68 100644
--- 
a/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/ExtendedClassLoader.java
+++ 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/ExtendedClassLoader.java
@@ -73,7 +73,7 @@ public class ExtendedClassLoader {
     }
 
     @SuppressWarnings("unchecked")
-    public <T> Class<T> locateClass(String className) throws 
ClassNotFoundException {
-        return (Class<T>) urlClassLoader.loadClass(className);
+    public <T> Class<T> locateClass(FullyQualifiedClassName className) throws 
ClassNotFoundException {
+        return (Class<T>) urlClassLoader.loadClass(className.getName());
     }
 }
diff --git 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/FullyQualifiedClassName.java
similarity index 55%
copy from 
server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
copy to 
server/container/guice/guice-utils/src/main/java/org/apache/james/utils/FullyQualifiedClassName.java
index db8959ad..64c421d 100644
--- 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
+++ 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/FullyQualifiedClassName.java
@@ -19,35 +19,36 @@
 
 package org.apache.james.utils;
 
-import javax.mail.MessagingException;
+import java.util.Objects;
 
-import org.apache.james.mailetcontainer.api.MatcherLoader;
-import org.apache.mailet.Matcher;
-import org.apache.mailet.MatcherConfig;
+import com.google.common.base.Preconditions;
 
-import com.google.inject.Inject;
-import com.google.inject.Injector;
+public class FullyQualifiedClassName {
+    private final String name;
 
-public class GuiceMatcherLoader implements MatcherLoader {
+    FullyQualifiedClassName(String name) {
+        Preconditions.checkNotNull(name);
+        Preconditions.checkArgument(!name.isEmpty(), "A class name can not be 
empty");
 
-    private static final String STANDARD_PACKAGE = 
"org.apache.james.transport.matchers.";
-
-    private final GuiceGenericLoader<Matcher> genericLoader;
+        this.name = name;
+    }
 
-    @Inject
-    public GuiceMatcherLoader(Injector injector, ExtendedClassLoader 
extendedClassLoader) {
-        this.genericLoader = new GuiceGenericLoader<>(injector, 
extendedClassLoader, STANDARD_PACKAGE);
+    public String getName() {
+        return name;
     }
 
     @Override
-    public Matcher getMatcher(MatcherConfig config) throws MessagingException {
-        try {
-            Matcher result = 
genericLoader.instanciate(config.getMatcherName());
-            result.init(config);
-            return result;
-        } catch (Exception e) {
-            throw new MessagingException("Can not load matcher " + 
config.getMatcherName(), e);
+    public final boolean equals(Object o) {
+        if (o instanceof FullyQualifiedClassName) {
+            FullyQualifiedClassName className = (FullyQualifiedClassName) o;
+
+            return Objects.equals(this.name, className.name);
         }
+        return false;
     }
 
+    @Override
+    public final int hashCode() {
+        return Objects.hash(name);
+    }
 }
diff --git 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/NamingScheme.java
similarity index 54%
copy from 
server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
copy to 
server/container/guice/guice-utils/src/main/java/org/apache/james/utils/NamingScheme.java
index db8959ad..4042a2e 100644
--- 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
+++ 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/NamingScheme.java
@@ -19,35 +19,25 @@
 
 package org.apache.james.utils;
 
-import javax.mail.MessagingException;
+import java.util.stream.Stream;
 
-import org.apache.james.mailetcontainer.api.MatcherLoader;
-import org.apache.mailet.Matcher;
-import org.apache.mailet.MatcherConfig;
+public interface NamingScheme {
+    Stream<FullyQualifiedClassName> toFullyQualifiedClassNames(ClassName 
className);
 
-import com.google.inject.Inject;
-import com.google.inject.Injector;
+    NamingScheme IDENTITY =  className -> 
Stream.of(className.asFullyQualified());
 
-public class GuiceMatcherLoader implements MatcherLoader {
+    class OptionalPackagePrefix implements NamingScheme {
+        private final PackageName packageName;
 
-    private static final String STANDARD_PACKAGE = 
"org.apache.james.transport.matchers.";
-
-    private final GuiceGenericLoader<Matcher> genericLoader;
-
-    @Inject
-    public GuiceMatcherLoader(Injector injector, ExtendedClassLoader 
extendedClassLoader) {
-        this.genericLoader = new GuiceGenericLoader<>(injector, 
extendedClassLoader, STANDARD_PACKAGE);
-    }
+        public OptionalPackagePrefix(PackageName packageName) {
+            this.packageName = packageName;
+        }
 
-    @Override
-    public Matcher getMatcher(MatcherConfig config) throws MessagingException {
-        try {
-            Matcher result = 
genericLoader.instanciate(config.getMatcherName());
-            result.init(config);
-            return result;
-        } catch (Exception e) {
-            throw new MessagingException("Can not load matcher " + 
config.getMatcherName(), e);
+        @Override
+        public Stream<FullyQualifiedClassName> 
toFullyQualifiedClassNames(ClassName className) {
+            return Stream.of(
+                className.asFullyQualified(),
+                className.appendPackage(packageName));
         }
     }
-
 }
diff --git 
a/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/PackageName.java
 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/PackageName.java
new file mode 100644
index 0000000..188fcd8
--- /dev/null
+++ 
b/server/container/guice/guice-utils/src/main/java/org/apache/james/utils/PackageName.java
@@ -0,0 +1,80 @@
+/****************************************************************
+ * 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.Objects;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+
+public class PackageName {
+    static final char PART_SEPARATOR = '.';
+
+    public static PackageName of(String value) {
+        Preconditions.checkNotNull(value);
+        String sanitizedValue = sanitize(value);
+        Preconditions.checkArgument(!hasEmptyParts(sanitizedValue),
+            "PackageName can not contain empty parts: " + sanitizedValue);
+
+        return new PackageName(sanitizedValue);
+    }
+
+    private static boolean hasEmptyParts(String sanitizedValue) {
+        return Splitter.on(PART_SEPARATOR)
+            .splitToList(sanitizedValue)
+            .stream()
+            .anyMatch(String::isEmpty);
+    }
+
+    private static String sanitize(String value) {
+        if (value.endsWith(String.valueOf(PART_SEPARATOR))) {
+            return value.substring(0, value.length() - 1);
+        }
+        return value;
+    }
+
+    private final String name;
+
+    private PackageName(String name) {
+        Preconditions.checkNotNull(name);
+        Preconditions.checkArgument(!name.isEmpty(), "Name should not be 
empty");
+
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof PackageName) {
+            PackageName className = (PackageName) o;
+
+            return Objects.equals(this.name, className.name);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(name);
+    }
+}
diff --git 
a/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/ClassNameTest.java
 
b/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/ClassNameTest.java
new file mode 100644
index 0000000..111a8e9
--- /dev/null
+++ 
b/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/ClassNameTest.java
@@ -0,0 +1,102 @@
+/****************************************************************
+ * 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 org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class ClassNameTest {
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(ClassName.class)
+            .verify();
+    }
+
+    @Test
+    void constructorShouldThrowWhenNull() {
+        assertThatThrownBy(() -> new ClassName(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void constructorShouldThrowWhenEmpty() {
+        assertThatThrownBy(() -> new ClassName(""))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void constructorShouldThrowWhenStartWithDot() {
+        assertThatThrownBy(() -> new ClassName(".MyClass"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void constructorShouldThrowWhenEndWithDot() {
+        assertThatThrownBy(() -> new ClassName("MyClass."))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void constructorShouldThrowWhenEmptyPackagePart() {
+        assertThatThrownBy(() -> new ClassName("part..MyClass"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void getNameShouldReturnSuppliedValue() {
+        String name = "org.apache.MyClass";
+        assertThat(new ClassName(name).getName())
+            .isEqualTo(name);
+    }
+
+    @Test
+    void getNameShouldReturnSuppliedValueWhenNoPackage() {
+        String name = "MyClass";
+        assertThat(new ClassName(name).getName())
+            .isEqualTo(name);
+    }
+
+    @Test
+    void asFullyQualifiedShouldReturnCorrespondingFullyQualifiedClassName() {
+        String name = "org.apache.MyClass";
+        assertThat(new ClassName(name).asFullyQualified())
+            .isEqualTo(new FullyQualifiedClassName(name));
+    }
+
+    @Test
+    void appendPackageShouldAddPackageInFullyQualifiedClassName() {
+        String name = "MyClass";
+        String packageName = "org.apache";
+        assertThat(new 
ClassName(name).appendPackage(PackageName.of(packageName)))
+            .isEqualTo(new FullyQualifiedClassName("org.apache.MyClass"));
+    }
+
+    @Test
+    void 
appendPackageShouldAddPackageInFullyQualifiedClassNameWhenAPackagePartAlreadyExists()
 {
+        String name = "part.MyClass";
+        String packageName = "org.apache";
+        assertThat(new 
ClassName(name).appendPackage(PackageName.of(packageName)))
+            .isEqualTo(new FullyQualifiedClassName("org.apache.part.MyClass"));
+    }
+}
\ No newline at end of file
diff --git 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
 
b/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/FullyQualifiedClassNameTest.java
similarity index 52%
copy from 
server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
copy to 
server/container/guice/guice-utils/src/test/java/org/apache/james/utils/FullyQualifiedClassNameTest.java
index db8959ad..6dff3dd 100644
--- 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
+++ 
b/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/FullyQualifiedClassNameTest.java
@@ -19,35 +19,43 @@
 
 package org.apache.james.utils;
 
-import javax.mail.MessagingException;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-import org.apache.james.mailetcontainer.api.MatcherLoader;
-import org.apache.mailet.Matcher;
-import org.apache.mailet.MatcherConfig;
+import org.junit.jupiter.api.Test;
 
-import com.google.inject.Inject;
-import com.google.inject.Injector;
+import nl.jqno.equalsverifier.EqualsVerifier;
 
-public class GuiceMatcherLoader implements MatcherLoader {
-
-    private static final String STANDARD_PACKAGE = 
"org.apache.james.transport.matchers.";
+class FullyQualifiedClassNameTest {
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(FullyQualifiedClassName.class)
+            .verify();
+    }
 
-    private final GuiceGenericLoader<Matcher> genericLoader;
+    @Test
+    void constructorShouldThrowWhenNull() {
+        assertThatThrownBy(() -> new FullyQualifiedClassName(null))
+            .isInstanceOf(NullPointerException.class);
+    }
 
-    @Inject
-    public GuiceMatcherLoader(Injector injector, ExtendedClassLoader 
extendedClassLoader) {
-        this.genericLoader = new GuiceGenericLoader<>(injector, 
extendedClassLoader, STANDARD_PACKAGE);
+    @Test
+    void constructorShouldThrowWhenEmpty() {
+        assertThatThrownBy(() -> new FullyQualifiedClassName(""))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
-    @Override
-    public Matcher getMatcher(MatcherConfig config) throws MessagingException {
-        try {
-            Matcher result = 
genericLoader.instanciate(config.getMatcherName());
-            result.init(config);
-            return result;
-        } catch (Exception e) {
-            throw new MessagingException("Can not load matcher " + 
config.getMatcherName(), e);
-        }
+    @Test
+    void getNameShouldReturnSuppliedValue() {
+        String name = "org.apache.MyClass";
+        assertThat(new FullyQualifiedClassName(name).getName())
+            .isEqualTo(name);
     }
 
-}
+    @Test
+    void getNameShouldReturnSuppliedValueWhenOnlyAClassName() {
+        String name = "MyClass";
+        assertThat(new FullyQualifiedClassName(name).getName())
+            .isEqualTo(name);
+    }
+}
\ No newline at end of file
diff --git 
a/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/NamingSchemeTest.java
 
b/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/NamingSchemeTest.java
new file mode 100644
index 0000000..22a3dc6
--- /dev/null
+++ 
b/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/NamingSchemeTest.java
@@ -0,0 +1,78 @@
+/****************************************************************
+ * 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.apache.james.utils.NamingScheme.IDENTITY;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class NamingSchemeTest {
+    public static final String PACKAGE = "org.apache.james";
+    public static final NamingScheme.OptionalPackagePrefix PACKAGE_PREFIX = 
new NamingScheme.OptionalPackagePrefix(PackageName.of(PACKAGE));
+
+    @Nested
+    class IdentityTest {
+        @Test
+        void asFullyQualifiedShouldPromoteNameToFullyQualifiedClass() {
+            String name = "org.MyClass";
+            ClassName className = new ClassName(name);
+
+            assertThat(IDENTITY.toFullyQualifiedClassNames(className))
+                .containsExactly(new FullyQualifiedClassName(name));
+        }
+
+        @Test
+        void 
asFullyQualifiedShouldPromoteNameToFullyQualifiedClassWhenNoPackagePart() {
+            String name = "MyClass";
+            ClassName className = new ClassName(name);
+
+            assertThat(IDENTITY.toFullyQualifiedClassNames(className))
+                .containsExactly(new FullyQualifiedClassName(name));
+        }
+    }
+
+    @Nested
+    class OptionalPackagePrefixTest {
+
+        @Test
+        void asFullyQualifiedShouldPromoteNameToFullyQualifiedClass() {
+            String name = "org.MyClass";
+            ClassName className = new ClassName(name);
+
+            assertThat(PACKAGE_PREFIX.toFullyQualifiedClassNames(className))
+                .containsExactly(
+                    new FullyQualifiedClassName("org.MyClass"),
+                    new 
FullyQualifiedClassName("org.apache.james.org.MyClass"));
+        }
+
+        @Test
+        void 
asFullyQualifiedShouldPromoteNameToFullyQualifiedClassWhenNoPackagePart() {
+            String name = "MyClass";
+            ClassName className = new ClassName(name);
+
+            assertThat(PACKAGE_PREFIX.toFullyQualifiedClassNames(className))
+                .containsExactly(
+                    new FullyQualifiedClassName("MyClass"),
+                    new FullyQualifiedClassName("org.apache.james.MyClass"));
+        }
+    }
+}
\ No newline at end of file
diff --git 
a/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/PackageNameTest.java
 
b/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/PackageNameTest.java
new file mode 100644
index 0000000..cce1a01
--- /dev/null
+++ 
b/server/container/guice/guice-utils/src/test/java/org/apache/james/utils/PackageNameTest.java
@@ -0,0 +1,72 @@
+/****************************************************************
+ * 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 org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class PackageNameTest {
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(PackageName.class)
+            .verify();
+    }
+
+    @Test
+    void ofShouldThrowWhenNull() {
+        assertThatThrownBy(() -> PackageName.of(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void ofShouldThrowWhenEmpty() {
+        assertThatThrownBy(() -> PackageName.of(""))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void getNameShouldReturnSuppliedValue() {
+        String name = "org.apache.MyClass";
+        assertThat(PackageName.of(name).getName())
+            .isEqualTo(name);
+    }
+
+    @Test
+    void ofShouldThrowWhenStartingDot() {
+        assertThatThrownBy(() -> PackageName.of(".org.apache.MyClass"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void ofShouldSanitizeEndingDot() {
+        assertThat(PackageName.of("org.apache.MyClass.").getName())
+            .isEqualTo("org.apache.MyClass");
+    }
+
+    @Test
+    void ofShouldThrowWhenDoubleDot() {
+        assertThatThrownBy(() -> PackageName.of("org.apache..MyClass"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+}
\ No newline at end of file
diff --git 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceGenericLoader.java
 
b/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceGenericLoader.java
index cdd6d41..d8c6188 100644
--- 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceGenericLoader.java
+++ 
b/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceGenericLoader.java
@@ -19,42 +19,38 @@
 
 package org.apache.james.utils;
 
-import java.util.Optional;
-
-import org.apache.james.util.OptionalUtils;
+import java.util.stream.Stream;
 
 import com.google.inject.Injector;
 
 public class GuiceGenericLoader<T> {
     private final Injector injector;
-    private final String defaultPackageName;
+    private final NamingScheme namingScheme;
     private final ExtendedClassLoader extendedClassLoader;
 
-    public GuiceGenericLoader(Injector injector, ExtendedClassLoader 
extendedClassLoader, String defaultPackageName) {
+    public GuiceGenericLoader(Injector injector, ExtendedClassLoader 
extendedClassLoader, NamingScheme namingScheme) {
         this.injector = injector;
-        this.defaultPackageName = defaultPackageName;
+        this.namingScheme = namingScheme;
         this.extendedClassLoader = extendedClassLoader;
     }
 
-
-    public T instanciate(String className) throws Exception {
+    public T instanciate(ClassName className) throws Exception {
         Class<T> clazz = locateClass(className);
         return injector.getInstance(clazz);
     }
 
-    private Class<T> locateClass(String className) throws 
ClassNotFoundException {
-        return OptionalUtils.orSuppliers(
-                () -> tryLocateClass(className),
-                () -> tryLocateClass(defaultPackageName + className),
-                () -> tryLocateClass(defaultPackageName + "." + className))
-            .orElseThrow(() -> new ClassNotFoundException(className));
+    private Class<T> locateClass(ClassName className) throws 
ClassNotFoundException {
+        return namingScheme.toFullyQualifiedClassNames(className)
+            .flatMap(this::tryLocateClass)
+            .findFirst()
+            .orElseThrow(() -> new 
ClassNotFoundException(className.getName()));
     }
 
-    private Optional<Class<T>> tryLocateClass(String className) {
+    private Stream<Class<T>> tryLocateClass(FullyQualifiedClassName className) 
{
         try {
-            return Optional.of(extendedClassLoader.locateClass(className));
+            return Stream.of(extendedClassLoader.locateClass(className));
         } catch (ClassNotFoundException e) {
-            return Optional.empty();
+            return Stream.empty();
         }
     }
 
diff --git 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMailetLoader.java
 
b/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMailetLoader.java
index 096aaee..ac6296d 100644
--- 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMailetLoader.java
+++ 
b/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMailetLoader.java
@@ -34,15 +34,15 @@ import com.google.inject.Inject;
 import com.google.inject.Injector;
 
 public class GuiceMailetLoader implements MailetLoader {
-
-    private static final String STANDARD_PACKAGE = 
"org.apache.james.transport.mailets.";
+    private static final PackageName STANDARD_PACKAGE = 
PackageName.of("org.apache.james.transport.mailets.");
+    private static final NamingScheme MAILET_NAMING_SCHEME = new 
NamingScheme.OptionalPackagePrefix(STANDARD_PACKAGE);
 
     private final GuiceGenericLoader<Mailet> genericLoader;
     private final Map<Class<? extends Mailet>, MailetConfig> 
configurationOverrides;
 
     @Inject
     public GuiceMailetLoader(Injector injector, ExtendedClassLoader 
extendedClassLoader, Set<MailetConfigurationOverride> 
mailetConfigurationOverrides) {
-        this.genericLoader = new GuiceGenericLoader<>(injector, 
extendedClassLoader, STANDARD_PACKAGE);
+        this.genericLoader = new GuiceGenericLoader<>(injector, 
extendedClassLoader, MAILET_NAMING_SCHEME);
         this.configurationOverrides = mailetConfigurationOverrides.stream()
             .collect(Guavate.toImmutableMap(
                 MailetConfigurationOverride::getClazz,
@@ -52,7 +52,8 @@ public class GuiceMailetLoader implements MailetLoader {
     @Override
     public Mailet getMailet(MailetConfig config) throws MessagingException {
         try {
-            Mailet result = genericLoader.instanciate(config.getMailetName());
+            ClassName className = new ClassName(config.getMailetName());
+            Mailet result = genericLoader.instanciate(className);
             result.init(resolveConfiguration(result, config));
             return result;
         } catch (Exception e) {
@@ -64,5 +65,4 @@ public class GuiceMailetLoader implements MailetLoader {
         return 
Optional.ofNullable(configurationOverrides.get(result.getClass()))
             .orElse(providedConfiguration);
     }
-
 }
diff --git 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
 
b/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
index db8959ad..6d2f874 100644
--- 
a/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
+++ 
b/server/container/guice/mailet/src/main/java/org/apache/james/utils/GuiceMatcherLoader.java
@@ -29,25 +29,25 @@ import com.google.inject.Inject;
 import com.google.inject.Injector;
 
 public class GuiceMatcherLoader implements MatcherLoader {
-
-    private static final String STANDARD_PACKAGE = 
"org.apache.james.transport.matchers.";
+    private static final PackageName STANDARD_PACKAGE = 
PackageName.of("org.apache.james.transport.matchers.");
+    private static final NamingScheme MATCHER_NAMING_SCHEME = new 
NamingScheme.OptionalPackagePrefix(STANDARD_PACKAGE);
 
     private final GuiceGenericLoader<Matcher> genericLoader;
 
     @Inject
     public GuiceMatcherLoader(Injector injector, ExtendedClassLoader 
extendedClassLoader) {
-        this.genericLoader = new GuiceGenericLoader<>(injector, 
extendedClassLoader, STANDARD_PACKAGE);
+        this.genericLoader = new GuiceGenericLoader<>(injector, 
extendedClassLoader, MATCHER_NAMING_SCHEME);
     }
 
     @Override
     public Matcher getMatcher(MatcherConfig config) throws MessagingException {
         try {
-            Matcher result = 
genericLoader.instanciate(config.getMatcherName());
+            ClassName className = new ClassName(config.getMatcherName());
+            Matcher result = genericLoader.instanciate(className);
             result.init(config);
             return result;
         } catch (Exception e) {
             throw new MessagingException("Can not load matcher " + 
config.getMatcherName(), e);
         }
     }
-
 }


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

Reply via email to