From 0b78c32b91761a7025e19d86276ae4a24f27626e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Wed, 20 Mar 2024 17:28:34 +0100 Subject: [PATCH] make path events work with payload --- build.gradle | 17 +-- .../java/org/xbib/event/GenericEvent.java | 8 ++ .../src/main/java/org/xbib/event/Payload.java | 12 +- event-common/build.gradle | 1 - event-common/src/main/java/module-info.java | 23 +-- .../org/xbib/event/common/EventManager.java | 131 ++++++++++-------- .../xbib/event/common/GenericEventImpl.java | 10 +- .../org/xbib/event/common/PayloadImpl.java | 35 +++++ .../event/path/PathEventManagerService.java | 66 ++++++--- .../org/xbib/event/path/PathEventService.java | 11 +- .../org/xbib/event/persistence/Journal.java | 2 +- .../event/timer/TimerEventManagerService.java | 6 +- .../xbib/event/timer/TimerEventService.java | 10 +- .../xbib/event/util/ExceptionFormatter.java | 56 ++++++++ .../xbib/event/path/PathEventManagerTest.java | 55 +++++++- .../event/timer/TimerEventManagerTest.java | 4 +- event-net-http/build.gradle | 6 +- .../event/net/http/test/HttpEventTest.java | 38 +++-- ...t.http.server.netty.HttpChannelInitializer | 4 + gradle.properties | 2 +- settings.gradle | 10 +- 21 files changed, 346 insertions(+), 161 deletions(-) create mode 100644 event-api/src/main/java/org/xbib/event/GenericEvent.java create mode 100644 event-common/src/main/java/org/xbib/event/common/PayloadImpl.java create mode 100644 event-common/src/main/java/org/xbib/event/util/ExceptionFormatter.java create mode 100644 event-net-http/src/test/resources/META-INF/services/org.xbib.net.http.server.netty.HttpChannelInitializer diff --git a/build.gradle b/build.gradle index 6cdbe89..428cbaf 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ ext { user = 'joerg' name = 'event' description = 'Event framework for Java (NIO paths, files, timers, journals)' - inceptionYear = '2021' + inceptionYear = '2024' url = 'https://xbib.org/' + user + '/' + name scmUrl = 'https://xbib.org/' + user + '/' + name scmConnection = 'scm:git:git://xbib.org/' + user + '/' + name + '.git' @@ -28,20 +28,7 @@ subprojects { apply from: rootProject.file('gradle/compile/java.gradle') apply from: rootProject.file('gradle/test/junit5.gradle') apply from: rootProject.file('gradle/repositories/maven.gradle') + apply from: rootProject.file('gradle/publish/maven.gradle') } apply from: rootProject.file('gradle/publish/sonatype.gradle') apply from: rootProject.file('gradle/publish/forgejo.gradle') - -/* -dependencies { - api libs.settings.api - implementation libs.net - implementation libs.time - implementation libs.datastructures.common - implementation libs.datastructures.json.tiny - implementation libs.netty.handler - implementation libs.reactivestreams - testImplementation libs.rxjava3 - testImplementation libs.settings.datastructures.json -} -*/ \ No newline at end of file diff --git a/event-api/src/main/java/org/xbib/event/GenericEvent.java b/event-api/src/main/java/org/xbib/event/GenericEvent.java new file mode 100644 index 0000000..eee077b --- /dev/null +++ b/event-api/src/main/java/org/xbib/event/GenericEvent.java @@ -0,0 +1,8 @@ +package org.xbib.event; + +public interface GenericEvent extends Event { + + GenericEvent setListener(Listener listener); + + void received(); +} diff --git a/event-api/src/main/java/org/xbib/event/Payload.java b/event-api/src/main/java/org/xbib/event/Payload.java index de6d3b4..d71929f 100644 --- a/event-api/src/main/java/org/xbib/event/Payload.java +++ b/event-api/src/main/java/org/xbib/event/Payload.java @@ -1,16 +1,6 @@ package org.xbib.event; -import java.util.LinkedHashMap; import java.util.Map; -@SuppressWarnings("serial") -public class Payload extends LinkedHashMap { - - public Payload() { - super(); - } - - public Payload(Map map) { - super(map); - } +public interface Payload extends Map { } diff --git a/event-common/build.gradle b/event-common/build.gradle index 67a71b0..10b2280 100644 --- a/event-common/build.gradle +++ b/event-common/build.gradle @@ -1,7 +1,6 @@ dependencies { api project(':event-api') api libs.settings.api - implementation libs.net implementation libs.time implementation libs.settings.datastructures.json implementation libs.datastructures.common diff --git a/event-common/src/main/java/module-info.java b/event-common/src/main/java/module-info.java index 7826d75..340cd25 100644 --- a/event-common/src/main/java/module-info.java +++ b/event-common/src/main/java/module-info.java @@ -1,6 +1,16 @@ import org.xbib.event.common.EventManagerService; +import org.xbib.event.EventConsumer; +import org.xbib.event.Event; module org.xbib.event.common { + requires java.logging; + requires org.xbib.event.api; + requires org.xbib.settings.api; + requires org.xbib.settings.datastructures.json; + requires org.xbib.time; + requires org.xbib.datastructures.common; + requires org.xbib.datastructures.tiny; + requires org.xbib.datastructures.json.tiny; exports org.xbib.event.bus; exports org.xbib.event.clock; exports org.xbib.event.common; @@ -8,16 +18,9 @@ module org.xbib.event.common { exports org.xbib.event.path; exports org.xbib.event.persistence; exports org.xbib.event.timer; + exports org.xbib.event.util; exports org.xbib.event.wal; + uses Event; + uses EventConsumer; uses EventManagerService; - uses org.xbib.event.EventConsumer; - uses org.xbib.event.Event; - requires org.xbib.event.api; - requires org.xbib.settings.api; - requires org.xbib.settings.datastructures.json; - requires org.xbib.net; - requires org.xbib.time; - requires org.xbib.datastructures.common; - requires org.xbib.datastructures.json.tiny; - requires java.logging; } diff --git a/event-common/src/main/java/org/xbib/event/common/EventManager.java b/event-common/src/main/java/org/xbib/event/common/EventManager.java index ae8b556..1639edc 100644 --- a/event-common/src/main/java/org/xbib/event/common/EventManager.java +++ b/event-common/src/main/java/org/xbib/event/common/EventManager.java @@ -16,7 +16,6 @@ import org.xbib.datastructures.json.tiny.JsonBuilder; import org.xbib.event.Event; import org.xbib.event.EventConsumer; import org.xbib.event.Listener; -import org.xbib.event.Payload; import org.xbib.event.bus.AsyncEventBus; import org.xbib.event.bus.EventBus; import org.xbib.event.bus.Subscriber; @@ -76,6 +75,70 @@ public final class EventManager extends AbstractEventManagerService implements E logger.log(Level.INFO, "installed event service managers = " + eventManagerServices.keySet()); } + @SuppressWarnings("unchecked") + public static E eventOf(String eventType, Class eventClass) { + return (E) eventBuilder() + .setType(eventType) + .build(); + } + + public static Event eventOf(String eventType, + String code, + String message, + Path path) { + return eventBuilder() + .setType(eventType) + .setCode(code) + .setMessage(message) + .setPath(path) + .setPayload(PayloadImpl.fromPath(path)) + .build(); + } + + public static Event eventOf(String eventType, + Instant scheduled) { + return eventBuilder() + .setType(eventType) + .setScheduledFor(scheduled) + .build(); + } + + public static Event eventFromFile(Path file) throws IOException { + return eventFromJson(Files.readString(file)); + } + + @SuppressWarnings("unchecked") + public static Event eventFromJson(String json) { + Map map = Json.toMap(json); + EventBuilder builder = eventBuilder(); + if (map.containsKey("type")) { + builder.setType(map.getOrDefault("type", "generic").toString()); + } + if (map.containsKey("code")) { + builder.setCode(map.getOrDefault("code", "").toString()); + } + if (map.containsKey("message")) { + builder.setMessage(map.getOrDefault("message", "").toString()); + } + if (map.containsKey("created")) { + String created = map.getOrDefault("created", "").toString(); + builder.setCreated(Instant.parse(created)); + } + if (map.containsKey("scheduled")) { + String scheduled = map.getOrDefault("scheduled", "").toString(); + builder.setCreated(Instant.parse(scheduled)); + } + if (map.containsKey("payload")) { + PayloadImpl payload = new PayloadImpl((Map) map.get("payload")); + builder.setPayload(payload); + } + if (map.containsKey("path")) { + Path path = Paths.get((String) map.get("path")); + builder.setPath(path); + } + return builder.build(); + } + public EventManagerService init(EventManager eventManager) { return this; } @@ -287,62 +350,6 @@ public final class EventManager extends AbstractEventManagerService implements E } } - public static Event eventOf(String eventType, - String code, - String message, - Path path) { - return eventBuilder() - .setType(eventType) - .setCode(code) - .setMessage(message) - .setPath(path) - .build(); - } - - public static Event eventOf(String eventType, - Instant scheduled) { - return eventBuilder() - .setType(eventType) - .setScheduledFor(scheduled) - .build(); - } - - public static Event eventFromFile(Path file) throws IOException { - return eventFromJson(Files.readString(file)); - } - - @SuppressWarnings("unchecked") - public static Event eventFromJson(String json) { - Map map = Json.toMap(json); - EventBuilder builder = eventBuilder(); - if (map.containsKey("type")) { - builder.setType(map.getOrDefault("type", "generic").toString()); - } - if (map.containsKey("code")) { - builder.setCode(map.getOrDefault("code", "").toString()); - } - if (map.containsKey("message")) { - builder.setMessage(map.getOrDefault("message", "").toString()); - } - if (map.containsKey("created")) { - String created = map.getOrDefault("created", "").toString(); - builder.setCreated(Instant.parse(created)); - } - if (map.containsKey("scheduled")) { - String scheduled = map.getOrDefault("scheduled", "").toString(); - builder.setCreated(Instant.parse(scheduled)); - } - if (map.containsKey("payload")) { - Payload payload = new Payload((Map) map.get("payload")); - builder.setPayload(payload); - } - if (map.containsKey("path")) { - Path path = Paths.get((String) map.get("path")); - builder.setPath(path); - } - return builder.build(); - } - public static class EventImpl implements Event { private final EventBuilder builder; @@ -371,7 +378,7 @@ public final class EventManager extends AbstractEventManagerService implements E } @Override - public Payload getPayload() { + public PayloadImpl getPayload() { return builder.payload; } @@ -412,7 +419,9 @@ public final class EventManager extends AbstractEventManagerService implements E builder.fieldIfNotNull("type", getType()); builder.fieldIfNotNull("code", getCode()); builder.fieldIfNotNull("message", getMessage()); - builder.buildKey("payload").buildMap(getPayload()); + if (getPayload() != null && !getPayload().isEmpty()) { + builder.buildKey("payload").buildMap(getPayload()); + } builder.fieldIfNotNull("created", getCreated() != null ? getCreated().toString() : null); builder.fieldIfNotNull("scheduled", getScheduledFor() != null ? getScheduledFor().toString() : null); builder.fieldIfNotNull("path", getPath() != null ? getPath().toAbsolutePath().toString() : null); @@ -477,7 +486,7 @@ public final class EventManager extends AbstractEventManagerService implements E Instant scheduled; - Payload payload; + PayloadImpl payload; Path path; @@ -527,7 +536,7 @@ public final class EventManager extends AbstractEventManagerService implements E return this; } - public EventBuilder setPayload(Payload payload) { + public EventBuilder setPayload(PayloadImpl payload) { this.payload = payload; return this; } diff --git a/event-common/src/main/java/org/xbib/event/common/GenericEventImpl.java b/event-common/src/main/java/org/xbib/event/common/GenericEventImpl.java index 68f119c..6070b63 100644 --- a/event-common/src/main/java/org/xbib/event/common/GenericEventImpl.java +++ b/event-common/src/main/java/org/xbib/event/common/GenericEventImpl.java @@ -1,28 +1,26 @@ package org.xbib.event.common; +import org.xbib.event.GenericEvent; import org.xbib.event.Listener; import java.util.Objects; -public class GenericEventImpl extends EventManager.EventImpl { +public class GenericEventImpl extends EventManager.EventImpl implements GenericEvent { private final EventManager.EventBuilder builder; public GenericEventImpl(EventManager.EventBuilder builder) { - this(builder, null); - } - - public GenericEventImpl(EventManager.EventBuilder builder, Listener listener) { super(builder); this.builder = builder; - this.builder.listener = Objects.requireNonNull(listener); } + @Override public GenericEventImpl setListener(Listener listener) { this.builder.listener = Objects.requireNonNull(listener); return this; } + @Override public void received() { builder.listener.listen(this); } diff --git a/event-common/src/main/java/org/xbib/event/common/PayloadImpl.java b/event-common/src/main/java/org/xbib/event/common/PayloadImpl.java new file mode 100644 index 0000000..f924fc6 --- /dev/null +++ b/event-common/src/main/java/org/xbib/event/common/PayloadImpl.java @@ -0,0 +1,35 @@ +package org.xbib.event.common; + +import org.xbib.datastructures.json.tiny.Json; +import org.xbib.event.Payload; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; + +@SuppressWarnings("serial") +public class PayloadImpl extends LinkedHashMap implements Payload { + + public PayloadImpl() { + super(); + } + + public PayloadImpl(Map map) { + super(map); + } + + public static PayloadImpl fromPath(Path path) { + PayloadImpl payload = new PayloadImpl(); + try (InputStream inputStream = Files.newInputStream(path)) { + String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + payload.putAll(Json.toMap(content)); + } catch (IOException e) { + throw new IllegalArgumentException("broken json content in path " + path); + } + return payload; + } +} diff --git a/event-common/src/main/java/org/xbib/event/path/PathEventManagerService.java b/event-common/src/main/java/org/xbib/event/path/PathEventManagerService.java index 3662c00..a97d8c2 100644 --- a/event-common/src/main/java/org/xbib/event/path/PathEventManagerService.java +++ b/event-common/src/main/java/org/xbib/event/path/PathEventManagerService.java @@ -14,6 +14,7 @@ import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -58,17 +59,35 @@ public class PathEventManagerService extends AbstractEventManagerService impleme } else { logger.log(Level.WARNING, "path service definition not enabled in configuration"); } - } catch (Exception e) { + } catch (IOException e) { logger.log(Level.SEVERE, "unable to create event path service " + entry.getKey() + ", reason " + e.getMessage(), e); } } return this; } + public void add(String name, Path path, String eventType) throws IOException{ + createQueue(name, path); + add(new PathEventService(this, name, path, eventType, TimeValue.timeValueHours(72))); + } + + public void add(String name, Path path, String eventType, TimeValue lifetime) throws IOException{ + createQueue(name, path); + add(new PathEventService(this, name, path, eventType, lifetime)); + } + public void add(PathEventService pathEventService) { Future future = executorService.submit(pathEventService); eventServiceMap.put(future, pathEventService); - logger.log(Level.INFO, "path event service " + pathEventService + " added"); + logger.log(Level.INFO, "path event service " + pathEventService + " added for path " + pathEventService.getPath()); + } + + public Collection getPathEventServices() { + return eventServiceMap.values(); + } + + public PathEventService getPathEventService(String name) { + return eventServiceMap.values().stream().filter(p -> p.getName().equals(name)).findFirst().orElse(null); } public void publish(String eventType, Path path) { @@ -111,19 +130,28 @@ public class PathEventManagerService extends AbstractEventManagerService impleme }); } - public boolean putIfNotExists(Path path, String key, Map map) throws IOException { + public boolean publishJsonIfNotExists(PathEventService pathEventService, + String key, + Map map) throws IOException { + Path path = pathEventService.getPath(); if (!exists(path, key, ".json")) { - return put(path, key, map); + return publishJson(pathEventService, key, map); } else { return false; } } - public boolean put(Path path, String key, Map map) throws IOException { - return put(path, key, ".json", Json.toString(map)); + public boolean publishJson(PathEventService pathEventService, + String key, + Map map) throws IOException { + return publishJson(pathEventService, key, ".json", Json.toString(map)); } - public boolean put(Path path, String key, String suffix, String string) throws IOException { + public boolean publishJson(PathEventService pathEventService, + String key, + String suffix, + String payload) throws IOException { + Path path = pathEventService.getPath(); String keyFileName = key + suffix; if (Files.exists(path.resolve(Event.INCOMING).resolve(keyFileName)) || Files.exists(path.resolve(Event.SUCCESS).resolve(keyFileName))) { @@ -132,7 +160,7 @@ public class PathEventManagerService extends AbstractEventManagerService impleme } Path p = path.resolve(Event.INCOMING).resolve(keyFileName); try (Writer writer = Files.newBufferedWriter(p)) { - writer.write(string); + writer.write(payload); } // obligatory purge. This is hacky. eventServiceMap.forEach((k, v) -> { @@ -145,12 +173,6 @@ public class PathEventManagerService extends AbstractEventManagerService impleme return true; } - public boolean exists(Path path, String key, String suffix) { - String keyFileName = key + suffix; - return Files.exists(path.resolve(Event.INCOMING).resolve(keyFileName)) || - Files.exists(path.resolve(Event.SUCCESS).resolve(keyFileName)); - } - public long sizeOfIncoming(Path path) throws IOException { return sizeOf(path.resolve(Event.INCOMING)); } @@ -169,13 +191,19 @@ public class PathEventManagerService extends AbstractEventManagerService impleme } } - private static void createQueue(String name, Path p) throws IOException { - logger.log(Level.FINE, "creating queue " + name + " at " + p); - if (!Files.exists(p)) { - Files.createDirectories(p); + private static boolean exists(Path path, String key, String suffix) { + String keyFileName = key + suffix; + return Files.exists(path.resolve(Event.INCOMING).resolve(keyFileName)) || + Files.exists(path.resolve(Event.SUCCESS).resolve(keyFileName)); + } + + private static void createQueue(String name, Path path) throws IOException { + logger.log(Level.FINE, "creating queue " + name + " at " + path); + if (!Files.exists(path)) { + Files.createDirectories(path); } for (String s: List.of(Event.INCOMING, Event.SUCCESS, Event.FAIL)) { - Path dir = p.resolve(s); + Path dir = path.resolve(s); if (!Files.exists(dir)) { logger.log(Level.FINE, "creating queue " + name + " dir " + dir); Files.createDirectories(dir); diff --git a/event-common/src/main/java/org/xbib/event/path/PathEventService.java b/event-common/src/main/java/org/xbib/event/path/PathEventService.java index 96ee1f9..e4ac821 100644 --- a/event-common/src/main/java/org/xbib/event/path/PathEventService.java +++ b/event-common/src/main/java/org/xbib/event/path/PathEventService.java @@ -19,6 +19,7 @@ import java.nio.file.WatchService; import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributes; import java.time.Instant; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.logging.Level; import java.util.logging.Logger; @@ -48,7 +49,7 @@ public class PathEventService implements Callable, Closeable { TimeValue lifetime) throws IOException { this.pathEventManager = pathEventManager; this.name = name; - this.path = path; + this.path = Objects.requireNonNull(path, "path must not be null"); this.eventType = eventType; this.lifetime = lifetime; this.watchService = path.getFileSystem().newWatchService(); @@ -59,6 +60,14 @@ public class PathEventService implements Callable, Closeable { logger.log(Level.INFO, "path event service created for files at " + path); } + public String getName() { + return name; + } + + public Path getPath() { + return path; + } + @SuppressWarnings("unchecked") @Override public Boolean call() { diff --git a/event-common/src/main/java/org/xbib/event/persistence/Journal.java b/event-common/src/main/java/org/xbib/event/persistence/Journal.java index 3be111f..5e96f26 100644 --- a/event-common/src/main/java/org/xbib/event/persistence/Journal.java +++ b/event-common/src/main/java/org/xbib/event/persistence/Journal.java @@ -1,6 +1,6 @@ package org.xbib.event.persistence; -import org.xbib.net.util.ExceptionFormatter; +import org.xbib.event.util.ExceptionFormatter; import java.io.IOException; import java.io.OutputStream; diff --git a/event-common/src/main/java/org/xbib/event/timer/TimerEventManagerService.java b/event-common/src/main/java/org/xbib/event/timer/TimerEventManagerService.java index 6d72b4b..9318af3 100644 --- a/event-common/src/main/java/org/xbib/event/timer/TimerEventManagerService.java +++ b/event-common/src/main/java/org/xbib/event/timer/TimerEventManagerService.java @@ -1,7 +1,7 @@ package org.xbib.event.timer; import org.xbib.event.Event; -import org.xbib.event.Payload; +import org.xbib.event.common.PayloadImpl; import org.xbib.event.common.AbstractEventManagerService; import org.xbib.event.common.EventManager; import org.xbib.event.common.EventManagerService; @@ -67,7 +67,7 @@ public class TimerEventManagerService extends AbstractEventManagerService implem public boolean schedule(String name, String timeSpec, - Payload payload) throws ParseException, IOException { + PayloadImpl payload) throws ParseException, IOException { if (services.containsKey(name)) { Span span = Chronic.parse(timeSpec); if (span != null) { @@ -87,7 +87,7 @@ public class TimerEventManagerService extends AbstractEventManagerService implem public boolean schedule(String service, Instant instant, - Payload payload) throws IOException { + PayloadImpl payload) throws IOException { if (services.containsKey(service)) { services.get(service).schedule(instant, payload); return true; diff --git a/event-common/src/main/java/org/xbib/event/timer/TimerEventService.java b/event-common/src/main/java/org/xbib/event/timer/TimerEventService.java index 7d0c40d..29b3202 100644 --- a/event-common/src/main/java/org/xbib/event/timer/TimerEventService.java +++ b/event-common/src/main/java/org/xbib/event/timer/TimerEventService.java @@ -1,7 +1,7 @@ package org.xbib.event.timer; import org.xbib.event.Event; -import org.xbib.event.Payload; +import org.xbib.event.common.PayloadImpl; import org.xbib.event.common.EventManager; import org.xbib.event.persistence.PersistenceStore; @@ -48,7 +48,7 @@ class TimerEventService implements Closeable { logger.log(Level.INFO, "timer event service " + name + " loaded and rescheduled, " + tasknum + " timer tasks"); } - void schedule(Instant instant, Payload payload) throws IOException { + void schedule(Instant instant, PayloadImpl payload) throws IOException { String scheduled = instant.atZone(zoneId).format(DateTimeFormatter.ISO_DATE_TIME); payload.put("scheduled", scheduled); TimerEventTask timerEventTask = new TimerEventTask(payload); @@ -64,7 +64,7 @@ class TimerEventService implements Closeable { persistenceStore.clear(); persistenceStore.commit(); for (Map task : tasks) { - Payload payload = new Payload(task); + PayloadImpl payload = new PayloadImpl(task); ZonedDateTime scheduledDate = ZonedDateTime.parse((String) task.get("scheduled"), DateTimeFormatter.ISO_DATE_TIME); if (scheduledDate.isBefore(ZonedDateTime.now())) { logger.log(Level.WARNING, "scheduled timer task " + task + " date already passed"); @@ -89,9 +89,9 @@ class TimerEventService implements Closeable { public class TimerEventTask extends TimerTask { - private final Payload payload; + private final PayloadImpl payload; - public TimerEventTask(Payload payload) throws IOException { + public TimerEventTask(PayloadImpl payload) throws IOException { this.payload = payload; persistenceStore.insert("tasks", this.payload); } diff --git a/event-common/src/main/java/org/xbib/event/util/ExceptionFormatter.java b/event-common/src/main/java/org/xbib/event/util/ExceptionFormatter.java new file mode 100644 index 0000000..efb7b54 --- /dev/null +++ b/event-common/src/main/java/org/xbib/event/util/ExceptionFormatter.java @@ -0,0 +1,56 @@ +package org.xbib.event.util; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Format exception messages and stack traces. + */ +public final class ExceptionFormatter { + + private ExceptionFormatter() { + } + + /** + * Format exception with stack trace. + * + * @param t the thrown object + * @return the formatted exception + */ + public static String format(Throwable t) { + StringBuilder sb = new StringBuilder(); + append(sb, t, 0, true); + return sb.toString(); + } + + /** + * Append Exception to string builder. + */ + private static void append(StringBuilder sb, Throwable t, int level, boolean details) { + if (((t != null) && (t.getMessage() != null)) && (!t.getMessage().isEmpty())) { + if (details && (level > 0)) { + sb.append("\n\nCaused by\n"); + } + sb.append(t.getMessage()); + } + if (details) { + if (t != null) { + if ((t.getMessage() != null) && (t.getMessage().isEmpty())) { + sb.append("\n\nCaused by "); + } else { + sb.append("\n\n"); + } + } + StringWriter sw = new StringWriter(); + if (t != null) { + t.printStackTrace(new PrintWriter(sw)); + } + sb.append(sw.toString()); + } + if (t != null) { + if (t.getCause() != null) { + append(sb, t.getCause(), level + 1, details); + } + } + } +} diff --git a/event-common/src/test/java/org/xbib/event/path/PathEventManagerTest.java b/event-common/src/test/java/org/xbib/event/path/PathEventManagerTest.java index be9910e..cd1ab50 100644 --- a/event-common/src/test/java/org/xbib/event/path/PathEventManagerTest.java +++ b/event-common/src/test/java/org/xbib/event/path/PathEventManagerTest.java @@ -1,7 +1,11 @@ package org.xbib.event.path; import org.junit.jupiter.api.Test; +import org.xbib.datastructures.api.TimeValue; +import org.xbib.event.EventConsumer; import org.xbib.event.PathEvent; +import org.xbib.event.bus.AllowConcurrentEvents; +import org.xbib.event.bus.Subscribe; import org.xbib.event.common.EventManager; import org.xbib.event.common.PathEventImpl; import org.xbib.settings.Settings; @@ -10,6 +14,7 @@ import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -31,9 +36,9 @@ public class PathEventManagerTest { .build(); Path testTxt = path.resolve("incoming").resolve("test.txt"); try (BufferedWriter bufferedWriter = Files.newBufferedWriter(testTxt)) { - bufferedWriter.write("Hello"); + bufferedWriter.write("{\"Hello\":\"world\"}"); } - Thread.sleep(2000L); + Thread.sleep(1000L); eventManager.shutdown(); // extra destroy to clean up test eventManager.getPathEventManagerService().destroy(); @@ -55,9 +60,30 @@ public class PathEventManagerTest { .build(); Path testTxt = path.resolve("incoming").resolve("test.txt"); try (BufferedWriter bufferedWriter = Files.newBufferedWriter(testTxt)) { - bufferedWriter.write("Hello"); + bufferedWriter.write("{\"Hello\":\"world\"}"); } - Thread.sleep(2000L); + Thread.sleep(1000L); + eventManager.shutdown(); + // extra destroy to clean up test + eventManager.getPathEventManagerService().destroy(); + } + + @Test + public void testPathEventByApi() throws Exception { + TestPathExtEventConsumer consumer = new TestPathExtEventConsumer(); + EventManager eventManager = EventManager.builder() + .register("path-ext", PathExtEvent.class) + .register(consumer) + .build(); + // create by API + Path path = Files.createTempDirectory("testpath"); + eventManager.getPathEventManagerService().add("test", path, "path-ext", TimeValue.timeValueHours(72)); + // publish + PathEventService pathEventService = eventManager.getPathEventManagerService().getPathEventService("test"); + eventManager.getPathEventManagerService() + .publishJson(pathEventService, "key", Map.of("hello", "world")); + // everything done + Thread.sleep(1000L); eventManager.shutdown(); // extra destroy to clean up test eventManager.getPathEventManagerService().destroy(); @@ -70,4 +96,25 @@ public class PathEventManagerTest { logger.log(Level.INFO, "I'm the path ext event"); } } + + public static class TestPathEventConsumer implements EventConsumer { + + @Subscribe + @AllowConcurrentEvents + void onEvent(PathEvent event) { + logger.log(Level.INFO, "received path event, path = " + event.getPath() + + " payload = " + event.getPayload()); + } + } + + + public static class TestPathExtEventConsumer implements EventConsumer { + + @Subscribe + @AllowConcurrentEvents + void onEvent(PathExtEvent event) { + logger.log(Level.INFO, "received path ext event, path = " + event.getPath() + + " payload = " + event.getPayload()); + } + } } diff --git a/event-common/src/test/java/org/xbib/event/timer/TimerEventManagerTest.java b/event-common/src/test/java/org/xbib/event/timer/TimerEventManagerTest.java index 3670262..bde961c 100644 --- a/event-common/src/test/java/org/xbib/event/timer/TimerEventManagerTest.java +++ b/event-common/src/test/java/org/xbib/event/timer/TimerEventManagerTest.java @@ -1,7 +1,7 @@ package org.xbib.event.timer; import org.junit.jupiter.api.Test; -import org.xbib.event.Payload; +import org.xbib.event.common.PayloadImpl; import org.xbib.event.common.EventManager; import org.xbib.settings.Settings; @@ -22,7 +22,7 @@ public class TimerEventManagerTest { .register(consumer) .build(); TimerEventManagerService timerEventManager = eventManager.getTimerEventManagerService(); - Payload payload = new Payload(Map.of("a", "b")); + PayloadImpl payload = new PayloadImpl(Map.of("a", "b")); timerEventManager.schedule("testtimerevent", Instant.now().plusSeconds(5L), payload); Thread.sleep(10000L); timerEventManager.shutdown(); diff --git a/event-net-http/build.gradle b/event-net-http/build.gradle index 16087e0..bf411db 100644 --- a/event-net-http/build.gradle +++ b/event-net-http/build.gradle @@ -1,7 +1,7 @@ dependencies { api project(':event-common') - api libs.net.http.server.netty - api libs.net.http.client.netty + api libs.net.http.server.netty.secure + api libs.net.http.client.netty.secure implementation libs.settings.datastructures.json implementation libs.datastructures.json.tiny -} \ No newline at end of file +} diff --git a/event-net-http/src/test/java/org/xbib/event/net/http/test/HttpEventTest.java b/event-net-http/src/test/java/org/xbib/event/net/http/test/HttpEventTest.java index 648b553..3d86068 100644 --- a/event-net-http/src/test/java/org/xbib/event/net/http/test/HttpEventTest.java +++ b/event-net-http/src/test/java/org/xbib/event/net/http/test/HttpEventTest.java @@ -2,7 +2,10 @@ package org.xbib.event.net.http.test; import io.netty.bootstrap.Bootstrap; import org.junit.jupiter.api.Test; +import org.xbib.event.Event; +import org.xbib.event.Listener; import org.xbib.event.common.EventManager; +import org.xbib.event.common.GenericEventImpl; import org.xbib.event.net.http.HttpEventManagerService; import org.xbib.net.NetworkClass; import org.xbib.net.URL; @@ -22,15 +25,13 @@ import org.xbib.net.http.server.netty.NettyHttpServerConfig; import org.xbib.net.http.server.route.BaseHttpRouter; import org.xbib.net.http.server.route.HttpRouter; import org.xbib.net.http.server.service.BaseHttpService; -import org.xbib.net.util.JsonUtil; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class HttpEventTest { @@ -48,6 +49,7 @@ public class HttpEventTest { nettyHttpServerConfig.setDebug(true); EventManager eventManager = EventManager.builder() + .register("dummy", DummyEvent.class) .build(); HttpEventManagerService httpEventManagerService = eventManager.getEventManagerService(HttpEventManagerService.class); @@ -58,10 +60,12 @@ public class HttpEventTest { .addService(BaseHttpService.builder() .setPath("/event") .setHandler(ctx -> { + DummyEvent dummyEvent = EventManager.eventOf("dummy", DummyEvent.class); + dummyEvent.setListener(new DummyEventListener()); ctx.status(HttpResponseStatus.OK) .header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN) .charset(StandardCharsets.UTF_8) - .body(ctx.getRequest().asJson()) + .body(dummyEvent.toJson()) .done(); }) .build()) @@ -93,14 +97,9 @@ public class HttpEventTest { " status = " + resp.getStatus() + " header = " + resp.getHeaders() + " body = " + body); - try { - Map map = JsonUtil.toMap(body); - org.xbib.net.http.server.netty.HttpRequest httpRequest = org.xbib.net.http.server.netty.HttpRequest.builder() - .parse(map).build(); - logger.log(Level.INFO, "parsed http request = " + httpRequest.asJson()); - } catch (IOException e) { - throw new RuntimeException(e); - } + DummyEvent dummyEvent = (DummyEvent) EventManager.eventFromJson(body); + assertNotNull(dummyEvent); + logger.log(Level.INFO, "dummy event transported = " + dummyEvent); received.set(true); }) .build(); @@ -109,4 +108,19 @@ public class HttpEventTest { assertTrue(received.get()); } } + + public static class DummyEvent extends GenericEventImpl { + + public DummyEvent(EventManager.EventBuilder builder) { + super(builder); + } + } + + public static class DummyEventListener implements Listener { + + @Override + public void listen(Event event) { + logger.log(Level.INFO, "got event " + event); + } + } } diff --git a/event-net-http/src/test/resources/META-INF/services/org.xbib.net.http.server.netty.HttpChannelInitializer b/event-net-http/src/test/resources/META-INF/services/org.xbib.net.http.server.netty.HttpChannelInitializer new file mode 100644 index 0000000..f079b71 --- /dev/null +++ b/event-net-http/src/test/resources/META-INF/services/org.xbib.net.http.server.netty.HttpChannelInitializer @@ -0,0 +1,4 @@ +org.xbib.net.http.server.netty.http1.Http1ChannelInitializer +org.xbib.net.http.server.netty.http2.Http2ChannelInitializer +org.xbib.net.http.server.netty.secure.http1.Https1ChannelInitializer +org.xbib.net.http.server.netty.secure.http2.Https2ChannelInitializer diff --git a/gradle.properties b/gradle.properties index 56c2845..8429c56 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group = org.xbib name = event -version = 0.2.0 +version = 0.3.0 diff --git a/settings.gradle b/settings.gradle index f23b7e7..764eccf 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,18 +17,16 @@ dependencyResolutionManagement { libs { version('gradle', '8.5') version('datastructures', '5.0.6') - version('net', '4.0.4') - version('net-http', '4.1.0') - version('netty', '4.1.104.Final') + version('net-http', '4.4.0') + version('netty', '4.1.107.Final') library('netty-handler', 'io.netty', 'netty-handler').versionRef('netty') - library('net', 'org.xbib', 'net').versionRef('net') library('datastructures-common', 'org.xbib', 'datastructures-common').versionRef('datastructures') library('datastructures-json-tiny', 'org.xbib', 'datastructures-json-tiny').versionRef('datastructures') library('settings-api', 'org.xbib', 'settings-api').versionRef('datastructures') library('settings-datastructures-json', 'org.xbib', 'settings-datastructures-json').versionRef('datastructures') library('time', 'org.xbib', 'time').version('4.0.0') - library('net-http-server-netty', 'org.xbib', 'net-http-server-netty-secure').versionRef('net-http') - library('net-http-client-netty', 'org.xbib', 'net-http-client-netty-secure').versionRef('net-http') + library('net-http-server-netty-secure', 'org.xbib', 'net-http-server-netty-secure').versionRef('net-http') + library('net-http-client-netty-secure', 'org.xbib', 'net-http-client-netty-secure').versionRef('net-http') library('reactivestreams', 'org.reactivestreams', 'reactive-streams').version('1.0.3') library('rxjava3', 'io.reactivex.rxjava3', 'rxjava').version('3.0.3') }