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