diff --git a/di/pom.xml b/di/pom.xml
new file mode 100644
index 0000000..5a5ac12
--- /dev/null
+++ b/di/pom.xml
@@ -0,0 +1,11 @@
+
+
+ 4.0.0
+
+ fr.gasser
+ java-cookbook
+ 1.0-SNAPSHOT
+
+ di
+
\ No newline at end of file
diff --git a/di/src/main/java/ovh/gasser/di/InjectConstant.java b/di/src/main/java/ovh/gasser/di/InjectConstant.java
new file mode 100644
index 0000000..1f31fa7
--- /dev/null
+++ b/di/src/main/java/ovh/gasser/di/InjectConstant.java
@@ -0,0 +1,11 @@
+package ovh.gasser.di;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD })
+public @interface InjectConstant {
+}
diff --git a/di/src/main/java/ovh/gasser/di/InjectionUtils.java b/di/src/main/java/ovh/gasser/di/InjectionUtils.java
new file mode 100644
index 0000000..a28859b
--- /dev/null
+++ b/di/src/main/java/ovh/gasser/di/InjectionUtils.java
@@ -0,0 +1,18 @@
+package ovh.gasser.di;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public interface InjectionUtils {
+ static void populateConstants(Class> clazz, Map, ?> values) {
+ Arrays.stream(clazz.getDeclaredFields())
+ .filter(field -> field.isAnnotationPresent(InjectConstant.class))
+ .forEach(field -> {
+ try {
+ field.set(null, values.get(field.getName()));
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Unable to setup constant for field " + field.getName(), e);
+ }
+ });
+ }
+}
diff --git a/di/src/main/java/ovh/gasser/di/TestClass.java b/di/src/main/java/ovh/gasser/di/TestClass.java
new file mode 100644
index 0000000..0cf3504
--- /dev/null
+++ b/di/src/main/java/ovh/gasser/di/TestClass.java
@@ -0,0 +1,17 @@
+package ovh.gasser.di;
+
+import java.util.Map;
+
+public class TestClass {
+ @InjectConstant
+ public static String TEST;
+
+ static {
+ Map values = Map.of("TEST", "Injected value for TEST");
+ InjectionUtils.populateConstants(TestClass.class, values);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(TEST);
+ }
+}
diff --git a/di/src/main/java/ovh/gasser/translation/InjectTranslation.java b/di/src/main/java/ovh/gasser/translation/InjectTranslation.java
new file mode 100644
index 0000000..149fad1
--- /dev/null
+++ b/di/src/main/java/ovh/gasser/translation/InjectTranslation.java
@@ -0,0 +1,11 @@
+package ovh.gasser.translation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD })
+public @interface InjectTranslation {
+}
diff --git a/di/src/main/java/ovh/gasser/translation/InjectionUtils.java b/di/src/main/java/ovh/gasser/translation/InjectionUtils.java
new file mode 100644
index 0000000..afc12b2
--- /dev/null
+++ b/di/src/main/java/ovh/gasser/translation/InjectionUtils.java
@@ -0,0 +1,50 @@
+package ovh.gasser.translation;
+
+import org.w3c.dom.Document;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.*;
+import java.util.Arrays;
+
+final class InjectionUtils {
+ private static final XPath xPath;
+ private static final Document document;
+
+ static {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ try {
+ var resource = InjectionUtils.class.getResource("/translations.xml");
+ DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
+ document = documentBuilder.parse(resource.getFile());
+ XPathFactory xpf = XPathFactory.newInstance();
+ xPath = xpf.newXPath();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Private ctor to prevent instantiation
+ */
+ private InjectionUtils() {
+ throw new IllegalStateException();
+ }
+
+ static void populateTranslations(Class> clazz, String language) {
+ Arrays.stream(clazz.getDeclaredFields())
+ .filter(field -> field.isAnnotationPresent(InjectTranslation.class))
+ .forEach(field -> {
+ try {
+ String expression = String.format("/translations/string[@key='%s']/translation[@lang='%s']", field.getName(), language);
+ XPathExpression xPathExpression = xPath.compile(expression);
+ String s = (String) xPathExpression.evaluate(document, XPathConstants.STRING);
+ field.set(null, s);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Unable to setup constant for field " + field.getName(), e);
+ } catch (XPathExpressionException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+}
diff --git a/di/src/main/java/ovh/gasser/translation/Main.java b/di/src/main/java/ovh/gasser/translation/Main.java
new file mode 100644
index 0000000..b12c29c
--- /dev/null
+++ b/di/src/main/java/ovh/gasser/translation/Main.java
@@ -0,0 +1,13 @@
+package ovh.gasser.translation;
+
+public class Main {
+ public static void main(String[] args) {
+ InjectionUtils.populateTranslations(TestClass.class, "DE");
+ System.out.println(TestClass.HELLO);
+ }
+
+ private static class TestClass {
+ @InjectTranslation
+ static String HELLO;
+ }
+}
diff --git a/di/src/main/resources/translations.xml b/di/src/main/resources/translations.xml
new file mode 100644
index 0000000..e30b91f
--- /dev/null
+++ b/di/src/main/resources/translations.xml
@@ -0,0 +1,8 @@
+
+
+
+ Hello
+ Bonjour
+ Guten tag
+
+
\ No newline at end of file
diff --git a/lang/src/main/java/annotations/jsonserializer/JsonSerializeException.java b/lang/src/main/java/annotations/jsonserializer/JsonSerializeException.java
index 5f2abea..492da56 100644
--- a/lang/src/main/java/annotations/jsonserializer/JsonSerializeException.java
+++ b/lang/src/main/java/annotations/jsonserializer/JsonSerializeException.java
@@ -1,7 +1,7 @@
package annotations.jsonserializer;
class JsonSerializeException extends RuntimeException {
- JsonSerializeException(String message) {
- super(message);
+ public JsonSerializeException(String message, Throwable cause) {
+ super(message, cause);
}
}
diff --git a/lang/src/main/java/annotations/jsonserializer/SerializableFieldExtractor.java b/lang/src/main/java/annotations/jsonserializer/SerializableFieldExtractor.java
index d2f4324..9ffbf46 100644
--- a/lang/src/main/java/annotations/jsonserializer/SerializableFieldExtractor.java
+++ b/lang/src/main/java/annotations/jsonserializer/SerializableFieldExtractor.java
@@ -21,7 +21,7 @@ class SerializableFieldExtractor {
}
return jsonElements;
} catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
- throw new JsonSerializeException(e.getMessage());
+ throw new JsonSerializeException("JSON serializing error", e);
}
}
diff --git a/messaging/src/test/java/messagebroker/handlers/EchoMessageHandlerTest.java b/messaging/src/test/java/messagebroker/handlers/EchoMessageHandlerTest.java
new file mode 100644
index 0000000..feb7bb8
--- /dev/null
+++ b/messaging/src/test/java/messagebroker/handlers/EchoMessageHandlerTest.java
@@ -0,0 +1,39 @@
+package messagebroker.handlers;
+
+import messagebroker.MessageHandlerContext;
+import messagebroker.UnknownMessageFormatException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import static org.mockito.Mockito.*;
+
+public class EchoMessageHandlerTest {
+
+ private final MessageHandlerContext context = mock(MessageHandlerContext.class);
+ private EchoMessageHandler messageHandler;
+
+ public EchoMessageHandlerTest() {
+ when(context.getOutputStream()).thenReturn(mock(OutputStream.class));
+ }
+
+ @Before()
+ public void setUp() {
+ messageHandler = new EchoMessageHandler(context);
+ }
+
+ @Test
+ public void shouldHandleEchoMessage() throws UnknownMessageFormatException, IOException {
+ var message = "ECHO Some message";
+ messageHandler.handleMessage(message);
+ verify(context.getOutputStream()).flush();
+ }
+
+ @Test(expected = UnknownMessageFormatException.class)
+ public void shouldThrowWhenMessageIsUnknown() throws UnknownMessageFormatException {
+ var message = "Bad message";
+ messageHandler.handleMessage(message);
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 8f935c1..9efa96c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,6 +13,8 @@
lang
persistence
messaging
+ di
+ spi
diff --git a/spi/pom.xml b/spi/pom.xml
new file mode 100644
index 0000000..d1c72ba
--- /dev/null
+++ b/spi/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ fr.gasser
+ java-cookbook
+ 1.0-SNAPSHOT
+
+
+ 4.0.0
+
+ spi
+
+
\ No newline at end of file
diff --git a/spi/src/main/java/ovh/gasser/spi/CommandProvider.java b/spi/src/main/java/ovh/gasser/spi/CommandProvider.java
new file mode 100644
index 0000000..8937000
--- /dev/null
+++ b/spi/src/main/java/ovh/gasser/spi/CommandProvider.java
@@ -0,0 +1,32 @@
+package ovh.gasser.spi;
+
+import ovh.gasser.spi.commands.Command;
+
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.stream.Stream;
+
+public class CommandProvider {
+ private static CommandProvider instance = new CommandProvider();
+
+ private final ServiceLoader loader;
+
+ private CommandProvider() {
+ loader = ServiceLoader.load(Command.class);
+ }
+
+ public static CommandProvider getInstance() {
+ return instance;
+ }
+
+ public Stream getCommands() {
+ return loader.stream().map(ServiceLoader.Provider::get);
+ }
+
+ public Optional getCommand(String commandId) {
+ return loader.stream()
+ .filter(commandProvider -> commandProvider.get().getId().equalsIgnoreCase(commandId))
+ .findFirst()
+ .map(ServiceLoader.Provider::get);
+ }
+}
diff --git a/spi/src/main/java/ovh/gasser/spi/Main.java b/spi/src/main/java/ovh/gasser/spi/Main.java
new file mode 100644
index 0000000..5a2d0a7
--- /dev/null
+++ b/spi/src/main/java/ovh/gasser/spi/Main.java
@@ -0,0 +1,16 @@
+package ovh.gasser.spi;
+
+public class Main {
+ public static void main(String[] args) {
+ CommandProvider commandProvider = CommandProvider.getInstance();
+ // Execute all commands
+ commandProvider.getCommands()
+ .forEach(command -> System.out.println(command.execute("Hello")));
+
+ // Execute command with specific ID
+ String result = commandProvider.getCommand("reverse")
+ .map(command -> command.execute("Reverse command"))
+ .orElse("Unknown command");
+ System.out.println(result);
+ }
+}
diff --git a/spi/src/main/java/ovh/gasser/spi/commands/Command.java b/spi/src/main/java/ovh/gasser/spi/commands/Command.java
new file mode 100644
index 0000000..7a0722d
--- /dev/null
+++ b/spi/src/main/java/ovh/gasser/spi/commands/Command.java
@@ -0,0 +1,7 @@
+package ovh.gasser.spi.commands;
+
+public interface Command {
+ String getId();
+
+ String execute(String input);
+}
diff --git a/spi/src/main/java/ovh/gasser/spi/commands/EchoCommand.java b/spi/src/main/java/ovh/gasser/spi/commands/EchoCommand.java
new file mode 100644
index 0000000..87fb60a
--- /dev/null
+++ b/spi/src/main/java/ovh/gasser/spi/commands/EchoCommand.java
@@ -0,0 +1,14 @@
+package ovh.gasser.spi.commands;
+
+public class EchoCommand implements Command {
+
+ private static final String COMMAND_NAME = "echo";
+
+ public String getId() {
+ return COMMAND_NAME;
+ }
+
+ public String execute(String input) {
+ return input;
+ }
+}
diff --git a/spi/src/main/java/ovh/gasser/spi/commands/ReverseCommand.java b/spi/src/main/java/ovh/gasser/spi/commands/ReverseCommand.java
new file mode 100644
index 0000000..fe4edbe
--- /dev/null
+++ b/spi/src/main/java/ovh/gasser/spi/commands/ReverseCommand.java
@@ -0,0 +1,16 @@
+package ovh.gasser.spi.commands;
+
+public class ReverseCommand implements Command {
+
+ private static final String COMMAND_NAME = "reverse";
+
+ @Override
+ public String getId() {
+ return COMMAND_NAME;
+ }
+
+ @Override
+ public String execute(String input) {
+ return new StringBuilder(input).reverse().toString();
+ }
+}
diff --git a/spi/src/main/java/ovh/gasser/spi/commands/UpperCommand.java b/spi/src/main/java/ovh/gasser/spi/commands/UpperCommand.java
new file mode 100644
index 0000000..4bb281b
--- /dev/null
+++ b/spi/src/main/java/ovh/gasser/spi/commands/UpperCommand.java
@@ -0,0 +1,16 @@
+package ovh.gasser.spi.commands;
+
+public class UpperCommand implements Command {
+
+ private static final String COMMAND_NAME = "upper";
+
+ @Override
+ public String getId() {
+ return COMMAND_NAME;
+ }
+
+ @Override
+ public String execute(String input) {
+ return input.toUpperCase();
+ }
+}
diff --git a/spi/src/main/resources/META-INF/services/ovh.gasser.spi.commands.Command b/spi/src/main/resources/META-INF/services/ovh.gasser.spi.commands.Command
new file mode 100644
index 0000000..bf5fcde
--- /dev/null
+++ b/spi/src/main/resources/META-INF/services/ovh.gasser.spi.commands.Command
@@ -0,0 +1,3 @@
+ovh.gasser.spi.commands.EchoCommand
+ovh.gasser.spi.commands.UpperCommand
+ovh.gasser.spi.commands.ReverseCommand
\ No newline at end of file