* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.core;
+
+import jakarta.validation.constraints.NotEmpty;
+import org.fuin.ddd4j.core.AggregateAlreadyExistsException;
+import org.fuin.ddd4j.core.AggregateDeletedException;
+import org.fuin.ddd4j.core.AggregateNotFoundException;
+import org.fuin.ddd4j.core.AggregateVersionConflictException;
+import org.fuin.ddd4j.core.AggregateVersionNotFoundException;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.objects4j.common.ConstraintViolationException;
+import org.fuin.objects4j.common.Contract;
import java.util.Arrays;
import java.util.HashMap;
@@ -23,33 +33,22 @@
import java.util.Map;
import java.util.Set;
-import jakarta.validation.constraints.NotEmpty;
-
-import org.fuin.ddd4j.ddd.AggregateAlreadyExistsException;
-import org.fuin.ddd4j.ddd.AggregateDeletedException;
-import org.fuin.ddd4j.ddd.AggregateNotFoundException;
-import org.fuin.ddd4j.ddd.AggregateVersionConflictException;
-import org.fuin.ddd4j.ddd.AggregateVersionNotFoundException;
-import org.fuin.ddd4j.ddd.EventType;
-import org.fuin.objects4j.common.ConstraintViolationException;
-import org.fuin.objects4j.common.Contract;
-
/**
* Handles multiple commands by delegating the call to other executors.
- *
+ *
* @param
* Type of context for the command execution.
* @param
* Result of the command execution.
*/
-@SuppressWarnings({ "unchecked", "rawtypes" })
+@SuppressWarnings({"unchecked", "rawtypes"})
public abstract class AbstractMultiCommandExecutor implements CommandExecutor {
private final Map commandExecutors;
/**
* Constructor with command handler array.
- *
+ *
* @param cmdExecutors
* Array of command executors.
*/
@@ -59,7 +58,7 @@ public AbstractMultiCommandExecutor(@NotEmpty final CommandExecutor... cmdExecut
/**
* Constructor with mandatory data.
- *
+ *
* @param cmdExecutors
* List of command executors.
*/
diff --git a/src/main/java/org/fuin/cqrs4j/AggregateCommand.java b/core/src/main/java/org/fuin/cqrs4j/core/AggregateCommand.java
similarity index 84%
rename from src/main/java/org/fuin/cqrs4j/AggregateCommand.java
rename to core/src/main/java/org/fuin/cqrs4j/core/AggregateCommand.java
index 9f8d5e6..751c7bf 100644
--- a/src/main/java/org/fuin/cqrs4j/AggregateCommand.java
+++ b/core/src/main/java/org/fuin/cqrs4j/core/AggregateCommand.java
@@ -1,31 +1,30 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.core;
import jakarta.validation.constraints.NotNull;
-
-import org.fuin.ddd4j.ddd.AggregateRootId;
-import org.fuin.ddd4j.ddd.DomainEvent;
-import org.fuin.ddd4j.ddd.EntityId;
+import org.fuin.ddd4j.core.AggregateRootId;
+import org.fuin.ddd4j.core.DomainEvent;
+import org.fuin.ddd4j.core.EntityId;
/**
* Common behavior shared by all commands related to an aggregate.
- *
+ *
* @param
* Type of the aggregate root identifier.
* @param
@@ -35,7 +34,7 @@ public interface AggregateCommand
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.core;
-import org.fuin.ddd4j.ddd.Event;
+import org.fuin.ddd4j.core.Event;
/**
* Common behavior shared by all commands.
diff --git a/src/main/java/org/fuin/cqrs4j/CommandExecutionFailedException.java b/core/src/main/java/org/fuin/cqrs4j/core/CommandExecutionFailedException.java
similarity index 88%
rename from src/main/java/org/fuin/cqrs4j/CommandExecutionFailedException.java
rename to core/src/main/java/org/fuin/cqrs4j/core/CommandExecutionFailedException.java
index 28ff366..322a4aa 100644
--- a/src/main/java/org/fuin/cqrs4j/CommandExecutionFailedException.java
+++ b/core/src/main/java/org/fuin/cqrs4j/core/CommandExecutionFailedException.java
@@ -1,39 +1,42 @@
-/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
- * http://www.fuin.org/
- *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option) any
- * later version.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library. If not, see http://www.gnu.org/licenses/.
- */
-package org.fuin.cqrs4j;
-
-import jakarta.validation.constraints.NotNull;
-
-/**
- * The execution of a command failed. This exception is used for "tunneling" other checked exceptions during command execution.
- */
-public final class CommandExecutionFailedException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructor with all data.
- *
- * @param cause
- * Causing exception.
- */
- public CommandExecutionFailedException(@NotNull final Exception cause) {
- super(cause);
- }
-
-}
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.core;
+
+import jakarta.validation.constraints.NotNull;
+
+import java.io.Serial;
+
+/**
+ * The execution of a command failed. This exception is used for "tunneling" other checked exceptions during command execution.
+ */
+public final class CommandExecutionFailedException extends Exception {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor with all data.
+ *
+ * @param cause
+ * Causing exception.
+ */
+ public CommandExecutionFailedException(@NotNull final Exception cause) {
+ super(cause);
+ }
+
+}
diff --git a/core/src/main/java/org/fuin/cqrs4j/core/CommandExecutor.java b/core/src/main/java/org/fuin/cqrs4j/core/CommandExecutor.java
new file mode 100644
index 0000000..74a849d
--- /dev/null
+++ b/core/src/main/java/org/fuin/cqrs4j/core/CommandExecutor.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.core;
+
+import jakarta.validation.constraints.NotNull;
+import org.fuin.ddd4j.core.AggregateAlreadyExistsException;
+import org.fuin.ddd4j.core.AggregateDeletedException;
+import org.fuin.ddd4j.core.AggregateNotFoundException;
+import org.fuin.ddd4j.core.AggregateVersionConflictException;
+import org.fuin.ddd4j.core.AggregateVersionNotFoundException;
+import org.fuin.ddd4j.core.EventType;
+
+import java.util.Set;
+
+/**
+ * Executes one or more commands.
+ *
+ * @param Type of context for the command execution.
+ * @param Result of the command execution.
+ * @param Type of command to execute.
+ */
+public interface CommandExecutor {
+
+ /**
+ * Returns a list of commands this executor can handle.
+ *
+ * @return List of unique command types.
+ */
+ @NotNull
+ public Set getCommandTypes();
+
+ /**
+ * Executes the given command. Only the main aggregate related exceptions are modeled via throws. All other checked exceptions must be
+ * wrapped into a {@link CommandExecutionFailedException}.
+ *
+ * @param ctx Context of command to execute.
+ * @param cmd Command to execute.
+ * @return Result.
+ * @throws AggregateVersionConflictException There is a conflict between an expected and an actual version for the aggregate targeted by the command.
+ * @throws AggregateNotFoundException The aggregate targeted by the command with a given type and identifier was not found in the repository.
+ * @throws AggregateVersionNotFoundException The requested version for the aggregate targeted by the command does not exist.
+ * @throws AggregateDeletedException The aggregate targeted by the command was deleted from the repository.
+ * @throws AggregateAlreadyExistsException The aggregate targeted by the command already exists when trying to create it.
+ * @throws CommandExecutionFailedException Other checked exceptions are wrapped into this one.
+ */
+ public RESULT execute(@NotNull CONTEXT ctx, @NotNull CMD cmd) throws AggregateVersionConflictException, AggregateNotFoundException,
+ AggregateVersionNotFoundException, AggregateDeletedException, AggregateAlreadyExistsException, CommandExecutionFailedException;
+
+}
diff --git a/core/src/main/java/org/fuin/cqrs4j/core/CqrsUtils.java b/core/src/main/java/org/fuin/cqrs4j/core/CqrsUtils.java
new file mode 100644
index 0000000..2a38262
--- /dev/null
+++ b/core/src/main/java/org/fuin/cqrs4j/core/CqrsUtils.java
@@ -0,0 +1,35 @@
+package org.fuin.cqrs4j.core;
+
+import org.fuin.ddd4j.core.EventType;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.zip.Adler32;
+
+/**
+ * CQRS related helper functions.
+ */
+public final class CqrsUtils {
+
+ private CqrsUtils() {
+ throw new UnsupportedOperationException("Utility classes cannot be instantiated");
+ }
+
+ /**
+ * Creates an Adler32 checksum based on event type names.
+ *
+ * @param eventTypes Types to calculate a checksum for.
+ * @return Checksum based on all names.
+ */
+ public static long calculateAdler32Checksum(final Collection eventTypes) {
+ if (eventTypes == null || eventTypes.isEmpty()) {
+ throw new IllegalArgumentException("eventTypes cannot be null or empty");
+ }
+ final Adler32 checksum = new Adler32();
+ for (final EventType eventType : eventTypes) {
+ checksum.update(eventType.asBaseType().getBytes(StandardCharsets.US_ASCII));
+ }
+ return checksum.getValue();
+ }
+
+}
diff --git a/src/main/java/org/fuin/cqrs4j/EventHandler.java b/core/src/main/java/org/fuin/cqrs4j/core/JpaEventHandler.java
similarity index 63%
rename from src/main/java/org/fuin/cqrs4j/EventHandler.java
rename to core/src/main/java/org/fuin/cqrs4j/core/JpaEventHandler.java
index 25688fa..ccaba5e 100644
--- a/src/main/java/org/fuin/cqrs4j/EventHandler.java
+++ b/core/src/main/java/org/fuin/cqrs4j/core/JpaEventHandler.java
@@ -1,46 +1,46 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.core;
-import org.fuin.ddd4j.ddd.Event;
-import org.fuin.ddd4j.ddd.EventType;
+import jakarta.persistence.EntityManager;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventType;
/**
- * Does something useful using the input from an event.
- *
- * @param
- * Event type.
+ * Event handler that maps an event to JPA entities.
+ *
+ * @param Event type.
*/
-public interface EventHandler {
+public interface JpaEventHandler {
/**
* Returns the type of event this handler operates on.
- *
+ *
* @return Unique event type.
*/
public EventType getEventType();
/**
* Modifies the view using the given event.
- *
- * @param event
- * Event to use.
+ *
+ * @param entityManager Entity manager to use.
+ * @param event Event to use.
*/
- public void handle(TYPE event);
+ public void handle(EntityManager entityManager, TYPE event);
}
diff --git a/core/src/main/java/org/fuin/cqrs4j/core/JpaView.java b/core/src/main/java/org/fuin/cqrs4j/core/JpaView.java
new file mode 100644
index 0000000..95e7616
--- /dev/null
+++ b/core/src/main/java/org/fuin/cqrs4j/core/JpaView.java
@@ -0,0 +1,40 @@
+package org.fuin.cqrs4j.core;
+
+import jakarta.persistence.EntityManager;
+import org.fuin.ddd4j.core.Event;
+
+import java.util.List;
+
+/**
+ * Defines a unit that projects events read from the event store into another representation.
+ * The view is updated regularly by using a scheduler and the result will be stored using JPA.
+ */
+public interface JpaView extends View {
+
+ /**
+ * Returns the CRON expression defining how often the view should be updated.
+ *
+ * @return Spring Quartz CRON expression
+ */
+ String getCron();
+
+
+ /**
+ * Number of events to read and handle in one transaction.
+ *
+ * @return Number of events (defaults to 100).
+ */
+ default int getChunkSize() {
+ return 100;
+ }
+
+
+ /**
+ * Events to handle by the view.
+ *
+ * @param em Entity manager to use.
+ * @param events Events used to update the view.
+ */
+ void handleEvents(EntityManager em, List events);
+
+}
diff --git a/src/main/java/org/fuin/cqrs4j/MultiCommandExecutor.java b/core/src/main/java/org/fuin/cqrs4j/core/MultiCommandExecutor.java
similarity index 92%
rename from src/main/java/org/fuin/cqrs4j/MultiCommandExecutor.java
rename to core/src/main/java/org/fuin/cqrs4j/core/MultiCommandExecutor.java
index de768eb..b843350 100644
--- a/src/main/java/org/fuin/cqrs4j/MultiCommandExecutor.java
+++ b/core/src/main/java/org/fuin/cqrs4j/core/MultiCommandExecutor.java
@@ -1,29 +1,29 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
-
-import java.util.List;
+package org.fuin.cqrs4j.core;
import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
/**
* Handles multiple commands by delegating the call to other executors.
- *
+ *
* @param
* Type of context for the command execution.
* @param
@@ -34,7 +34,7 @@ public final class MultiCommandExecutor extends AbstractMultiCo
/**
* Constructor with command handler array.
- *
+ *
* @param cmdExecutors
* Array of command executors.
*/
@@ -44,7 +44,7 @@ public MultiCommandExecutor(@NotEmpty final CommandExecutor... cmdExecutors) {
/**
* Constructor with mandatory data.
- *
+ *
* @param cmdExecutors
* List of command executors.
*/
diff --git a/src/main/java/org/fuin/cqrs4j/Result.java b/core/src/main/java/org/fuin/cqrs4j/core/Result.java
similarity index 86%
rename from src/main/java/org/fuin/cqrs4j/Result.java
rename to core/src/main/java/org/fuin/cqrs4j/core/Result.java
index ffc5828..b38f03c 100644
--- a/src/main/java/org/fuin/cqrs4j/Result.java
+++ b/core/src/main/java/org/fuin/cqrs4j/core/Result.java
@@ -1,31 +1,30 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.core;
+import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;
-import org.fuin.objects4j.common.Nullable;
-
/**
- * Result of a request. The type signals if the execution was successful or not. In case the the result is not {@link ResultType#OK}, the
+ * Result of a request. The type signals if the execution was successful or not. In case the result is not {@link ResultType#OK}, the
* fields code and message should contain unique information to help the user identifying the cause of the problem. A result may carry some
* optional data.
- *
+ *
* @param
* Type of data returned.
*/
@@ -33,7 +32,7 @@ public interface Result {
/**
* Returns the result type.
- *
+ *
* @return Type.
*/
@NotNull
@@ -41,7 +40,7 @@ public interface Result {
/**
* Returns the result code.
- *
+ *
* @return Code.
*/
@Nullable
@@ -49,7 +48,7 @@ public interface Result {
/**
* Returns the result message.
- *
+ *
* @return Message.
*/
@Nullable
@@ -57,7 +56,7 @@ public interface Result {
/**
* Returns the result data.
- *
+ *
* @return Optional data.
*/
@Nullable
diff --git a/src/main/java/org/fuin/cqrs4j/ResultType.java b/core/src/main/java/org/fuin/cqrs4j/core/ResultType.java
similarity index 89%
rename from src/main/java/org/fuin/cqrs4j/ResultType.java
rename to core/src/main/java/org/fuin/cqrs4j/core/ResultType.java
index 912d64b..a7a8a68 100644
--- a/src/main/java/org/fuin/cqrs4j/ResultType.java
+++ b/core/src/main/java/org/fuin/cqrs4j/core/ResultType.java
@@ -1,21 +1,21 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.core;
/**
* Type of the result.
diff --git a/src/main/java/org/fuin/cqrs4j/ToResultCapable.java b/core/src/main/java/org/fuin/cqrs4j/core/ToResultCapable.java
similarity index 87%
rename from src/main/java/org/fuin/cqrs4j/ToResultCapable.java
rename to core/src/main/java/org/fuin/cqrs4j/core/ToResultCapable.java
index eccfe3c..ee4dde1 100644
--- a/src/main/java/org/fuin/cqrs4j/ToResultCapable.java
+++ b/core/src/main/java/org/fuin/cqrs4j/core/ToResultCapable.java
@@ -1,35 +1,35 @@
-/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
- * http://www.fuin.org/
- *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option) any
- * later version.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library. If not, see http://www.gnu.org/licenses/.
- */
-package org.fuin.cqrs4j;
-
-/**
- * Marks an object that can be converted into a result.
- */
-public interface ToResultCapable {
-
- /**
- * Returns a result for the object.
- *
- * @return Object expressed as result.
- *
- * @param
- * Type of data.
- */
- public Result toResult();
-
-}
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.core;
+
+/**
+ * Marks an object that can be converted into a result.
+ */
+public interface ToResultCapable {
+
+ /**
+ * Returns a result for the object.
+ *
+ * @return Object expressed as result.
+ *
+ * @param
+ * Type of data.
+ */
+ public Result toResult();
+
+}
diff --git a/core/src/main/java/org/fuin/cqrs4j/core/UrlParamEntityIdPathNotEqualsCmdException.java b/core/src/main/java/org/fuin/cqrs4j/core/UrlParamEntityIdPathNotEqualsCmdException.java
new file mode 100644
index 0000000..be0130b
--- /dev/null
+++ b/core/src/main/java/org/fuin/cqrs4j/core/UrlParamEntityIdPathNotEqualsCmdException.java
@@ -0,0 +1,49 @@
+package org.fuin.cqrs4j.core;
+
+import org.fuin.ddd4j.core.EntityIdPath;
+
+/**
+ * The entity identifier path constructed from the URL does not match the one that is inside the received command.
+ *
+ * This can happen if URL for example contains the name of the aggregate, followed by an aggregate identifier.
+ * Example: POST /customer/f832a5a4-dd80-49df-856a-7274de82cd6b/create (Command send in the request body)
+ * The ID from the URL must match the aggregate ID that is passed via the command in the body.
+ */
+@SuppressWarnings("rawtypes")
+public class UrlParamEntityIdPathNotEqualsCmdException extends Exception {
+
+ private final EntityIdPath urlEntityIdPath;
+
+ private final AggregateCommand command;
+
+ /**
+ * Constructor with mandatory data.
+ *
+ * @param urlEntityIdPath Entity identifier path constructed from the URL.
+ * @param command Command with the entity identifier path that does not match the one from the URL.
+ */
+ public UrlParamEntityIdPathNotEqualsCmdException(EntityIdPath urlEntityIdPath, AggregateCommand command) {
+ super("Entity path constructed from URL parameters '" + urlEntityIdPath.asBaseType() + "' " +
+ "is not the same as command's entityPath: '" + command.getEntityIdPath().asBaseType() + "'");
+ this.urlEntityIdPath = urlEntityIdPath;
+ this.command = command;
+ }
+
+ /**
+ * Returns the entity identifier path constructed from the URL.
+ *
+ * @return Entity ID path from URL.
+ */
+ public EntityIdPath getUrlEntityIdPath() {
+ return urlEntityIdPath;
+ }
+
+ /**
+ * Returns the command with the entity identifier path that does not match the one from the URL.
+ *
+ * @return Command with mismatching entity identifier path.
+ */
+ public AggregateCommand getCommand() {
+ return command;
+ }
+}
diff --git a/core/src/main/java/org/fuin/cqrs4j/core/View.java b/core/src/main/java/org/fuin/cqrs4j/core/View.java
new file mode 100644
index 0000000..59c3220
--- /dev/null
+++ b/core/src/main/java/org/fuin/cqrs4j/core/View.java
@@ -0,0 +1,27 @@
+package org.fuin.cqrs4j.core;
+
+import org.fuin.ddd4j.core.EventType;
+
+import java.util.Set;
+
+/**
+ * Defines a unit that projects events into another representation.
+ */
+public interface View {
+
+ /**
+ * Unique name of the view.
+ *
+ * @return Name that is unique in this program instance.
+ */
+ String getName();
+
+ /**
+ * Returns the type of events the view is interested in.
+ *
+ * @return List of events.
+ */
+ Set getEventTypes();
+
+
+}
diff --git a/src/main/java/org/fuin/cqrs4j/package-info.java b/core/src/main/java/org/fuin/cqrs4j/core/package-info.java
similarity index 67%
rename from src/main/java/org/fuin/cqrs4j/package-info.java
rename to core/src/main/java/org/fuin/cqrs4j/core/package-info.java
index 439582b..79eef5a 100644
--- a/src/main/java/org/fuin/cqrs4j/package-info.java
+++ b/core/src/main/java/org/fuin/cqrs4j/core/package-info.java
@@ -1,29 +1,22 @@
-/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
- * http://www.fuin.org/
- *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option) any
- * later version.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library. If not, see http://www.gnu.org/licenses/.
- */
-@XmlJavaTypeAdapter(type = ZonedDateTime.class, value = ZonedDateTimeXmlAdapter.class)
-package org.fuin.cqrs4j;
-
-import java.time.ZonedDateTime;
-
-import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-import io.github.threetenjaxb.core.ZonedDateTimeXmlAdapter;
-
-/**
- * Command Query Responsibility Segregation base classes.
- */
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.core;
+
+/**
+ * Command Query Responsibility Segregation base classes.
+ */
diff --git a/core/src/test/java/org/fuin/cqrs4j/core/ArchitectureTest.java b/core/src/test/java/org/fuin/cqrs4j/core/ArchitectureTest.java
new file mode 100644
index 0000000..45bfc21
--- /dev/null
+++ b/core/src/test/java/org/fuin/cqrs4j/core/ArchitectureTest.java
@@ -0,0 +1,39 @@
+package org.fuin.cqrs4j.core;
+
+import com.tngtech.archunit.core.importer.ImportOption;
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import static com.tngtech.archunit.library.DependencyRules.NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
+
+/**
+ * Tests architectural aspects.
+ */
+@AnalyzeClasses(packagesOf = ArchitectureTest.class, importOptions = ImportOption.DoNotIncludeTests.class)
+public class ArchitectureTest {
+
+ private static final String THIS_PACKAGE = ArchitectureTest.class.getPackageName();
+
+ @ArchTest
+ static final ArchRule no_accesses_to_upper_package = NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
+
+ @ArchTest
+ static final ArchRule access_only_to_defined_packages = classes()
+ .that()
+ .resideInAPackage(THIS_PACKAGE)
+ .should()
+ .onlyDependOnClassesThat()
+ .resideInAnyPackage(THIS_PACKAGE, "java..",
+ "org.fuin.ddd4j.common..",
+ "org.fuin.ddd4j.core..",
+ "org.fuin.objects4j.common..",
+ "org.fuin.objects4j.core..",
+ "jakarta.validation.constraints..",
+ "jakarta.annotation..",
+ "org.slf4j..",
+ "javax.annotation.concurrent.."
+ );
+
+}
diff --git a/core/src/test/java/org/fuin/cqrs4j/core/BaseTest.java b/core/src/test/java/org/fuin/cqrs4j/core/BaseTest.java
new file mode 100644
index 0000000..8364809
--- /dev/null
+++ b/core/src/test/java/org/fuin/cqrs4j/core/BaseTest.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2013 Future Invent Informationsmanagement GmbH. All rights
+ * reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+package org.fuin.cqrs4j.core;
+
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import org.fuin.units4j.archunit.Units4JConditions;
+
+@AnalyzeClasses(packagesOf = BaseTest.class)
+class BaseTest {
+
+ @ArchTest
+ static final ArchRule all_classes_should_have_tests = Units4JConditions.ALL_CLASSES_SHOULD_HAVE_TESTS;
+
+}
+
diff --git a/core/src/test/java/org/fuin/cqrs4j/core/CommandExecutionFailedExceptionTest.java b/core/src/test/java/org/fuin/cqrs4j/core/CommandExecutionFailedExceptionTest.java
new file mode 100644
index 0000000..81efaa8
--- /dev/null
+++ b/core/src/test/java/org/fuin/cqrs4j/core/CommandExecutionFailedExceptionTest.java
@@ -0,0 +1,28 @@
+package org.fuin.cqrs4j.core;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for the {@link CommandExecutionFailedException} class.
+ */
+class CommandExecutionFailedExceptionTest {
+
+ @Test
+ void testCreate() {
+
+ // PREPARE
+ final IOException ex = new IOException("Whatever");
+
+ // TEST
+ CommandExecutionFailedException testee = new CommandExecutionFailedException(ex);
+
+ // VERIFY
+ assertThat(testee.getCause()).isEqualTo(ex);
+
+ }
+
+}
diff --git a/core/src/test/java/org/fuin/cqrs4j/core/CqrsUtilsTest.java b/core/src/test/java/org/fuin/cqrs4j/core/CqrsUtilsTest.java
new file mode 100644
index 0000000..4e243f6
--- /dev/null
+++ b/core/src/test/java/org/fuin/cqrs4j/core/CqrsUtilsTest.java
@@ -0,0 +1,26 @@
+package org.fuin.cqrs4j.core;
+
+import org.fuin.ddd4j.core.EventType;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for the {@link CqrsUtils} class.
+ */
+class CqrsUtilsTest {
+
+ @Test
+ void testCalculateAdler32Checksum() {
+
+ assertThat(CqrsUtils.calculateAdler32Checksum(List.of(new EventType("A"))))
+ .isEqualTo(4325442L);
+
+ assertThat(CqrsUtils.calculateAdler32Checksum(List.of(new EventType("A"), new EventType("B"))))
+ .isEqualTo(12976260L);
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/fuin/cqrs4j/MultiCommandExecutorTest.java b/core/src/test/java/org/fuin/cqrs4j/core/MultiCommandExecutorTest.java
similarity index 86%
rename from src/test/java/org/fuin/cqrs4j/MultiCommandExecutorTest.java
rename to core/src/test/java/org/fuin/cqrs4j/core/MultiCommandExecutorTest.java
index 091f728..eda2f47 100644
--- a/src/test/java/org/fuin/cqrs4j/MultiCommandExecutorTest.java
+++ b/core/src/test/java/org/fuin/cqrs4j/core/MultiCommandExecutorTest.java
@@ -1,202 +1,234 @@
-/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
- * http://www.fuin.org/
- *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option) any
- * later version.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library. If not, see http://www.gnu.org/licenses/.
- */
-package org.fuin.cqrs4j;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-
-import org.fuin.ddd4j.ddd.EventType;
-import org.fuin.objects4j.common.ConstraintViolationException;
-import org.junit.jupiter.api.Test;
-
-/**
- * Test for {@link MultiCommandExecutor}.
- */
-public class MultiCommandExecutorTest {
-
- @Test
- @SuppressWarnings("rawtypes")
- public final void testDispatch() throws Exception {
-
- // PREPARE
- final CountDownLatch done = new CountDownLatch(2);
- final CommandExecutor cmdHandler1 = new CommandExecutor() {
- @Override
- public final Set getCommandTypes() {
- final Set set = new HashSet<>();
- set.add(MyCommand.EVENT_TYPE);
- return set;
- }
-
- @Override
- public final Long execute(final MyContext ctx, final MyCommand cmd) {
- done.countDown();
- return done.getCount();
- }
-
- };
- final List list = new ArrayList<>();
- list.add(cmdHandler1);
- final MultiCommandExecutor testee = new MultiCommandExecutor<>(list);
- final MyContext ctx = new MyContext(InetAddress.getLocalHost());
-
- // TEST call twice
- testee.execute(ctx, new MyCommand());
- testee.execute(ctx, new MyCommand());
-
- // VERIFY
- assertThat(done.getCount()).isEqualTo(0);
-
- }
-
- @Test
- public final void testCreateNullArray() {
-
- try {
- new MultiCommandExecutor();
- fail();
- } catch (final ConstraintViolationException ex) {
- assertThat(ex.getMessage()).isEqualTo("The argument 'cmdExecutors' cannot be an empty list");
- }
-
- }
-
- @Test
- @SuppressWarnings("rawtypes")
- public final void testCreateNullList() {
-
- try {
- new MultiCommandExecutor((List) null);
- fail();
- } catch (final ConstraintViolationException ex) {
- assertThat(ex.getMessage()).isEqualTo("The argument 'cmdExecutors' cannot be null");
- }
-
- }
-
- @Test
- public final void testCreateEmptyArray() {
-
- try {
- new MultiCommandExecutor(new CommandExecutor[] {});
- fail();
- } catch (final ConstraintViolationException ex) {
- assertThat(ex.getMessage()).isEqualTo("The argument 'cmdExecutors' cannot be an empty list");
- }
-
- }
-
- @Test
- @SuppressWarnings("rawtypes")
- public final void testCreateEmptyList() {
-
- try {
- new MultiCommandExecutor(new ArrayList());
- fail();
- } catch (final ConstraintViolationException ex) {
- assertThat(ex.getMessage()).isEqualTo("The argument 'cmdExecutors' cannot be an empty list");
- }
-
- }
-
- @Test
- @SuppressWarnings("rawtypes")
- public final void testCreateDuplicate() {
-
- // PREPARE
- final CommandExecutor cmdHandler1 = new CommandExecutor() {
- @Override
- public final Set getCommandTypes() {
- final Set set = new HashSet<>();
- set.add(MyCommand.EVENT_TYPE);
- return set;
- }
-
- @Override
- public final Void execute(final MyContext ctx, final MyCommand event) {
- return null;
- }
- };
- final CommandExecutor cmdHandler2 = new CommandExecutor() {
- @Override
- public final Set getCommandTypes() {
- final Set set = new HashSet<>();
- set.add(MyCommand.EVENT_TYPE);
- return set;
- }
-
- @Override
- public final Void execute(final MyContext ctx, final MyCommand event) {
- return null;
- }
- };
- final List list = new ArrayList<>();
- list.add(cmdHandler1);
- list.add(cmdHandler2);
-
- // TEST
- try {
- new MultiCommandExecutor(list);
- fail();
- } catch (final ConstraintViolationException ex) {
- assertThat(ex.getMessage())
- .isEqualTo("The argument 'cmdExecutors' contains multiple executors for command: " + MyCommand.EVENT_TYPE);
- }
-
- }
-
- public static class MyCommand extends AbstractCommand {
-
- private static final long serialVersionUID = 1L;
-
- private static final EventType EVENT_TYPE = new EventType("MyCommand");
-
- public MyCommand() {
- super();
- }
-
- @Override
- public EventType getEventType() {
- return EVENT_TYPE;
- }
-
- }
-
- public static class MyContext {
-
- private InetAddress ipAddr;
-
- public MyContext(final InetAddress ipAddr) {
- super();
- this.ipAddr = ipAddr;
- }
-
- public InetAddress getIpAddr() {
- return ipAddr;
- }
-
- }
-
-}
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.core;
+
+import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import org.fuin.ddd4j.core.EventId;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.objects4j.common.ConstraintViolationException;
+import org.junit.jupiter.api.Test;
+
+import java.io.Serial;
+import java.net.InetAddress;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Test for {@link MultiCommandExecutor}.
+ */
+public class MultiCommandExecutorTest {
+
+ @Test
+ @SuppressWarnings("rawtypes")
+ public final void testDispatch() throws Exception {
+
+ // PREPARE
+ final CountDownLatch done = new CountDownLatch(2);
+ final CommandExecutor cmdHandler1 = new CommandExecutor() {
+ @Override
+ public final Set getCommandTypes() {
+ final Set set = new HashSet<>();
+ set.add(MyCommand.EVENT_TYPE);
+ return set;
+ }
+
+ @Override
+ public final Long execute(final MyContext ctx, final MyCommand cmd) {
+ done.countDown();
+ return done.getCount();
+ }
+
+ };
+ final List list = new ArrayList<>();
+ list.add(cmdHandler1);
+ final MultiCommandExecutor testee = new MultiCommandExecutor<>(list);
+ final MyContext ctx = new MyContext(InetAddress.getLocalHost());
+
+ // TEST call twice
+ testee.execute(ctx, new MyCommand());
+ testee.execute(ctx, new MyCommand());
+
+ // VERIFY
+ assertThat(done.getCount()).isEqualTo(0);
+
+ }
+
+ @Test
+ public final void testCreateNullArray() {
+
+ try {
+ new MultiCommandExecutor();
+ fail();
+ } catch (final ConstraintViolationException ex) {
+ assertThat(ex.getMessage()).isEqualTo("The argument 'cmdExecutors' cannot be an empty list");
+ }
+
+ }
+
+ @Test
+ @SuppressWarnings("rawtypes")
+ public final void testCreateNullList() {
+
+ try {
+ new MultiCommandExecutor((List) null);
+ fail();
+ } catch (final ConstraintViolationException ex) {
+ assertThat(ex.getMessage()).isEqualTo("The argument 'cmdExecutors' cannot be null");
+ }
+
+ }
+
+ @Test
+ public final void testCreateEmptyArray() {
+
+ try {
+ new MultiCommandExecutor();
+ fail();
+ } catch (final ConstraintViolationException ex) {
+ assertThat(ex.getMessage()).isEqualTo("The argument 'cmdExecutors' cannot be an empty list");
+ }
+
+ }
+
+ @Test
+ @SuppressWarnings("rawtypes")
+ public final void testCreateEmptyList() {
+
+ try {
+ new MultiCommandExecutor(new ArrayList());
+ fail();
+ } catch (final ConstraintViolationException ex) {
+ assertThat(ex.getMessage()).isEqualTo("The argument 'cmdExecutors' cannot be an empty list");
+ }
+
+ }
+
+ @Test
+ @SuppressWarnings("rawtypes")
+ public final void testCreateDuplicate() {
+
+ // PREPARE
+ final CommandExecutor cmdHandler1 = new CommandExecutor() {
+ @Override
+ public final Set getCommandTypes() {
+ final Set set = new HashSet<>();
+ set.add(MyCommand.EVENT_TYPE);
+ return set;
+ }
+
+ @Override
+ public final Void execute(final MyContext ctx, final MyCommand event) {
+ return null;
+ }
+ };
+ final CommandExecutor cmdHandler2 = new CommandExecutor() {
+ @Override
+ public final Set getCommandTypes() {
+ final Set set = new HashSet<>();
+ set.add(MyCommand.EVENT_TYPE);
+ return set;
+ }
+
+ @Override
+ public final Void execute(final MyContext ctx, final MyCommand event) {
+ return null;
+ }
+ };
+ final List list = new ArrayList<>();
+ list.add(cmdHandler1);
+ list.add(cmdHandler2);
+
+ // TEST
+ try {
+ new MultiCommandExecutor(list);
+ fail();
+ } catch (final ConstraintViolationException ex) {
+ assertThat(ex.getMessage())
+ .isEqualTo("The argument 'cmdExecutors' contains multiple executors for command: " + MyCommand.EVENT_TYPE);
+ }
+
+ }
+
+ public static class MyCommand implements Command {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private static final EventType EVENT_TYPE = new EventType("MyCommand");
+
+ private ZonedDateTime timestamp;
+
+ public MyCommand() {
+ super();
+ timestamp = ZonedDateTime.now();
+ }
+
+ @Override
+ public @NotNull EventId getEventId() {
+ return null;
+ }
+
+ @Override
+ public EventType getEventType() {
+ return EVENT_TYPE;
+ }
+
+ @Override
+ @NotNull
+ public ZonedDateTime getEventTimestamp() {
+ return timestamp;
+ }
+
+ @Nullable
+ @Override
+ public EventId getCorrelationId() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public EventId getCausationId() {
+ return null;
+ }
+
+ }
+
+ public static class MyContext {
+
+ private InetAddress ipAddr;
+
+ public MyContext(final InetAddress ipAddr) {
+ super();
+ this.ipAddr = ipAddr;
+ }
+
+ public InetAddress getIpAddr() {
+ return ipAddr;
+ }
+
+ }
+
+}
diff --git a/core/src/test/java/org/fuin/cqrs4j/core/UrlParamEntityIdPathNotEqualsCmdExceptionTest.java b/core/src/test/java/org/fuin/cqrs4j/core/UrlParamEntityIdPathNotEqualsCmdExceptionTest.java
new file mode 100644
index 0000000..135b10b
--- /dev/null
+++ b/core/src/test/java/org/fuin/cqrs4j/core/UrlParamEntityIdPathNotEqualsCmdExceptionTest.java
@@ -0,0 +1,37 @@
+package org.fuin.cqrs4j.core;
+
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.EntityIdPath;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test for the {@link UrlParamEntityIdPathNotEqualsCmdException} class.
+ */
+public class UrlParamEntityIdPathNotEqualsCmdExceptionTest {
+
+ @Test
+ void testCreate() {
+
+ // PREPARE
+ final EntityId entityId = Mockito.mock(EntityId.class);
+ final EntityIdPath urlEntityIdPath = new EntityIdPath(entityId);
+ final AggregateCommand command = Mockito.mock(AggregateCommand.class);
+
+ final EntityId cmdEntityId = Mockito.mock(EntityId.class);
+ final EntityIdPath cmdEntityIdPath = new EntityIdPath(cmdEntityId);
+ when(command.getEntityIdPath()).thenReturn(cmdEntityIdPath);
+
+ // TEST
+ final UrlParamEntityIdPathNotEqualsCmdException testee = new UrlParamEntityIdPathNotEqualsCmdException(urlEntityIdPath, command);
+
+ // VERIFY
+ assertThat(testee.getUrlEntityIdPath()).isEqualTo(urlEntityIdPath);
+ assertThat(testee.getCommand()).isEqualTo(command);
+
+ }
+
+}
diff --git a/cqrs-4-java.iml b/cqrs-4-java.iml
new file mode 100644
index 0000000..20baf01
--- /dev/null
+++ b/cqrs-4-java.iml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/esc/cqrs-4-java-esc.iml b/esc/cqrs-4-java-esc.iml
new file mode 100644
index 0000000..2ddb30b
--- /dev/null
+++ b/esc/cqrs-4-java-esc.iml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/esc/pom.xml b/esc/pom.xml
new file mode 100644
index 0000000..f776378
--- /dev/null
+++ b/esc/pom.xml
@@ -0,0 +1,189 @@
+
+
+
+ 4.0.0
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java
+ 0.6.0-SNAPSHOT
+ ../pom.xml
+
+
+ cqrs-4-java-esc
+ jar
+ ${description} (ESC)
+
+
+
+
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java-core
+
+
+
+ org.fuin.ddd4j
+ ddd-4-java-core
+
+
+
+ org.fuin.objects4j
+ objects4j-common
+
+
+
+ org.fuin.esc
+ esc-api
+
+
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+
+ jakarta.persistence
+ jakarta.persistence-api
+
+
+
+
+
+ org.fuin.ddd4j
+ ddd-4-java-jsonb
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+ org.fuin
+ units4j
+ test
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+ test
+
+
+
+ org.glassfish.expressly
+ expressly
+ test
+
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+ com.tngtech.archunit
+ archunit
+ test
+
+
+
+ com.tngtech.archunit
+ archunit-junit5
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ **/*
+
+
+
+ org.fuin.cqrs4j.esc
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jdeps-plugin
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ org.glassfish.expressly:expressly
+ org.hibernate.validator:hibernate-validator
+ com.tngtech.archunit:archunit-junit5
+ org.junit.jupiter:junit-jupiter
+
+
+ com.tngtech.archunit:archunit-junit5-api
+ org.junit.jupiter:junit-jupiter-api
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/fuin/cqrs4j/EventDispatcher.java b/esc/src/main/java/org/fuin/cqrs4j/esc/JpaEventDispatcher.java
similarity index 58%
rename from src/main/java/org/fuin/cqrs4j/EventDispatcher.java
rename to esc/src/main/java/org/fuin/cqrs4j/esc/JpaEventDispatcher.java
index 8e3edb4..de2d566 100644
--- a/src/main/java/org/fuin/cqrs4j/EventDispatcher.java
+++ b/esc/src/main/java/org/fuin/cqrs4j/esc/JpaEventDispatcher.java
@@ -1,39 +1,39 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
-
-import java.util.List;
-import java.util.Set;
+package org.fuin.cqrs4j.esc;
+import jakarta.persistence.EntityManager;
import jakarta.validation.constraints.NotNull;
-
-import org.fuin.ddd4j.ddd.Event;
-import org.fuin.ddd4j.ddd.EventType;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventType;
import org.fuin.esc.api.CommonEvent;
+import java.util.List;
+import java.util.Set;
+
/**
- * Registry with all event handlers.
+ * Registry with all JPA event handlers.
*/
-public interface EventDispatcher {
+public interface JpaEventDispatcher {
/**
* Returns a set of all known types.
- *
+ *
* @return All known event types.
*/
@NotNull
@@ -41,27 +41,27 @@ public interface EventDispatcher {
/**
* Dispatch all common events to the appropriate event handler.
- *
- * @param commonEvents
- * Events to dispatch.
+ *
+ * @param entityManager Entity manager to use.
+ * @param commonEvents Events to dispatch.
*/
- public void dispatchCommonEvents(@NotNull List commonEvents);
+ public void dispatchCommonEvents(@NotNull EntityManager entityManager, @NotNull List commonEvents);
/**
* Dispatch all events to the appropriate event handler.
- *
- * @param events
- * Events to dispatch.
+ *
+ * @param entityManager Entity manager to use.
+ * @param events Events to dispatch.
*/
- public void dispatchEvents(@NotNull List events);
+ public void dispatchEvents(@NotNull EntityManager entityManager, @NotNull List events);
/**
* Dispatches the given event to the appropriate event handler. The event is ignored if no event handler can be found that is capable of
* handling it.
- *
- * @param event
- * Event to dispatch.
+ *
+ * @param entityManager Entity manager to use.
+ * @param event Event to dispatch.
*/
- public void dispatchEvent(@NotNull Event event);
+ public void dispatchEvent(@NotNull EntityManager entityManager, @NotNull Event event);
}
diff --git a/src/main/java/org/fuin/cqrs4j/ProjectionService.java b/esc/src/main/java/org/fuin/cqrs4j/esc/ProjectionService.java
similarity index 92%
rename from src/main/java/org/fuin/cqrs4j/ProjectionService.java
rename to esc/src/main/java/org/fuin/cqrs4j/esc/ProjectionService.java
index 1877902..1ec6dff 100644
--- a/src/main/java/org/fuin/cqrs4j/ProjectionService.java
+++ b/esc/src/main/java/org/fuin/cqrs4j/esc/ProjectionService.java
@@ -1,24 +1,23 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.esc;
import jakarta.validation.constraints.NotNull;
-
import org.fuin.esc.api.StreamId;
/**
@@ -28,19 +27,19 @@ public interface ProjectionService {
/**
* Sets the stored position of the projection to the start position.
- *
+ *
* @param streamId
* Unique ID of the stream.
- *
+ *
*/
public void resetProjectionPosition(@NotNull final StreamId streamId);
/**
* Reads the position that was read last time.
- *
+ *
* @param streamId
* Unique ID of the stream.
- *
+ *
* @return Number of the next event to read.
*/
@NotNull
@@ -48,7 +47,7 @@ public interface ProjectionService {
/**
* Updates the position to read next time.
- *
+ *
* @param streamId
* Unique ID of the stream.
* @param nextEventNumber
diff --git a/src/main/java/org/fuin/cqrs4j/SimpleEventDispatcher.java b/esc/src/main/java/org/fuin/cqrs4j/esc/SimpleJpaEventDispatcher.java
similarity index 54%
rename from src/main/java/org/fuin/cqrs4j/SimpleEventDispatcher.java
rename to esc/src/main/java/org/fuin/cqrs4j/esc/SimpleJpaEventDispatcher.java
index 0f97828..86efe07 100644
--- a/src/main/java/org/fuin/cqrs4j/SimpleEventDispatcher.java
+++ b/esc/src/main/java/org/fuin/cqrs4j/esc/SimpleJpaEventDispatcher.java
@@ -1,21 +1,29 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.esc;
+
+import jakarta.persistence.EntityManager;
+import jakarta.validation.constraints.NotNull;
+import org.fuin.cqrs4j.core.JpaEventHandler;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.esc.api.CommonEvent;
+import org.fuin.objects4j.common.Contract;
import java.util.ArrayList;
import java.util.Arrays;
@@ -24,53 +32,46 @@
import java.util.Map;
import java.util.Set;
-import jakarta.validation.constraints.NotNull;
-
-import org.fuin.ddd4j.ddd.Event;
-import org.fuin.ddd4j.ddd.EventType;
-import org.fuin.esc.api.CommonEvent;
-import org.fuin.objects4j.common.Contract;
-
/**
- * Registry with all event handlers.
+ * Registry with all JPA event handlers.
*/
-public final class SimpleEventDispatcher implements EventDispatcher {
+public final class SimpleJpaEventDispatcher implements JpaEventDispatcher {
@SuppressWarnings("rawtypes")
- private final Map> eventHandlers;
+ private final Map> eventHandlers;
/**
* Constructor with array of event handlers.
- *
- * @param eventHandlers
+ *
+ * @param jpaEventHandlers
* Event handlers.
*/
@SuppressWarnings("rawtypes")
- public SimpleEventDispatcher(@NotNull final EventHandler... eventHandlers) {
- this(Arrays.asList(eventHandlers));
+ public SimpleJpaEventDispatcher(@NotNull final JpaEventHandler... jpaEventHandlers) {
+ this(Arrays.asList(jpaEventHandlers));
}
/**
* Constructor with list of event handlers.
- *
- * @param eventHandlers
+ *
+ * @param jpaEventHandlers
* Event handlers.
*/
@SuppressWarnings("rawtypes")
- public SimpleEventDispatcher(@NotNull final List eventHandlers) {
+ public SimpleJpaEventDispatcher(@NotNull final List jpaEventHandlers) {
super();
- Contract.requireArgNotNull("eventHandlers", eventHandlers);
- if (eventHandlers.isEmpty()) {
+ Contract.requireArgNotNull("eventHandlers", jpaEventHandlers);
+ if (jpaEventHandlers.isEmpty()) {
throw new IllegalArgumentException("The argument 'eventHandlers' cannot be an empty list");
}
this.eventHandlers = new HashMap<>();
- for (final EventHandler eventHandler : eventHandlers) {
- List handlers = this.eventHandlers.get(eventHandler.getEventType());
+ for (final JpaEventHandler jpaEventHandler : jpaEventHandlers) {
+ List handlers = this.eventHandlers.get(jpaEventHandler.getEventType());
if (handlers == null) {
handlers = new ArrayList<>();
- this.eventHandlers.put(eventHandler.getEventType(), handlers);
+ this.eventHandlers.put(jpaEventHandler.getEventType(), handlers);
}
- handlers.add(eventHandler);
+ handlers.add(jpaEventHandler);
}
}
@@ -81,36 +82,36 @@ public final Set getAllTypes() {
}
@Override
- public final void dispatchCommonEvents(@NotNull final List commonEvents) {
+ public final void dispatchCommonEvents(@NotNull EntityManager em, @NotNull final List commonEvents) {
Contract.requireArgNotNull("commonEvents", commonEvents);
for (final CommonEvent commonEvent : commonEvents) {
final Event event = (Event) commonEvent.getData();
- dispatchEvent(event);
+ dispatchEvent(em, event);
}
}
@Override
- public final void dispatchEvents(@NotNull final List events) {
+ public final void dispatchEvents(@NotNull EntityManager em, @NotNull final List events) {
Contract.requireArgNotNull("events", events);
for (final Event event : events) {
- dispatchEvent(event);
+ dispatchEvent(em, event);
}
}
- @SuppressWarnings({ "rawtypes", "unchecked" })
+ @SuppressWarnings({"rawtypes", "unchecked"})
@Override
- public final void dispatchEvent(@NotNull final Event event) {
+ public final void dispatchEvent(@NotNull EntityManager em, @NotNull final Event event) {
Contract.requireArgNotNull("event", event);
- final List handlers = eventHandlers.get(event.getEventType());
+ final List handlers = eventHandlers.get(event.getEventType());
if (handlers != null) {
- for (final EventHandler handler : handlers) {
- handler.handle(event);
+ for (final JpaEventHandler handler : handlers) {
+ handler.handle(em, event);
}
}
}
diff --git a/esc/src/test/java/org/fuin/cqrs4j/esc/ArchitectureTest.java b/esc/src/test/java/org/fuin/cqrs4j/esc/ArchitectureTest.java
new file mode 100644
index 0000000..99682da
--- /dev/null
+++ b/esc/src/test/java/org/fuin/cqrs4j/esc/ArchitectureTest.java
@@ -0,0 +1,43 @@
+package org.fuin.cqrs4j.esc;
+
+import com.tngtech.archunit.core.importer.ImportOption;
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import org.fuin.cqrs4j.core.Command;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import static com.tngtech.archunit.library.DependencyRules.NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
+
+/**
+ * Tests architectural aspects.
+ */
+@AnalyzeClasses(packagesOf = ArchitectureTest.class, importOptions = ImportOption.DoNotIncludeTests.class)
+public class ArchitectureTest {
+
+ private static final String THIS_PACKAGE = ArchitectureTest.class.getPackageName();
+
+ private static final String CORE_PACKAGE = Command.class.getPackageName();
+
+ @ArchTest
+ static final ArchRule no_accesses_to_upper_package = NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
+
+ @ArchTest
+ static final ArchRule access_only_to_defined_packages = classes()
+ .that()
+ .resideInAPackage(THIS_PACKAGE)
+ .should()
+ .onlyDependOnClassesThat()
+ .resideInAnyPackage(THIS_PACKAGE, CORE_PACKAGE, "java..",
+ "org.fuin.ddd4j.common..",
+ "org.fuin.ddd4j.core..",
+ "org.fuin.ddd4j.esc..",
+ "org.fuin.esc.api..",
+ "org.fuin.objects4j.common..",
+ "org.fuin.objects4j.core..",
+ "jakarta.validation.constraints..",
+ "org.slf4j..",
+ "javax.annotation.concurrent.."
+ );
+
+}
diff --git a/esc/src/test/java/org/fuin/cqrs4j/esc/BaseTest.java b/esc/src/test/java/org/fuin/cqrs4j/esc/BaseTest.java
new file mode 100644
index 0000000..69bff94
--- /dev/null
+++ b/esc/src/test/java/org/fuin/cqrs4j/esc/BaseTest.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2013 Future Invent Informationsmanagement GmbH. All rights
+ * reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+package org.fuin.cqrs4j.esc;
+
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import org.fuin.units4j.archunit.Units4JConditions;
+
+@AnalyzeClasses(packagesOf = BaseTest.class)
+class BaseTest {
+
+ @ArchTest
+ static final ArchRule all_classes_should_have_tests = Units4JConditions.ALL_CLASSES_SHOULD_HAVE_TESTS;
+
+}
+
diff --git a/src/test/java/org/fuin/cqrs4j/SimpleEventDispatcherTest.java b/esc/src/test/java/org/fuin/cqrs4j/esc/SimpleJpaEventDispatcherTest.java
similarity index 66%
rename from src/test/java/org/fuin/cqrs4j/SimpleEventDispatcherTest.java
rename to esc/src/test/java/org/fuin/cqrs4j/esc/SimpleJpaEventDispatcherTest.java
index c163931..bb6e935 100644
--- a/src/test/java/org/fuin/cqrs4j/SimpleEventDispatcherTest.java
+++ b/esc/src/test/java/org/fuin/cqrs4j/esc/SimpleJpaEventDispatcherTest.java
@@ -1,38 +1,41 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.esc;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.fuin.ddd4j.ddd.AbstractEvent;
-import org.fuin.ddd4j.ddd.Event;
-import org.fuin.ddd4j.ddd.EventType;
+import jakarta.persistence.EntityManager;
+import org.fuin.cqrs4j.core.JpaEventHandler;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.ddd4j.jsonb.AbstractEvent;
import org.fuin.esc.api.CommonEvent;
import org.fuin.esc.api.EventId;
import org.fuin.esc.api.SimpleCommonEvent;
import org.fuin.esc.api.TypeName;
import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.io.Serial;
+import java.util.ArrayList;
+import java.util.List;
-//CHECKSTYLE:OFF
-public final class SimpleEventDispatcherTest {
+import static org.assertj.core.api.Assertions.assertThat;
+
+public final class SimpleJpaEventDispatcherTest {
private static final EventType EVENT_TYPE_A = new EventType("EventA");
@@ -42,9 +45,9 @@ public final class SimpleEventDispatcherTest {
public final void testDispatchEvents() {
// PREPARE
- final CollectingEventHandler handlerA = new CollectingEventHandler<>(EVENT_TYPE_A);
- final CollectingEventHandler handlerB = new CollectingEventHandler<>(EVENT_TYPE_B);
- final EventDispatcher testee = new SimpleEventDispatcher(handlerA, handlerB);
+ final CollectingJpaEventHandler handlerA = new CollectingJpaEventHandler<>(EVENT_TYPE_A);
+ final CollectingJpaEventHandler handlerB = new CollectingJpaEventHandler<>(EVENT_TYPE_B);
+ final JpaEventDispatcher testee = new SimpleJpaEventDispatcher(handlerA, handlerB);
final List events = new ArrayList<>();
final EventA a1 = new EventA();
@@ -58,8 +61,10 @@ public final void testDispatchEvents() {
final EventB b2 = new EventB();
events.add(b2);
+ final EntityManager em = Mockito.mock(EntityManager.class);
+
// TEST
- testee.dispatchEvents(events);
+ testee.dispatchEvents(em, events);
// VERIFY
assertThat(handlerA.getEvents()).containsExactly(a1, a2, a3);
@@ -71,9 +76,9 @@ public final void testDispatchEvents() {
public final void testDispatchCommonEvents() {
// PREPARE
- final CollectingEventHandler handlerA = new CollectingEventHandler<>(EVENT_TYPE_A);
- final CollectingEventHandler handlerB = new CollectingEventHandler<>(EVENT_TYPE_B);
- final EventDispatcher testee = new SimpleEventDispatcher(handlerA, handlerB);
+ final CollectingJpaEventHandler handlerA = new CollectingJpaEventHandler<>(EVENT_TYPE_A);
+ final CollectingJpaEventHandler handlerB = new CollectingJpaEventHandler<>(EVENT_TYPE_B);
+ final JpaEventDispatcher testee = new SimpleJpaEventDispatcher(handlerA, handlerB);
final List events = new ArrayList<>();
final EventA a1 = new EventA();
@@ -87,8 +92,10 @@ public final void testDispatchCommonEvents() {
final EventB b2 = new EventB();
events.add(asCommonEvent(b2));
+ final EntityManager em = Mockito.mock(EntityManager.class);
+
// TEST
- testee.dispatchCommonEvents(events);
+ testee.dispatchCommonEvents(em, events);
// VERIFY
assertThat(handlerA.getEvents()).containsExactly(a1, a2, a3);
@@ -100,9 +107,9 @@ public final void testDispatchCommonEvents() {
public final void testGetAllTypes() {
// PREPARE
- final CollectingEventHandler handlerA = new CollectingEventHandler<>(EVENT_TYPE_A);
- final CollectingEventHandler handlerB = new CollectingEventHandler<>(EVENT_TYPE_B);
- final EventDispatcher testee = new SimpleEventDispatcher(handlerA, handlerB);
+ final CollectingJpaEventHandler handlerA = new CollectingJpaEventHandler<>(EVENT_TYPE_A);
+ final CollectingJpaEventHandler handlerB = new CollectingJpaEventHandler<>(EVENT_TYPE_B);
+ final JpaEventDispatcher testee = new SimpleJpaEventDispatcher(handlerA, handlerB);
final List typeList = new ArrayList<>();
typeList.add(handlerA.getEventType());
@@ -117,10 +124,10 @@ public final void testGetAllTypes() {
public final void testMultipleEventHandlersForOneEvent() {
// PREPARE
- final CollectingEventHandler handlerA1 = new CollectingEventHandler<>(EVENT_TYPE_A);
- final CollectingEventHandler handlerA2 = new CollectingEventHandler<>(EVENT_TYPE_A);
- final CollectingEventHandler handlerB = new CollectingEventHandler<>(EVENT_TYPE_B);
- final EventDispatcher testee = new SimpleEventDispatcher(handlerA1, handlerA2, handlerB);
+ final CollectingJpaEventHandler handlerA1 = new CollectingJpaEventHandler<>(EVENT_TYPE_A);
+ final CollectingJpaEventHandler handlerA2 = new CollectingJpaEventHandler<>(EVENT_TYPE_A);
+ final CollectingJpaEventHandler handlerB = new CollectingJpaEventHandler<>(EVENT_TYPE_B);
+ final JpaEventDispatcher testee = new SimpleJpaEventDispatcher(handlerA1, handlerA2, handlerB);
final List events = new ArrayList<>();
final EventA a1 = new EventA();
@@ -134,8 +141,10 @@ public final void testMultipleEventHandlersForOneEvent() {
final EventB b2 = new EventB();
events.add(b2);
+ final EntityManager em = Mockito.mock(EntityManager.class);
+
// TEST
- testee.dispatchEvents(events);
+ testee.dispatchEvents(em, events);
// VERIFY
assertThat(handlerA1.getEvents()).containsExactly(a1, a2, a3);
@@ -152,6 +161,7 @@ private static CommonEvent asCommonEvent(final Event event) {
private static class EventA extends AbstractEvent {
+ @Serial
private static final long serialVersionUID = 1L;
@Override
@@ -163,6 +173,7 @@ public EventType getEventType() {
private static class EventB extends AbstractEvent {
+ @Serial
private static final long serialVersionUID = 1L;
@Override
@@ -172,14 +183,14 @@ public EventType getEventType() {
}
- @SuppressWarnings({ "unused" })
- private static class CollectingEventHandler implements EventHandler {
+ @SuppressWarnings({"unused"})
+ private static class CollectingJpaEventHandler implements JpaEventHandler {
private EventType type;
private List events;
- public CollectingEventHandler(EventType type) {
+ public CollectingJpaEventHandler(EventType type) {
super();
this.type = type;
this.events = new ArrayList();
@@ -191,15 +202,10 @@ public EventType getEventType() {
}
@Override
- public void handle(TYPE event) {
+ public void handle(EntityManager em, TYPE event) {
events.add(event);
}
- @SuppressWarnings("unused")
- public EventType getType() {
- return type;
- }
-
public List getEvents() {
return events;
}
@@ -207,4 +213,3 @@ public List getEvents() {
}
}
-// CHECKSTYLE:ON
diff --git a/jackson/pom.xml b/jackson/pom.xml
new file mode 100644
index 0000000..e07af4c
--- /dev/null
+++ b/jackson/pom.xml
@@ -0,0 +1,238 @@
+
+
+
+ 4.0.0
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java
+ 0.6.0-SNAPSHOT
+ ../pom.xml
+
+
+ cqrs-4-java-jackson
+ jar
+ ${description} (JACKSON)
+
+
+
+
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java-core
+
+
+
+ org.fuin.ddd4j
+ ddd-4-java-core
+
+
+
+ org.fuin.ddd4j
+ ddd-4-java-jackson
+
+
+
+ org.fuin
+ utils4j
+
+
+
+ org.fuin.objects4j
+ objects4j-common
+
+
+
+ org.fuin.objects4j
+ objects4j-jackson
+
+
+
+ org.fuin.objects4j
+ objects4j-ui
+
+
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+
+ jakarta.annotation
+ jakarta.annotation-api
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+ org.fuin
+ units4j
+ test
+
+
+
+ org.fuin.esc
+ esc-api
+ test
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+ test
+
+
+
+ org.glassfish.expressly
+ expressly
+ test
+
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+ nl.jqno.equalsverifier
+ equalsverifier
+ test
+
+
+
+ com.tngtech.archunit
+ archunit
+ test
+
+
+
+ com.tngtech.archunit
+ archunit-junit5
+ test
+
+
+
+ net.javacrumbs.json-unit
+ json-unit-fluent
+ test
+
+
+
+ com.google.code.gson
+ gson
+ test
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ **/*
+
+
+
+ org.fuin.cqrs4j.jackson
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jdeps-plugin
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ jakarta.el:jakarta.el-api
+ org.glassfish.expressly:expressly
+ org.hibernate.validator:hibernate-validator
+ com.tngtech.archunit:archunit-junit5
+ org.junit.jupiter:junit-jupiter
+ com.google.code.gson:gson
+
+
+ com.tngtech.archunit:archunit-junit5-api
+ org.junit.jupiter:junit-jupiter-api
+
+
+
+
+
+
+
+
+
diff --git a/jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractAggregateCommand.java b/jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractAggregateCommand.java
new file mode 100644
index 0000000..27ce6f1
--- /dev/null
+++ b/jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractAggregateCommand.java
@@ -0,0 +1,260 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import org.fuin.cqrs4j.core.AggregateCommand;
+import org.fuin.ddd4j.core.AggregateRootId;
+import org.fuin.ddd4j.core.AggregateVersion;
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.EntityIdPath;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventId;
+import org.fuin.objects4j.common.Contract;
+
+import java.io.Serial;
+
+/**
+ * Base class for all commands that are directed to an existing aggregate.
+ *
+ * @param
+ * Type of the aggregate root identifier.
+ * @param
+ * Type of the identifier (the last one in the path).
+ */
+public abstract class AbstractAggregateCommand extends AbstractCommand
+ implements AggregateCommand {
+
+ @Serial
+ private static final long serialVersionUID = 1000L;
+
+ @NotNull
+ @JsonProperty("entity-id-path")
+ private EntityIdPath entityIdPath;
+
+ @Nullable
+ @JsonProperty("aggregate-version")
+ private AggregateVersion aggregateVersion;
+
+ /**
+ * Default constructor for JAXB.
+ */
+ protected AbstractAggregateCommand() { // NOSONAR Ignore uninitialized fields
+ super();
+ }
+
+ /**
+ * Constructor with aggregate root id and version.
+ *
+ * @param aggregateRootId
+ * Aggregate root identifier.
+ * @param aggregateVersion
+ * Expected aggregate version.
+ */
+ public AbstractAggregateCommand(@NotNull final AggregateRootId aggregateRootId, @Nullable final AggregateVersion aggregateVersion) {
+ this(new EntityIdPath(aggregateRootId), aggregateVersion);
+ }
+
+ /**
+ * Constructor with entitiy id path and version.
+ *
+ * @param entityIdPath
+ * Path from root aggregate to target entity.
+ * @param aggregateVersion
+ * Expected aggregate version.
+ */
+ public AbstractAggregateCommand(@NotNull final EntityIdPath entityIdPath, @Nullable final AggregateVersion aggregateVersion) {
+ super();
+ Contract.requireArgNotNull("entityIdPath", entityIdPath);
+ this.entityIdPath = entityIdPath;
+ this.aggregateVersion = aggregateVersion;
+ }
+
+ /**
+ * Constructor with event this one responds to. Convenience method to set the correlation and causation identifiers correctly.
+ *
+ * @param entityIdPath
+ * Path from root aggregate to target entity.
+ * @param aggregateVersion
+ * Expected aggregate version.
+ * @param respondTo
+ * Causing event.
+ */
+ public AbstractAggregateCommand(@NotNull final EntityIdPath entityIdPath, @Nullable final AggregateVersion aggregateVersion,
+ @NotNull final Event respondTo) {
+ super(respondTo);
+ Contract.requireArgNotNull("entityIdPath", entityIdPath);
+ this.entityIdPath = entityIdPath;
+ this.aggregateVersion = aggregateVersion;
+ }
+
+ /**
+ * Constructor with optional data.
+ *
+ * @param entityIdPath
+ * Path from root aggregate to target entity.
+ * @param aggregateVersion
+ * Expected aggregate version.
+ * @param correlationId
+ * Correlation ID.
+ * @param causationId
+ * ID of the event that caused this one.
+ */
+ public AbstractAggregateCommand(@NotNull final EntityIdPath entityIdPath, @Nullable final AggregateVersion aggregateVersion,
+ @Nullable final EventId correlationId, @Nullable final EventId causationId) {
+ super(correlationId, causationId);
+ Contract.requireArgNotNull("entityIdPath", entityIdPath);
+ this.entityIdPath = entityIdPath;
+ this.aggregateVersion = aggregateVersion;
+ }
+
+ @Override
+ @NotNull
+ @JsonIgnore
+ public final EntityIdPath getEntityIdPath() {
+ return entityIdPath;
+ }
+
+ @Override
+ @NotNull
+ @JsonIgnore
+ public final ENTITY_ID getEntityId() {
+ return entityIdPath.last();
+ }
+
+ @Override
+ @NotNull
+ @JsonIgnore
+ public final ROOT_ID getAggregateRootId() {
+ return entityIdPath.first();
+ }
+
+ @Override
+ @Nullable
+ @JsonIgnore
+ public final AggregateVersion getAggregateVersion() {
+ return aggregateVersion;
+ }
+
+ @Override
+ @Nullable
+ @JsonIgnore
+ public final Integer getAggregateVersionInteger() {
+ if (aggregateVersion == null) {
+ return null;
+ }
+ return aggregateVersion.asBaseType();
+ }
+
+ /**
+ * Base class for event builders.
+ *
+ * @param
+ * Type of the aggregate identifier.
+ * @param
+ * Type of the entity identifier.
+ * @param
+ * Type of the event.
+ * @param
+ * Type of the builder.
+ */
+ protected abstract static class Builder, BUILDER extends AbstractCommand.Builder>
+ extends AbstractCommand.Builder {
+
+ private AbstractAggregateCommand delegate;
+
+ /**
+ * Constructor with event.
+ *
+ * @param delegate
+ * Event to populate with data.
+ */
+ public Builder(final TYPE delegate) {
+ super(delegate);
+ this.delegate = delegate;
+ }
+
+ /**
+ * Sets the identifier path from aggregate root to the entity that emitted the event.
+ *
+ * @param entityIdPath
+ * Path of entity identifiers.
+ *
+ * @return This builder.
+ */
+ @SuppressWarnings("unchecked")
+ public final BUILDER entityIdPath(@NotNull final EntityIdPath entityIdPath) {
+ Contract.requireArgNotNull("entityIdPath", entityIdPath);
+ delegate.entityIdPath = entityIdPath;
+ return (BUILDER) this;
+ }
+
+ /**
+ * Convenience method to set the entity identifier path if the path has only the aggregate root identifier.
+ *
+ * @param id
+ * Aggregate root identifier that will be used to create the entity id path.
+ *
+ * @return This builder.
+ */
+ @SuppressWarnings("unchecked")
+ public final BUILDER entityIdPath(@NotNull AggregateRootId id) {
+ Contract.requireArgNotNull("id", id);
+ delegate.entityIdPath = new EntityIdPath(id);
+ return (BUILDER) this;
+ }
+
+ /**
+ * Sets the expected aggregate version.
+ *
+ * @param aggregateVersion
+ * Expected aggregate version.
+ *
+ * @return This builder.
+ */
+ @SuppressWarnings("unchecked")
+ public final BUILDER aggregateVersion(@Nullable final AggregateVersion aggregateVersion) {
+ delegate.aggregateVersion = aggregateVersion;
+ return (BUILDER) this;
+ }
+
+ /**
+ * Ensures that everything is set up for building the object or throws a runtime exception otherwise.
+ */
+ protected final void ensureBuildableAbstractAggregateCommand() {
+ ensureBuildableAbstractCommand();
+ ensureNotNull("entityIdPath", delegate.entityIdPath);
+ }
+
+ /**
+ * Sets the internal instance to a new one. This must be called within the build method.
+ *
+ * @param delegate
+ * Delegate to use.
+ */
+ protected final void resetAbstractAggregateCommand(final TYPE delegate) {
+ resetAbstractCommand(delegate);
+ this.delegate = delegate;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/fuin/cqrs4j/AbstractCommand.java b/jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractCommand.java
similarity index 83%
rename from src/main/java/org/fuin/cqrs4j/AbstractCommand.java
rename to jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractCommand.java
index ff3aca8..f42ec86 100644
--- a/src/main/java/org/fuin/cqrs4j/AbstractCommand.java
+++ b/jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractCommand.java
@@ -1,35 +1,38 @@
/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
- *
+ *
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.jackson;
+import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;
+import org.fuin.cqrs4j.core.Command;
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventId;
+import org.fuin.ddd4j.jackson.AbstractEvent;
-import org.fuin.ddd4j.ddd.AbstractEvent;
-import org.fuin.ddd4j.ddd.EntityId;
-import org.fuin.ddd4j.ddd.Event;
-import org.fuin.ddd4j.ddd.EventId;
-import org.fuin.objects4j.common.Nullable;
+import java.io.Serial;
/**
* Base class for all commands.
*/
public abstract class AbstractCommand extends AbstractEvent implements Command {
+ @Serial
private static final long serialVersionUID = 1000L;
/**
@@ -41,7 +44,7 @@ public AbstractCommand() {
/**
* Constructor with event this one responds to. Convenience method to set the correlation and causation identifiers correctly.
- *
+ *
* @param respondTo
* Causing event.
*/
@@ -51,7 +54,7 @@ public AbstractCommand(@NotNull final Event respondTo) {
/**
* Constructor with optional data.
- *
+ *
* @param correlationId
* Correlation ID.
* @param causationId
@@ -63,7 +66,7 @@ public AbstractCommand(@Nullable final EventId correlationId, @Nullable final Ev
/**
* Base class for event builders.
- *
+ *
* @param
* Type of the entity identifier.
* @param
@@ -74,21 +77,18 @@ public AbstractCommand(@Nullable final EventId correlationId, @Nullable final Ev
protected abstract static class Builder>
extends AbstractEvent.Builder {
- private AbstractCommand delegate;
-
/**
* Constructor with event.
- *
+ *
* @param delegate
* Event to populate with data.
*/
public Builder(final TYPE delegate) {
super(delegate);
- this.delegate = delegate;
}
/**
- * Ensures that everything is setup for building the object or throws a runtime exception otherwise.
+ * Ensures that everything is set up for building the object or throws a runtime exception otherwise.
*/
protected final void ensureBuildableAbstractCommand() {
ensureBuildableAbstractEvent();
@@ -96,13 +96,12 @@ protected final void ensureBuildableAbstractCommand() {
/**
* Sets the internal instance to a new one. This must be called within the build method.
- *
+ *
* @param delegate
* Delegate to use.
*/
protected final void resetAbstractCommand(final TYPE delegate) {
resetAbstractEvent(delegate);
- this.delegate = delegate;
}
}
diff --git a/jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractResult.java b/jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractResult.java
new file mode 100644
index 0000000..2fb4477
--- /dev/null
+++ b/jackson/src/main/java/org/fuin/cqrs4j/jackson/AbstractResult.java
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import org.fuin.cqrs4j.core.Result;
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.objects4j.common.Contract;
+import org.fuin.objects4j.common.ExceptionShortIdentifable;
+import org.fuin.objects4j.ui.Label;
+import org.fuin.objects4j.ui.Prompt;
+import org.fuin.objects4j.ui.ShortLabel;
+import org.fuin.objects4j.ui.Tooltip;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Result of a request. The type signals if the execution was successful or not. In case the result is not {@link ResultType#OK}, the
+ * fields code and message should contain unique information to help the user identifying the cause of the problem. A result may carry some
+ * optional data.
+ *
+ * @param
+ * Type of data returned.
+ */
+public abstract class AbstractResult implements Result, Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1000L;
+
+ static final String TYPE_PROPERTY = "type";
+
+ static final String CODE_PROPERTY = "code";
+
+ static final String MESSAGE_PROPERTY = "message";
+
+ @Label("Result Type")
+ @ShortLabel("TYPE")
+ @Tooltip("Type of the result")
+ @Prompt("ERROR")
+ @NotNull
+ @JsonProperty(TYPE_PROPERTY)
+ private ResultType type;
+
+ @Label("Result Code")
+ @ShortLabel("CODE")
+ @Tooltip("Code that uniquely identifies the result. Mostly used in case of warnings or errors.")
+ @Prompt("E00001")
+ @Nullable
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ @JsonProperty(CODE_PROPERTY)
+ private String code;
+
+ @Label("Result Message")
+ @ShortLabel("MSG")
+ @Tooltip("Message that describes the result. Mostly used in case of warnings or errors.")
+ @Prompt("The field 'Xyz' is mandatory")
+ @Nullable
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ @JsonProperty(MESSAGE_PROPERTY)
+ private String message;
+
+ /**
+ * Protected default constructor for de-serialization.
+ */
+ protected AbstractResult() { // NOSONAR Ignore uninitialized fields
+ super();
+ }
+
+ /**
+ * Constructor with all data.
+ *
+ * @param type
+ * Type.
+ * @param code
+ * Code.
+ * @param message
+ * Message.
+ */
+ public AbstractResult(@NotNull final ResultType type, @Nullable final String code, @Nullable final String message) {
+ Contract.requireArgNotNull("type", type);
+ this.type = type;
+ this.code = code;
+ this.message = message;
+ }
+
+ /**
+ * Constructor with exception. An exception of type {@link ExceptionShortIdentifable} will be used to fill the code field
+ * with the identifier value. If it's not a {@link ExceptionShortIdentifable} the code field will be set using the full
+ * qualified class name of the exception.
+ *
+ * @param exception
+ * The message for the result is equal to the exception message or the simple name of the exception class if the exception
+ * message is null.
+ */
+ public AbstractResult(@NotNull final Exception exception) {
+ super();
+ Contract.requireArgNotNull("exception", exception);
+ this.type = ResultType.ERROR;
+ if (exception instanceof ExceptionShortIdentifable) {
+ this.code = ((ExceptionShortIdentifable) exception).getShortId();
+ } else {
+ this.code = exception.getClass().getName();
+ }
+ if (exception.getMessage() == null) {
+ this.message = "";
+ } else {
+ this.message = exception.getMessage();
+ }
+ }
+
+ @Override
+ @JsonIgnore
+ public final ResultType getType() {
+ return type;
+ }
+
+ @Override
+ @JsonIgnore
+ public final String getCode() {
+ return code;
+ }
+
+ @Override
+ @JsonIgnore
+ public final String getMessage() {
+ return message;
+ }
+
+}
diff --git a/jackson/src/main/java/org/fuin/cqrs4j/jackson/Cqrs4JacksonModule.java b/jackson/src/main/java/org/fuin/cqrs4j/jackson/Cqrs4JacksonModule.java
new file mode 100644
index 0000000..b8fbfe7
--- /dev/null
+++ b/jackson/src/main/java/org/fuin/cqrs4j/jackson/Cqrs4JacksonModule.java
@@ -0,0 +1,46 @@
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.module.SimpleDeserializers;
+import com.fasterxml.jackson.databind.module.SimpleSerializers;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.fuin.utils4j.TestOmitted;
+
+import java.util.List;
+
+/**
+ * Module that registers the adapters for the package.
+ */
+@TestOmitted("Tested with other tests")
+public class Cqrs4JacksonModule extends Module {
+
+ @Override
+ public String getModuleName() {
+ return "Cqrs4JModule";
+ }
+
+ @Override
+ public Iterable extends Module> getDependencies() {
+ return List.of(new JavaTimeModule());
+ }
+
+ @Override
+ public void setupModule(SetupContext context) {
+ final SimpleSerializers serializers = new SimpleSerializers();
+ serializers.addSerializer(new DataResultJacksonSerializer());
+ context.addSerializers(serializers);
+
+ final SimpleDeserializers deserializers = new SimpleDeserializers();
+ deserializers.addDeserializer(DataResult.class, new DataResultJacksonDeserializer());
+ context.addDeserializers(deserializers);
+ }
+
+ @Override
+ public Version version() {
+ // Don't forget to change from release to SNAPSHOT and back!
+ return new Version(0, 6, 0, "SNAPSHOT",
+ "org.fuin.cqrs4j", "cqrs-4-java-jackson");
+ }
+
+}
\ No newline at end of file
diff --git a/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResult.java b/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResult.java
new file mode 100644
index 0000000..0727aa2
--- /dev/null
+++ b/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResult.java
@@ -0,0 +1,281 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.annotation.Nullable;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.ddd4j.core.ExceptionData;
+import org.fuin.objects4j.common.Contract;
+import org.fuin.objects4j.common.MarshalInformation;
+import org.fuin.objects4j.ui.Label;
+import org.fuin.objects4j.ui.Prompt;
+import org.fuin.objects4j.ui.ShortLabel;
+import org.fuin.objects4j.ui.Tooltip;
+
+import java.io.Serial;
+
+/**
+ * Result of a request that contains data in addition to the standard result fields. The type signals if the execution was successful or
+ * not. In case the the result is not {@link ResultType#OK}, the fields code and message should contain unique information to help the user
+ * identifying the cause of the problem.
+ *
+ * @param Type of data returned in case of success (type = {@link ResultType#OK}).
+ */
+public final class DataResult extends AbstractResult {
+
+ @Serial
+ private static final long serialVersionUID = 1000L;
+
+ static final String DATA_CLASS_PROPERTY = "data-class";
+
+ static final String DATA_ELEMENT_PROPERTY = "data-element";
+
+ @JsonProperty(DATA_CLASS_PROPERTY)
+ private String dataClass;
+
+ @JsonProperty(DATA_ELEMENT_PROPERTY)
+ private String dataElement;
+
+ @Label("Data")
+ @ShortLabel("DATA")
+ @Tooltip("Optional result data")
+ @Prompt("Optional Data")
+ @Valid
+ @SuppressWarnings("java:S1948") // We assume the unknown data is serializable
+ private Object data;
+
+ /**
+ * Protected default constructor for de-serialization.
+ */
+ protected DataResult() { // NOSONAR Ignore uninitialized fields
+ super();
+ }
+
+ /**
+ * Constructor without data element name.
+ *
+ * @param type Type.
+ * @param code Code.
+ * @param message Message.
+ * @param data Optional result data.
+ */
+ public DataResult(@NotNull final ResultType type, @Nullable final String code, @Nullable final String message,
+ @Nullable final DATA data) {
+ super(type, code, message);
+ if (data instanceof MarshalInformation) {
+ final MarshalInformation mui = (MarshalInformation) data;
+ this.data = mui.getData();
+ this.dataClass = mui.getDataClass().getName();
+ this.dataElement = mui.getDataElement();
+ } else {
+ this.data = data;
+ this.dataClass = null;
+ this.dataElement = null;
+ }
+ }
+
+ /**
+ * Constructor with all data.
+ *
+ * @param type Type.
+ * @param code Code.
+ * @param message Message.
+ * @param data Optional result data.
+ * @param dataElement Optional name of the data element.
+ */
+ public DataResult(@NotNull final ResultType type, @Nullable final String code, @Nullable final String message, @Nullable final DATA data,
+ final String dataElement) {
+ super(type, code, message);
+ this.data = data;
+ if (data == null) {
+ this.dataClass = null;
+ this.dataElement = null;
+ } else {
+ this.dataClass = data.getClass().getName();
+ this.dataElement = dataElement;
+ }
+ }
+
+ /**
+ * Constructor with exception data.
+ *
+ * @param exceptionData .
+ */
+ public DataResult(@NotNull final ExceptionData extends Exception> exceptionData) {
+ super(exceptionData.toException());
+ this.data = exceptionData;
+ this.dataClass = exceptionData.getClass().getName();
+ this.dataElement = exceptionData.getDataElement();
+ }
+
+ /**
+ * Returns the name of the class contained in the data element.
+ *
+ * @return Full qualified class name.
+ */
+ @JsonIgnore
+ public final String getDataClass() {
+ return dataClass;
+ }
+
+ /**
+ * Returns the name of the data attribute.
+ *
+ * @return Data element name.
+ */
+ @JsonIgnore
+ public final String getDataElement() {
+ return dataElement;
+ }
+
+ /**
+ * Returns the result data.
+ *
+ * @return Response data.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ @JsonIgnore
+ public final DATA getData() {
+ return (DATA) data;
+ }
+
+ @Override
+ public final int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((getCode() == null) ? 0 : getCode().hashCode());
+ result = prime * result + ((getMessage() == null) ? 0 : getMessage().hashCode());
+ result = prime * result + getType().hashCode();
+ result = prime * result + ((dataClass == null) ? 0 : dataClass.hashCode());
+ result = prime * result + ((dataElement == null) ? 0 : dataElement.hashCode());
+ result = prime * result + ((data == null) ? 0 : data.hashCode());
+ return result;
+ }
+
+ @Override
+ public final boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final DataResult> other = (DataResult>) obj;
+ if (getCode() == null) {
+ if (other.getCode() != null) {
+ return false;
+ }
+ } else if (!getCode().equals(other.getCode())) {
+ return false;
+ }
+ if (getMessage() == null) {
+ if (other.getMessage() != null) {
+ return false;
+ }
+ } else if (!getMessage().equals(other.getMessage())) {
+ return false;
+ }
+ if (getType() != other.getType()) {
+ return false;
+ }
+ if (dataClass == null) {
+ if (other.dataClass != null) {
+ return false;
+ }
+ } else if (!dataClass.equals(other.dataClass)) {
+ return false;
+ }
+ if (dataElement == null) {
+ if (other.dataElement != null) {
+ return false;
+ }
+ } else if (!dataElement.equals(other.dataElement)) {
+ return false;
+ }
+ if (data == null) {
+ if (other.data != null) {
+ return false;
+ }
+ } else if (!data.equals(other.data)) {
+ return false;
+ }
+ return true;
+ }
+
+
+ @Override
+ public final String toString() {
+ return "Result [type=" + getType() + ", code=" + getCode() + ", message=" + getMessage() + ", dataClass=" + dataClass
+ + ", dataElement=" + dataElement + "]";
+ }
+
+ /**
+ * Create a success result without any data.
+ *
+ * @return Result with type {@link ResultType#OK}.
+ */
+ public static DataResult ok() {
+ return new DataResult<>(ResultType.OK, null, null, null);
+ }
+
+ /**
+ * Create a success result with some data.
+ *
+ * @param data Optional data.
+ * @param Type of data.
+ * @return Result with type {@link ResultType#OK}.
+ */
+ public static DataResult ok(@Nullable final T data) {
+ return new DataResult<>(ResultType.OK, null, null, data);
+ }
+
+ /**
+ * Create a success result with some data.
+ *
+ * @param data Optional data.
+ * @param dataElement Optional name of the data element.
+ * @param Type of data.
+ * @return Result with type {@link ResultType#OK}.
+ */
+ public static DataResult ok(@Nullable final T data, final String dataElement) {
+ return new DataResult<>(ResultType.OK, null, null, data, dataElement);
+ }
+
+ /**
+ * Create an error result without any data.
+ *
+ * @param code Code.
+ * @param message Message.
+ * @param Not used.
+ * @return Error result with.
+ */
+ public static DataResult error(@NotNull final String code, @NotNull final String message) {
+ Contract.requireArgNotNull("code", code);
+ Contract.requireArgNotNull("message", message);
+ return new DataResult<>(ResultType.ERROR, code, message, null);
+ }
+
+}
diff --git a/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResultJacksonDeserializer.java b/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResultJacksonDeserializer.java
new file mode 100644
index 0000000..7f4f885
--- /dev/null
+++ b/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResultJacksonDeserializer.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.objects4j.jackson.Objects4JacksonUtils;
+
+import java.io.IOException;
+
+/**
+ * Converts an {@link DataResult} from/to JSON.
+ */
+@SuppressWarnings("rawtypes")
+public final class DataResultJacksonDeserializer extends StdDeserializer {
+
+ /**
+ * Default constructor.
+ */
+ public DataResultJacksonDeserializer() {
+ super(DataResult.class);
+ }
+
+ @Override
+ public DataResult deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+
+ final JsonNode node = jp.getCodec().readTree(jp);
+
+ final ResultType type = ResultType.valueOf(node.get(AbstractResult.TYPE_PROPERTY).asText());
+ final String code;
+ if (node.has(AbstractResult.CODE_PROPERTY)) {
+ code = node.get(AbstractResult.CODE_PROPERTY).asText();
+ } else {
+ code = null;
+ }
+ final String message;
+ if (node.has(AbstractResult.MESSAGE_PROPERTY)) {
+ message = node.get(AbstractResult.MESSAGE_PROPERTY).asText();
+ } else {
+ message = null;
+ }
+ if (node.has(DataResult.DATA_CLASS_PROPERTY)) {
+ if (!node.has(DataResult.DATA_ELEMENT_PROPERTY)) {
+ throw new IllegalStateException(
+ "The '" + DataResult.DATA_ELEMENT_PROPERTY + "' was not found, but is required for deserialization: " + node);
+ }
+ final String dataClassName = node.get(DataResult.DATA_CLASS_PROPERTY).asText();
+ final String dataElement = node.get(DataResult.DATA_ELEMENT_PROPERTY).asText();
+ final JsonNode dataNode = node.get(dataElement);
+ final Object data = Objects4JacksonUtils.deserialize(jp, ctxt, dataClassName, dataNode);
+ return new DataResult<>(type, code, message, data, dataElement);
+ }
+ return new DataResult<>(type, code, message, null);
+ }
+
+}
diff --git a/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResultJacksonSerializer.java b/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResultJacksonSerializer.java
new file mode 100644
index 0000000..d885ba5
--- /dev/null
+++ b/jackson/src/main/java/org/fuin/cqrs4j/jackson/DataResultJacksonSerializer.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import org.fuin.utils4j.TestOmitted;
+
+import java.io.IOException;
+
+/**
+ * Converts an {@link DataResult} from/to JSON.
+ */
+@SuppressWarnings("rawtypes")
+@TestOmitted("Tested with other tests")
+public final class DataResultJacksonSerializer extends StdSerializer {
+
+ /**
+ * Default constructor.
+ */
+ public DataResultJacksonSerializer() {
+ super(DataResult.class);
+ }
+
+ @Override
+ public void serialize(DataResult result, JsonGenerator generator,
+ SerializerProvider provider) throws IOException {
+
+ generator.writeStartObject();
+ generator.writeStringField(AbstractResult.TYPE_PROPERTY, result.getType().name());
+ if (result.getCode() != null) {
+ generator.writeStringField(AbstractResult.CODE_PROPERTY, result.getCode());
+ }
+ if (result.getMessage() != null) {
+ generator.writeStringField(AbstractResult.MESSAGE_PROPERTY, result.getMessage());
+ }
+ if (result.getData() != null) {
+ generator.writeStringField(DataResult.DATA_CLASS_PROPERTY, result.getData().getClass().getName());
+ final String elName = result.getDataElement();
+ if (elName == null) {
+ throw new IllegalStateException("The 'dataElementName' was empty, but is required for serialization: " + result);
+ }
+ generator.writeStringField(DataResult.DATA_ELEMENT_PROPERTY, result.getDataElement());
+ generator.writeObjectField(elName, result.getData());
+ }
+ generator.writeEndObject();
+ }
+
+}
diff --git a/src/main/java/org/fuin/cqrs4j/SimpleResult.java b/jackson/src/main/java/org/fuin/cqrs4j/jackson/SimpleResult.java
similarity index 89%
rename from src/main/java/org/fuin/cqrs4j/SimpleResult.java
rename to jackson/src/main/java/org/fuin/cqrs4j/jackson/SimpleResult.java
index 3260699..aa47c4e 100644
--- a/src/main/java/org/fuin/cqrs4j/SimpleResult.java
+++ b/jackson/src/main/java/org/fuin/cqrs4j/jackson/SimpleResult.java
@@ -15,23 +15,25 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.jackson;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;
-import jakarta.xml.bind.annotation.XmlRootElement;
-
+import org.fuin.cqrs4j.core.ResultType;
import org.fuin.objects4j.common.Contract;
import org.fuin.objects4j.common.ExceptionShortIdentifable;
-import org.fuin.objects4j.common.Nullable;
+
+import java.io.Serial;
/**
- * Result of a request. The type signals if the execution was successful or not. In case the the result is not {@link ResultType#OK}, the
+ * Result of a request. The type signals if the execution was successful or not. In case the result is not {@link ResultType#OK}, the
* fields code and message should contain unique information to help the user identifying the cause of the problem. A simple result does not
* carry any additional data.
*/
-@XmlRootElement(name = "result")
public final class SimpleResult extends AbstractResult {
+ @Serial
private static final long serialVersionUID = 1000L;
/**
@@ -64,25 +66,23 @@ public SimpleResult(@NotNull final ResultType type, @Nullable final String code,
* The message for the result is equal to the exception message or the simple name of the exception class if the exception
* message is null.
*/
- // CHECKSTYLE:OFF:AvoidInlineConditionals
public SimpleResult(@NotNull final Exception exception) {
- // CHECKSTYLE:ON
- super(exception);
+ super(exception);
}
@Override
+ @JsonIgnore
public Void getData() {
return null;
}
@Override
- // CHECKSTYLE:OFF Generated code
public final int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getCode() == null) ? 0 : getCode().hashCode());
result = prime * result + ((getMessage() == null) ? 0 : getMessage().hashCode());
- result = prime * result + ((getType() == null) ? 0 : getType().hashCode());
+ result = prime * result + getType().hashCode();
return result;
}
@@ -112,14 +112,10 @@ public final boolean equals(final Object obj) {
} else if (!getMessage().equals(other.getMessage())) {
return false;
}
- if (getType() != other.getType()) {
- return false;
- }
- return true;
+ return getType() == other.getType();
}
- // CHECKSTYLE:ON
-
+
@Override
public final String toString() {
return "Result [type=" + getType() + ", code=" + getCode() + ", message=" + getMessage() + "]";
diff --git a/jackson/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module b/jackson/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module
new file mode 100644
index 0000000..ea456e8
--- /dev/null
+++ b/jackson/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module
@@ -0,0 +1 @@
+org.fuin.cqrs4j.jackson.Cqrs4JacksonModule
\ No newline at end of file
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/ACreatedEvent.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/ACreatedEvent.java
new file mode 100644
index 0000000..6c428d8
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/ACreatedEvent.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import org.fuin.ddd4j.core.EntityIdPath;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.ddd4j.jackson.AbstractDomainEvent;
+import org.fuin.esc.api.HasSerializedDataTypeConstant;
+import org.fuin.esc.api.SerializedDataType;
+import org.fuin.esc.api.TypeName;
+import org.fuin.utils4j.TestOmitted;
+
+import java.io.Serial;
+
+@TestOmitted("This is only a test class")
+@HasSerializedDataTypeConstant
+public class ACreatedEvent extends AbstractDomainEvent {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /** Unique name of the event. */
+ public static final TypeName TYPE = new TypeName("ACreatedEvent");
+
+ /** Unique name of the serialized event. */
+ public static final SerializedDataType SER_TYPE = new SerializedDataType(TYPE.asBaseType());
+
+ private static final EventType EVENT_TYPE = new EventType(TYPE.asBaseType());
+
+ private AId id;
+
+ public ACreatedEvent(final AId id) {
+ super(new EntityIdPath(id));
+ this.id = id;
+ }
+
+ public AId getId() {
+ return id;
+ }
+
+ @Override
+ public EventType getEventType() {
+ return EVENT_TYPE;
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/AId.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/AId.java
new file mode 100644
index 0000000..9d3f546
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/AId.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import org.fuin.ddd4j.core.AggregateRootId;
+import org.fuin.ddd4j.core.EntityType;
+import org.fuin.ddd4j.core.StringBasedEntityType;
+import org.fuin.utils4j.TestOmitted;
+
+import java.io.Serial;
+
+@TestOmitted("This is only a test class")
+public class AId implements AggregateRootId {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public static final EntityType TYPE = new StringBasedEntityType("A");
+
+ private final long id;
+
+ public AId(final long id) {
+ this.id = id;
+ }
+
+ @Override
+ public EntityType getType() {
+ return TYPE;
+ }
+
+ @Override
+ public String asString() {
+ return "" + id;
+ }
+
+ @Override
+ public String asTypedString() {
+ return getType() + " " + asString();
+ }
+
+ @Override
+ public String toString() {
+ return "AId [id=" + id + "]";
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/AbstractAggregateCommandTest.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/AbstractAggregateCommandTest.java
new file mode 100644
index 0000000..5b937eb
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/AbstractAggregateCommandTest.java
@@ -0,0 +1,430 @@
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.validation.Validation;
+import org.fuin.ddd4j.core.AggregateVersion;
+import org.fuin.ddd4j.core.EntityIdPath;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventId;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.ddd4j.jackson.AbstractEvent;
+import org.fuin.objects4j.common.Contract;
+import org.junit.jupiter.api.Test;
+
+import java.io.Serial;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.fuin.utils4j.Utils4J.deserialize;
+import static org.fuin.utils4j.Utils4J.serialize;
+
+public class AbstractAggregateCommandTest {
+
+ private static final EventType MY_EVENT_TYPE = new EventType("MyEvent");
+
+ private static final EventType MY_COMMAND_TYPE = new EventType("MyCommandt");
+
+ @Test
+ public final void testConstructor() {
+
+ // PREPARE
+ final AId aid = new AId(123L);
+ final EntityIdPath entityIdPath = new EntityIdPath(aid);
+ final AggregateVersion version = new AggregateVersion(1);
+
+ // TEST
+ final AbstractAggregateCommand testee = new MyCommand(entityIdPath, version);
+
+ // VERIFY
+ assertThat(testee.getAggregateRootId()).isEqualTo(aid);
+ assertThat(testee.getEntityId()).isEqualTo(aid);
+ assertThat(testee.getAggregateVersion()).isEqualTo(version);
+ assertThat(testee.getEventId()).isNotNull();
+ assertThat(testee.getEventTimestamp()).isNotNull();
+ assertThat(testee.getCausationId()).isNull();
+ assertThat(testee.getCorrelationId()).isNull();
+ assertThat(testee.getEventType()).isEqualTo(MY_COMMAND_TYPE);
+
+ }
+
+ @Test
+ public final void testConstructor2() {
+
+ // PREPARE
+ final AId aid = new AId(123L);
+ final BId bid = new BId(1L);
+ final EntityIdPath entityIdPath = new EntityIdPath(aid, bid);
+ final AggregateVersion version = new AggregateVersion(1);
+
+ // TEST
+ final AbstractAggregateCommand testee = new MyCommand2(entityIdPath, version);
+
+ // VERIFY
+ assertThat(testee.getAggregateRootId()).isEqualTo(aid);
+ assertThat(testee.getEntityId()).isEqualTo(bid);
+ assertThat(testee.getAggregateVersion()).isEqualTo(version);
+ assertThat(testee.getEventId()).isNotNull();
+ assertThat(testee.getEventTimestamp()).isNotNull();
+ assertThat(testee.getCausationId()).isNull();
+ assertThat(testee.getCorrelationId()).isNull();
+ assertThat(testee.getEventType()).isEqualTo(MY_COMMAND_TYPE);
+
+ }
+
+ @Test
+ public final void testConstructor3() {
+
+ // PREPARE
+ final AId aid = new AId(123L);
+ final BId bid = new BId(1L);
+ final CId cid = new CId(2L);
+ final EntityIdPath entityIdPath = new EntityIdPath(aid, bid, cid);
+ final AggregateVersion version = new AggregateVersion(1);
+
+ // TEST
+ final AbstractAggregateCommand testee = new MyCommand3(entityIdPath, version);
+
+ // VERIFY
+ assertThat(testee.getAggregateRootId()).isEqualTo(aid);
+ assertThat(testee.getEntityId()).isEqualTo(cid);
+ assertThat(testee.getAggregateVersion()).isEqualTo(version);
+ assertThat(testee.getEventId()).isNotNull();
+ assertThat(testee.getEventTimestamp()).isNotNull();
+ assertThat(testee.getCausationId()).isNull();
+ assertThat(testee.getCorrelationId()).isNull();
+ assertThat(testee.getEventType()).isEqualTo(MY_COMMAND_TYPE);
+
+ }
+
+ @Test
+ public final void testConstructorEvent() {
+
+ // PREPARE
+ final AId aid = new AId(123L);
+ final EntityIdPath entityIdPath = new EntityIdPath(aid);
+ final AggregateVersion version = new AggregateVersion(1);
+ final EventId correlationId = new EventId();
+ final EventId causationId = new EventId();
+ final MyEvent event = new MyEvent(correlationId, causationId);
+
+ // TEST
+ final AbstractAggregateCommand, ?> testee = new MyCommand(entityIdPath, version, event);
+
+ // VERIFY
+ assertThat(testee.getAggregateRootId()).isEqualTo(aid);
+ assertThat(testee.getEntityId()).isEqualTo(aid);
+ assertThat(testee.getAggregateVersion()).isEqualTo(version);
+ assertThat(testee.getEventId()).isNotNull();
+ assertThat(testee.getEventTimestamp()).isNotNull();
+ assertThat(testee.getCausationId()).isEqualTo(event.getEventId());
+ assertThat(testee.getCorrelationId()).isEqualTo(correlationId);
+ assertThat(testee.getEventType()).isEqualTo(MY_COMMAND_TYPE);
+
+ }
+
+ @Test
+ public final void testConstructorCorrelationCausation() {
+
+ // PREPARE
+ final AId aid = new AId(123L);
+ final EntityIdPath entityIdPath = new EntityIdPath(aid);
+ final AggregateVersion version = new AggregateVersion(1);
+ final EventId correlationId = new EventId();
+ final EventId causationId = new EventId();
+
+ // TEST
+ final AbstractAggregateCommand, ?> testee = new MyCommand(entityIdPath, version, correlationId, causationId);
+
+ // VERIFY
+ assertThat(testee.getAggregateRootId()).isEqualTo(aid);
+ assertThat(testee.getEntityId()).isEqualTo(aid);
+ assertThat(testee.getAggregateVersion()).isEqualTo(version);
+ assertThat(testee.getEventId()).isNotNull();
+ assertThat(testee.getEventTimestamp()).isNotNull();
+ assertThat(testee.getCausationId()).isEqualTo(causationId);
+ assertThat(testee.getCorrelationId()).isEqualTo(correlationId);
+ assertThat(testee.getEventType()).isEqualTo(MY_COMMAND_TYPE);
+
+ }
+
+ @Test
+ public final void testBuilder() {
+
+ // PREPARE
+ final EventId eventId = new EventId();
+ final ZonedDateTime timestamp = ZonedDateTime.now();
+ final AId aid = new AId(123L);
+ final EntityIdPath entityIdPath = new EntityIdPath(aid);
+ final AggregateVersion version = new AggregateVersion(1);
+ final EventId correlationId = new EventId();
+ final EventId causationId = new EventId();
+ final MyCommand.Builder testee = new MyCommand.Builder();
+
+ // TEST
+ final MyCommand cmd = testee
+ .eventId(eventId)
+ .timestamp(timestamp)
+ .entityIdPath(entityIdPath)
+ .aggregateVersion(version)
+ .correlationId(correlationId)
+ .causationId(causationId).build();
+
+ // VERIFY
+ assertThat(cmd.getEventId()).isEqualTo(eventId);
+ assertThat(cmd.getEventTimestamp()).isEqualTo(timestamp);
+ assertThat(cmd.getAggregateRootId()).isEqualTo(aid);
+ assertThat(cmd.getEntityId()).isEqualTo(aid);
+ assertThat(cmd.getAggregateVersion()).isEqualTo(version);
+ assertThat(cmd.getEventId()).isNotNull();
+ assertThat(cmd.getEventTimestamp()).isNotNull();
+ assertThat(cmd.getCausationId()).isEqualTo(causationId);
+ assertThat(cmd.getCorrelationId()).isEqualTo(correlationId);
+ assertThat(cmd.getEventType()).isEqualTo(MY_COMMAND_TYPE);
+
+ }
+
+ @Test
+ public final void testSerializeDeserialize() {
+
+ // PREPARE
+ final AId aid = new AId(123L);
+ final EntityIdPath entityIdPath = new EntityIdPath(aid);
+ final AggregateVersion version = new AggregateVersion(1);
+ final EventId correlationId = new EventId();
+ final EventId causationId = new EventId();
+ final MyCommand original = new MyCommand(entityIdPath, version, correlationId, causationId);
+
+ // TEST
+ final MyCommand copy = deserialize(serialize(original));
+
+ // VERIFY
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.getEntityIdPath()).isEqualTo(new EntityIdPath(aid));
+ assertThat(copy.getAggregateVersion()).isEqualTo(version);
+ assertThat(copy.getCausationId()).isEqualTo(original.getCausationId());
+ assertThat(copy.getCorrelationId()).isEqualTo(original.getCorrelationId());
+ assertThat(copy.getEventId()).isEqualTo(original.getEventId());
+ assertThat(copy.getEventType()).isEqualTo(original.getEventType());
+ assertThat(copy.getEventTimestamp()).isEqualTo(original.getEventTimestamp());
+
+ }
+
+ @Test
+ public final void testMarshalUnmarshal() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final AId aid = new AId(123L);
+ final EntityIdPath entityIdPath = new EntityIdPath(aid);
+ final AggregateVersion version = new AggregateVersion(1);
+ final EventId correlationId = new EventId();
+ final EventId causationId = new EventId();
+ final MyCommand original = new MyCommand(entityIdPath, version, correlationId, causationId);
+
+ // TEST
+ final String json = objectMapper.writeValueAsString(original);
+ final MyCommand copy = objectMapper.readValue(json, MyCommand.class);
+
+ // VERIFY
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.getEntityIdPath()).isEqualTo(new EntityIdPath(aid));
+ assertThat(copy.getAggregateVersion()).isEqualTo(version);
+ assertThat(copy.getCausationId()).isEqualTo(original.getCausationId());
+ assertThat(copy.getCorrelationId()).isEqualTo(original.getCorrelationId());
+ assertThat(copy.getEventId()).isEqualTo(original.getEventId());
+ assertThat(copy.getEventType()).isEqualTo(original.getEventType());
+ assertThat(copy.getEventTimestamp()).isEqualTo(original.getEventTimestamp());
+
+ }
+
+ @Test
+ public final void testUnmarshal() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final String json = """
+ {
+ "entity-id-path" : "A 1/B 2/C 3",
+ "aggregate-version" : 1,
+ "event-id" : "f910c6d7-debc-46e1-ae02-9ca6f4658cf5",
+ "event-timestamp" : "2016-09-18T10:38:08.0+02:00[Europe/Berlin]",
+ "correlation-id" : "2a5893a9-00da-4003-b280-98324eccdef1",
+ "causation-id" : "f13d3481-51b7-423f-8fe7-5c342f7d7c46"
+ }
+ """;
+
+ // TEST
+ final MyCommand copy = objectMapper.readValue(json, MyCommand.class);
+
+ // VERIFY
+ Contract.requireValid(Validation.buildDefaultValidatorFactory().getValidator(), copy);
+ assertThat(copy.getEntityIdPath()).isEqualTo(new EntityIdPath(new AId(1L), new BId(2L), new CId(3L)));
+ assertThat(copy.getAggregateVersion()).isEqualTo(new AggregateVersion(1));
+ assertThat(copy.getCausationId()).isEqualTo(new EventId(UUID.fromString("f13d3481-51b7-423f-8fe7-5c342f7d7c46")));
+ assertThat(copy.getCorrelationId()).isEqualTo(new EventId(UUID.fromString("2a5893a9-00da-4003-b280-98324eccdef1")));
+ assertThat(copy.getEventId()).isEqualTo(new EventId(UUID.fromString("f910c6d7-debc-46e1-ae02-9ca6f4658cf5")));
+ assertThat(copy.getEventType()).isEqualTo(copy.getEventType());
+ assertThat(copy.getEventTimestamp()).isEqualTo(ZonedDateTime.of(2016, 9, 18, 10, 38, 8, 0, ZoneId.of("Europe/Berlin")));
+
+ }
+
+ @Test
+ public final void testUnmarshalNullVersion() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final String json = """
+ {
+ "entity-id-path" : "A 1/B 2/C 3",
+ "event-id" : "f910c6d7-debc-46e1-ae02-9ca6f4658cf5",
+ "event-timestamp" : "2016-09-18T10:38:08.0+02:00[Europe/Berlin]",
+ "correlation-id" : "2a5893a9-00da-4003-b280-98324eccdef1",
+ "causation-id" : "f13d3481-51b7-423f-8fe7-5c342f7d7c46"
+ }
+ """;
+
+ // TEST
+ final MyCommand copy = objectMapper.readValue(json, MyCommand.class);
+
+ // VERIFY
+ Contract.requireValid(Validation.buildDefaultValidatorFactory().getValidator(), copy);
+ assertThat(copy.getEntityIdPath()).isEqualTo(new EntityIdPath(new AId(1L), new BId(2L), new CId(3L)));
+ assertThat(copy.getAggregateVersion()).isNull();
+ assertThat(copy.getCausationId()).isEqualTo(new EventId(UUID.fromString("f13d3481-51b7-423f-8fe7-5c342f7d7c46")));
+ assertThat(copy.getCorrelationId()).isEqualTo(new EventId(UUID.fromString("2a5893a9-00da-4003-b280-98324eccdef1")));
+ assertThat(copy.getEventId()).isEqualTo(new EventId(UUID.fromString("f910c6d7-debc-46e1-ae02-9ca6f4658cf5")));
+ assertThat(copy.getEventType()).isEqualTo(copy.getEventType());
+ assertThat(copy.getEventTimestamp()).isEqualTo(ZonedDateTime.of(2016, 9, 18, 10, 38, 8, 0, ZoneId.of("Europe/Berlin")));
+
+ }
+
+ public static class MyCommand extends AbstractAggregateCommand {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public MyCommand() {
+ super();
+ }
+
+ public MyCommand(EntityIdPath entityIdPath, AggregateVersion aggregateVersion) {
+ super(entityIdPath, aggregateVersion);
+ }
+
+ public MyCommand(EntityIdPath entityIdPath, AggregateVersion aggregateVersion, Event respondTo) {
+ super(entityIdPath, aggregateVersion, respondTo);
+ }
+
+ public MyCommand(EntityIdPath entityIdPath, AggregateVersion aggregateVersion, EventId correlationId, EventId causationId) {
+ super(entityIdPath, aggregateVersion, correlationId, causationId);
+ }
+
+ @Override
+ @JsonIgnore
+ public EventType getEventType() {
+ return MY_COMMAND_TYPE;
+ }
+
+ public static class Builder extends AbstractAggregateCommand.Builder {
+
+ private MyCommand delegate;
+
+ public Builder() {
+ super(new MyCommand());
+ delegate = delegate();
+ }
+
+ public MyCommand build() {
+ ensureBuildableAbstractAggregateCommand();
+ final MyCommand result = delegate;
+ delegate = new MyCommand();
+ resetAbstractAggregateCommand(delegate);
+ return result;
+ }
+
+ }
+
+ }
+
+ public static class MyCommand2 extends AbstractAggregateCommand {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public MyCommand2() {
+ super();
+ }
+
+ public MyCommand2(EntityIdPath entityIdPath, AggregateVersion aggregateVersion) {
+ super(entityIdPath, aggregateVersion);
+ }
+
+ public MyCommand2(EntityIdPath entityIdPath, AggregateVersion aggregateVersion, Event respondTo) {
+ super(entityIdPath, aggregateVersion, respondTo);
+ }
+
+ public MyCommand2(EntityIdPath entityIdPath, AggregateVersion aggregateVersion, EventId correlationId, EventId causationId) {
+ super(entityIdPath, aggregateVersion, correlationId, causationId);
+ }
+
+ @Override
+ @JsonIgnore
+ public EventType getEventType() {
+ return MY_COMMAND_TYPE;
+ }
+
+ }
+
+ public static class MyCommand3 extends AbstractAggregateCommand {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public MyCommand3() {
+ super();
+ }
+
+ public MyCommand3(EntityIdPath entityIdPath, AggregateVersion aggregateVersion) {
+ super(entityIdPath, aggregateVersion);
+ }
+
+ public MyCommand3(EntityIdPath entityIdPath, AggregateVersion aggregateVersion, Event respondTo) {
+ super(entityIdPath, aggregateVersion, respondTo);
+ }
+
+ public MyCommand3(EntityIdPath entityIdPath, AggregateVersion aggregateVersion, EventId correlationId, EventId causationId) {
+ super(entityIdPath, aggregateVersion, correlationId, causationId);
+ }
+
+ @Override
+ @JsonIgnore
+ public EventType getEventType() {
+ return MY_COMMAND_TYPE;
+ }
+
+ }
+
+ public static class MyEvent extends AbstractEvent {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public MyEvent(EventId correlationId, EventId causationId) {
+ super(correlationId, causationId);
+ }
+
+ @Override
+ @JsonIgnore
+ public EventType getEventType() {
+ return MY_EVENT_TYPE;
+ }
+
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/AbstractCommandTest.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/AbstractCommandTest.java
new file mode 100644
index 0000000..eb04add
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/AbstractCommandTest.java
@@ -0,0 +1,175 @@
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventId;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.ddd4j.jackson.AbstractEvent;
+import org.junit.jupiter.api.Test;
+
+import java.io.Serial;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.fuin.utils4j.Utils4J.deserialize;
+import static org.fuin.utils4j.Utils4J.serialize;
+
+public class AbstractCommandTest {
+
+ private static final EventType MY_EVENT_TYPE = new EventType("MyEvent");
+
+ private static final EventType MY_COMMAND_TYPE = new EventType("MyCommand");
+
+ @Test
+ public final void testConstructorDefault() {
+
+ // TEST
+ final AbstractCommand testee = new MyCommand();
+
+ // VERIFY
+ assertThat(testee.getEventId()).isNotNull();
+ assertThat(testee.getEventTimestamp()).isNotNull();
+ assertThat(testee.getCausationId()).isNull();
+ assertThat(testee.getCorrelationId()).isNull();
+ assertThat(testee.getEventType()).isEqualTo(MY_COMMAND_TYPE);
+
+ }
+
+ @Test
+ public final void testConstructorEvent() {
+
+ // PREPARE
+ final EventId correlationId = new EventId();
+ final EventId causationId = new EventId();
+ final MyEvent event = new MyEvent(correlationId, causationId);
+
+ // TEST
+ final AbstractCommand testee = new MyCommand(event);
+
+ // VERIFY
+ assertThat(testee.getEventId()).isNotNull();
+ assertThat(testee.getEventTimestamp()).isNotNull();
+ assertThat(testee.getCausationId()).isEqualTo(event.getEventId());
+ assertThat(testee.getCorrelationId()).isEqualTo(correlationId);
+ assertThat(testee.getEventType()).isEqualTo(MY_COMMAND_TYPE);
+
+ }
+
+ @Test
+ public final void testSerializeDeserialize() {
+
+ // PREPARE
+ final EventId correlationId = new EventId();
+ final EventId causationId = new EventId();
+ final MyCommand original = new MyCommand(correlationId, causationId);
+
+ // TEST
+ final MyCommand copy = deserialize(serialize(original));
+
+ // VERIFY
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.getCausationId()).isEqualTo(original.getCausationId());
+ assertThat(copy.getCorrelationId()).isEqualTo(original.getCorrelationId());
+ assertThat(copy.getEventId()).isEqualTo(original.getEventId());
+ assertThat(copy.getEventType()).isEqualTo(original.getEventType());
+ assertThat(copy.getEventTimestamp()).isEqualTo(original.getEventTimestamp());
+
+ }
+
+ @Test
+ public final void testMarshalUnmarshal() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final EventId correlationId = new EventId();
+ final EventId causationId = new EventId();
+ final MyCommand original = new MyCommand(correlationId, causationId);
+
+ // TEST
+ final String json = objectMapper.writeValueAsString(original);
+ final MyCommand copy = objectMapper.readValue(json, MyCommand.class);
+
+ // VERIFY
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.getCausationId()).isEqualTo(original.getCausationId());
+ assertThat(copy.getCorrelationId()).isEqualTo(original.getCorrelationId());
+ assertThat(copy.getEventId()).isEqualTo(original.getEventId());
+ assertThat(copy.getEventType()).isEqualTo(original.getEventType());
+ assertThat(copy.getEventTimestamp()).isEqualTo(original.getEventTimestamp());
+
+ }
+
+ @Test
+ public final void testUnmarshal() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final String originalJson = """
+ {
+ "event-id" : "f910c6d7-debc-46e1-ae02-9ca6f4658cf5",
+ "event-timestamp" : "2016-09-18T10:38:08.0+02:00[Europe/Berlin]",
+ "correlation-id" : "2a5893a9-00da-4003-b280-98324eccdef1",
+ "causation-id" : "f13d3481-51b7-423f-8fe7-5c342f7d7c46"
+ }
+ """;
+
+ // TEST
+ final MyCommand copy = objectMapper.readValue(originalJson, MyCommand.class);
+
+ // VERIFY
+ assertThat(copy.getCausationId()).isEqualTo(new EventId(UUID.fromString("f13d3481-51b7-423f-8fe7-5c342f7d7c46")));
+ assertThat(copy.getCorrelationId()).isEqualTo(new EventId(UUID.fromString("2a5893a9-00da-4003-b280-98324eccdef1")));
+ assertThat(copy.getEventId()).isEqualTo(new EventId(UUID.fromString("f910c6d7-debc-46e1-ae02-9ca6f4658cf5")));
+ assertThat(copy.getEventType()).isEqualTo(copy.getEventType());
+ assertThat(copy.getEventTimestamp()).isEqualTo(ZonedDateTime.of(2016, 9, 18, 10, 38, 8, 0, ZoneId.of("Europe/Berlin")));
+
+ }
+
+ public static class MyCommand extends AbstractCommand {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public MyCommand() {
+ super();
+ }
+
+ public MyCommand(Event respondTo) {
+ super(respondTo);
+ }
+
+ public MyCommand(EventId correlationId, EventId causationId) {
+ super(correlationId, causationId);
+ }
+
+ @Override
+ @JsonIgnore
+ public EventType getEventType() {
+ return MY_COMMAND_TYPE;
+ }
+
+ }
+
+ public static class MyEvent extends AbstractEvent {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public MyEvent(EventId correlationId, EventId causationId) {
+ super(correlationId, causationId);
+ }
+
+ @Override
+ @JsonIgnore
+ public EventType getEventType() {
+ return MY_EVENT_TYPE;
+ }
+
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/ArchitectureTest.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/ArchitectureTest.java
new file mode 100644
index 0000000..6947cb1
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/ArchitectureTest.java
@@ -0,0 +1,47 @@
+package org.fuin.cqrs4j.jackson;
+
+import com.tngtech.archunit.core.importer.ImportOption;
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import org.fuin.cqrs4j.core.Command;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import static com.tngtech.archunit.library.DependencyRules.NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
+
+/**
+ * Tests architectural aspects.
+ */
+@AnalyzeClasses(packagesOf = ArchitectureTest.class, importOptions = ImportOption.DoNotIncludeTests.class)
+public class ArchitectureTest {
+
+ private static final String THIS_PACKAGE = ArchitectureTest.class.getPackageName();
+
+ private static final String CORE_PACKAGE = Command.class.getPackageName();
+
+ @ArchTest
+ static final ArchRule no_accesses_to_upper_package = NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
+
+ @ArchTest
+ static final ArchRule access_only_to_defined_packages = classes()
+ .that()
+ .resideInAPackage(THIS_PACKAGE)
+ .should()
+ .onlyDependOnClassesThat()
+ .resideInAnyPackage(THIS_PACKAGE, CORE_PACKAGE, "java..",
+ "org.fuin.ddd4j.common..",
+ "org.fuin.ddd4j.core..",
+ "org.fuin.ddd4j.jackson..",
+ "org.fuin.objects4j.ui..",
+ "org.fuin.objects4j.common..",
+ "org.fuin.objects4j.core..",
+ "org.fuin.objects4j.jackson..",
+ "org.fuin.utils4j..",
+ "jakarta.validation..",
+ "jakarta.annotation..",
+ "com.fasterxml.jackson..",
+ "org.slf4j..",
+ "javax.annotation.concurrent.."
+ );
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/BId.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/BId.java
new file mode 100644
index 0000000..ec16326
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/BId.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.EntityType;
+import org.fuin.ddd4j.core.StringBasedEntityType;
+import org.fuin.utils4j.TestOmitted;
+
+import java.io.Serial;
+
+@TestOmitted("This is only a test class")
+public class BId implements EntityId {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public static final EntityType TYPE = new StringBasedEntityType("B");
+
+ private final long id;
+
+ public BId(final long id) {
+ this.id = id;
+ }
+
+ @Override
+ public EntityType getType() {
+ return TYPE;
+ }
+
+ @Override
+ public String asString() {
+ return "" + id;
+ }
+
+ @Override
+ public String asTypedString() {
+ return getType() + " " + asString();
+ }
+
+ @Override
+ public String toString() {
+ return "BId [id=" + id + "]";
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/BaseTest.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/BaseTest.java
new file mode 100644
index 0000000..b3982a2
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/BaseTest.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2013 Future Invent Informationsmanagement GmbH. All rights
+ * reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import org.fuin.units4j.archunit.Units4JConditions;
+
+@AnalyzeClasses(packagesOf = BaseTest.class)
+class BaseTest {
+
+ @ArchTest
+ static final ArchRule all_classes_should_have_tests = Units4JConditions.ALL_CLASSES_SHOULD_HAVE_TESTS;
+
+}
+
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/CId.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/CId.java
new file mode 100644
index 0000000..99e08bb
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/CId.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.EntityType;
+import org.fuin.ddd4j.core.StringBasedEntityType;
+import org.fuin.utils4j.TestOmitted;
+
+import java.io.Serial;
+
+@TestOmitted("This is only a test class")
+public class CId implements EntityId {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public static final EntityType TYPE = new StringBasedEntityType("C");
+
+ private final long id;
+
+ public CId(final long id) {
+ this.id = id;
+ }
+
+ @Override
+ public EntityType getType() {
+ return TYPE;
+ }
+
+ @Override
+ public String asString() {
+ return "" + id;
+ }
+
+ @Override
+ public String asTypedString() {
+ return getType() + " " + asString();
+ }
+
+ @Override
+ public String toString() {
+ return "CId [id=" + id + "]";
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/DataResultJacksonDeserializerTest.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/DataResultJacksonDeserializerTest.java
new file mode 100644
index 0000000..d659a90
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/DataResultJacksonDeserializerTest.java
@@ -0,0 +1,349 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleDeserializers;
+import com.fasterxml.jackson.databind.module.SimpleSerializers;
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.ddd4j.jackson.AggregateNotFoundExceptionData;
+import org.fuin.objects4j.common.AsStringCapable;
+import org.fuin.objects4j.jackson.ValueObjectStringJacksonDeserializer;
+import org.fuin.objects4j.jackson.ValueObjectStringJacksonSerializer;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for the {@link DataResultJacksonDeserializer} class.
+ */
+public class DataResultJacksonDeserializerTest {
+
+ @Test
+ public final void testToFromJson() throws Exception {
+
+ // PREPARE
+ final ObjectMapper objectMapper = createObjectMapper();
+ final DataResult original = DataResult.ok(new MyData(1, "one"), "my-data");
+
+ // TEST
+ final String json = objectMapper.writeValueAsString(original);
+ final DataResult copy = objectMapper.readValue(json, DataResult.class);
+
+ // VERIFY
+ assertThat(copy).isEqualTo(original);
+
+ }
+
+ @Test
+ public final void testFromToJsonVoidResult() throws IOException {
+
+ // PREPARE
+ final ObjectMapper objectMapper = createObjectMapper();
+ final String jsonOriginal = """
+ {
+ "type": "OK"
+ }
+ """;
+
+ // TEST
+ final DataResult original = objectMapper.readValue(jsonOriginal, DataResult.class);
+
+ // VERIFY
+ assertThat(original.getType()).isEqualTo(ResultType.OK);
+ assertThat(original.getCode()).isNull();
+ assertThat(original.getMessage()).isNull();
+ assertThat(original.getData()).isNull();
+ assertThat(original.getDataClass()).isNull();
+ assertThat(original.getDataElement()).isNull();
+
+ // TEST
+ final String jsonCopy = objectMapper.writeValueAsString(original);
+ final DataResult copy = objectMapper.readValue(jsonCopy, DataResult.class);
+ assertThat(copy).isEqualTo(original);
+
+ }
+
+ @Test
+ public final void testFromToJsonSimpleResultOK() throws IOException {
+
+ // PREPARE
+ final ObjectMapper objectMapper = createObjectMapper();
+ final String jsonOriginal = """
+ {
+ "type": "OK"
+ }
+ """;
+
+ // TEST
+ final SimpleResult original = objectMapper.readValue(jsonOriginal, SimpleResult.class);
+
+ // VERIFY
+ assertThat(original.getType()).isEqualTo(ResultType.OK);
+ assertThat(original.getCode()).isNull();
+ assertThat(original.getMessage()).isNull();
+ assertThat(original.getData()).isNull();
+
+ // TEST
+ final String jsonCopy = objectMapper.writeValueAsString(original);
+ final SimpleResult copy = objectMapper.readValue(jsonCopy, SimpleResult.class);
+ assertThat(copy).isEqualTo(original);
+
+ }
+
+ @Test
+ public final void testFromToJsonSimpleResultException() throws IOException {
+
+ // PREPARE
+ final ObjectMapper objectMapper = createObjectMapper();
+ final String jsonOriginal = """
+ {
+ "type": "ERROR",
+ "code": "DDD4J-AGGREGATE_NOT_FOUND",
+ "message": "Invoice with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found"
+ }
+ """;
+
+ // TEST
+ final SimpleResult original = objectMapper.readValue(jsonOriginal, SimpleResult.class);
+
+ // VERIFY
+ assertThat(original.getType()).isEqualTo(ResultType.ERROR);
+ assertThat(original.getCode()).isEqualTo("DDD4J-AGGREGATE_NOT_FOUND");
+ assertThat(original.getMessage()).isEqualTo("Invoice with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found");
+ assertThat(original.getData()).isNull();
+
+ // TEST
+ final String jsonCopy = objectMapper.writeValueAsString(original);
+ final SimpleResult copy = objectMapper.readValue(jsonCopy, SimpleResult.class);
+ assertThat(copy).isEqualTo(original);
+
+ }
+
+ @Test
+ public final void testFromToJsonResultData() throws IOException {
+
+ // PREPARE
+ final ObjectMapper objectMapper = createObjectMapper();
+ final String jsonOriginal = """
+ {
+ "type": "OK",
+ "data-class": "org.fuin.cqrs4j.jackson.Invoice",
+ "data-element": "invoice",
+ "invoice": {
+ "id" : "I-0123456"
+ }
+ }
+ """;
+
+ // TEST
+ final DataResult original = objectMapper.readValue(jsonOriginal, DataResult.class);
+
+ // VERIFY
+ assertThat(original.getType()).isEqualTo(ResultType.OK);
+ assertThat(original.getCode()).isNull();
+ assertThat(original.getMessage()).isNull();
+ assertThat(original.getDataClass()).isEqualTo(Invoice.class.getName());
+ assertThat(original.getDataElement()).isEqualTo("invoice");
+ assertThat(original.getData()).isInstanceOf(Invoice.class);
+ assertThat(original.getData().getId()).isEqualTo("I-0123456");
+
+ // TEST
+ final String jsonCopy = objectMapper.writeValueAsString(original);
+ final DataResult copy = objectMapper.readValue(jsonCopy, DataResult.class);
+ assertThat(copy).isEqualTo(original);
+
+ }
+
+ @Test
+ public final void testFromToJsonResultException() throws IOException {
+
+ // PREPARE
+ final ObjectMapper objectMapper = createObjectMapper();
+ final String jsonOriginal = """
+ {
+ "type": "ERROR",
+ "code": "DDD4J-AGGREGATE_NOT_FOUND",
+ "message": "Invoice with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found",
+ "data-class": "org.fuin.ddd4j.jackson.AggregateNotFoundExceptionData",
+ "data-element": "aggregate-not-found-exception",
+ "aggregate-not-found-exception": {
+ "msg": "Invoice with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found",
+ "sid": "DDD4J-AGGREGATE_NOT_FOUND",
+ "aggregate-type": "Invoice",
+ "aggregate-id": "4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119"
+ }
+ }
+ """;
+
+ // TEST
+ final DataResult original = objectMapper.readValue(jsonOriginal, DataResult.class);
+
+ // VERIFY
+ assertThat(original.getType()).isEqualTo(ResultType.ERROR);
+ assertThat(original.getCode()).isEqualTo("DDD4J-AGGREGATE_NOT_FOUND");
+ assertThat(original.getMessage()).isEqualTo("Invoice with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found");
+ assertThat(original.getDataClass()).isEqualTo("org.fuin.ddd4j.jackson.AggregateNotFoundExceptionData");
+ assertThat(original.getDataElement()).isEqualTo("aggregate-not-found-exception");
+ assertThat(original.getData()).isInstanceOf(AggregateNotFoundExceptionData.class);
+
+ // TEST
+ final String jsonCopy = objectMapper.writeValueAsString(original);
+ final DataResult copy = objectMapper.readValue(jsonCopy, DataResult.class);
+ assertThat(copy).isEqualTo(original);
+
+ }
+
+ public static ObjectMapper createObjectMapper() {
+ return TestUtils.objectMapper()
+ .registerModule(new TestAdapterModule());
+ }
+
+
+ public static final class InvoiceId implements AsStringCapable {
+
+ private String id;
+
+ public InvoiceId(final String id) {
+ super();
+ this.id = id;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ InvoiceId other = (InvoiceId) obj;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+
+ @Override
+ public String asString() {
+ return id;
+ }
+
+ }
+
+ public static class TestAdapterModule extends Module {
+
+ @Override
+ public String getModuleName() {
+ return "TestModule";
+ }
+
+ @Override
+ public void setupModule(SetupContext context) {
+
+ final SimpleSerializers serializers = new SimpleSerializers();
+ serializers.addSerializer(new ValueObjectStringJacksonSerializer<>(InvoiceId.class));
+ context.addSerializers(serializers);
+
+ final SimpleDeserializers deserializers = new SimpleDeserializers();
+ deserializers.addDeserializer(InvoiceId.class, new ValueObjectStringJacksonDeserializer<>(InvoiceId.class, InvoiceId::new));
+ context.addDeserializers(deserializers);
+ }
+
+ @Override
+ public Version version() {
+ return new Version(1, 0, 0, null,
+ "foo", "bar");
+ }
+
+ }
+
+ public static final class MyData {
+
+ @JsonProperty("id")
+ private int id;
+
+ @JsonProperty("name")
+ private String name;
+
+ protected MyData() {
+ super();
+ }
+
+ public MyData(final int id, final String name) {
+ super();
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + id;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ MyData other = (MyData) obj;
+ if (id != other.id)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "MyData [id=" + id + ", name=" + name + "]";
+ }
+
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/DataResultTest.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/DataResultTest.java
new file mode 100644
index 0000000..bf6f3d2
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/DataResultTest.java
@@ -0,0 +1,240 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.ddd4j.core.AggregateNotFoundException;
+import org.fuin.ddd4j.core.AggregateRootId;
+import org.fuin.ddd4j.core.EntityType;
+import org.fuin.ddd4j.core.StringBasedEntityType;
+import org.fuin.ddd4j.jackson.AggregateNotFoundExceptionData;
+import org.junit.jupiter.api.Test;
+
+import java.io.Serial;
+import java.util.UUID;
+
+import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public final class DataResultTest {
+
+ private static final EntityType TEST_TYPE = new StringBasedEntityType("Test");
+
+ @Test
+ public final void testEqualsHashCode() {
+ EqualsVerifier.simple().forClass(DataResult.class).verify();
+ }
+
+ @Test
+ public final void testConstructorAll() {
+
+ // PREPARE
+ final String data = "Whatever";
+
+ // TEST
+ final DataResult testee = new DataResult<>(ResultType.WARNING, "X1", "Yes!", data);
+
+ // VERIFY
+ assertThat(testee.getType()).isEqualTo(ResultType.WARNING);
+ assertThat(testee.getCode()).isEqualTo("X1");
+ assertThat(testee.getMessage()).isEqualTo("Yes!");
+ assertThat(testee.getData()).isEqualTo(data);
+
+ }
+
+ @Test
+ public final void testConstructorException() {
+
+ // PREPARE
+ final TestId id = new TestId();
+ final AggregateNotFoundException ex = new AggregateNotFoundException(TEST_TYPE, id);
+ final AggregateNotFoundExceptionData exData = new AggregateNotFoundExceptionData(ex);
+
+ // TEST
+ final DataResult testee = new DataResult<>(exData);
+
+ // VERIFY
+ assertThat(testee.getType()).isEqualTo(ResultType.ERROR);
+ assertThat(testee.getCode()).isEqualTo("DDD4J-AGGREGATE_NOT_FOUND");
+ assertThat(testee.getMessage()).isEqualTo(TEST_TYPE + " with id " + id.asString() + " not found");
+ assertThat(testee.getData()).isInstanceOf(AggregateNotFoundExceptionData.class);
+
+ }
+
+ @Test
+ public final void testUnmarshalMarshalVoidResult() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final String originalJson = """
+ {
+ "type": "OK"
+ }
+ """;
+
+ // TEST
+ final DataResult copy = objectMapper.readValue(originalJson, DataResult.class);
+
+ // VERIFY
+ assertThat(copy.getType()).isEqualTo(ResultType.OK);
+
+ // TEST
+ final String copyJson = objectMapper.writeValueAsString(copy);
+
+ // VERIFY
+ assertThatJson(copyJson).isEqualTo(originalJson);
+
+ }
+
+ @Test
+ public final void testUnmarshalMarshalDataResult() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final String originalJson = """
+ {
+ "type": "OK",
+ "data-class": "org.fuin.cqrs4j.jackson.Invoice",
+ "data-element": "invoice",
+ "invoice": {
+ "id" : "I-0123456"
+ }
+ }
+ """;
+
+ // TEST
+ final DataResult copy = objectMapper.readValue(originalJson, DataResult.class);
+
+ // VERIFY
+ assertThat(copy.getType()).isEqualTo(ResultType.OK);
+ assertThat(copy.getCode()).isNull();
+ assertThat(copy.getMessage()).isNull();
+ assertThat(copy.getData()).isInstanceOf(Invoice.class);
+ assertThat(copy.getData().getId()).isEqualTo("I-0123456");
+
+ // TEST
+ final String copyJson = objectMapper.writeValueAsString(copy);
+
+ // VERIFY
+ assertThatJson(copyJson).isEqualTo(originalJson);
+
+ }
+
+ @Test
+ public final void testUnmarshalExceptionResult() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final String originalJson = """
+ {
+ "type": "ERROR",
+ "code": "DDD4J-AGGREGATE_NOT_FOUND",
+ "message": "Vendor with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found",
+ "data-class": "org.fuin.ddd4j.jackson.AggregateNotFoundExceptionData",
+ "data-element": "aggregate-not-found-exception",
+ "aggregate-not-found-exception" : {
+ "msg" : "Vendor with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found",
+ "sid" : "DDD4J-AGGREGATE_NOT_FOUND",
+ "aggregate-type" : "Vendor",
+ "aggregate-id" : "4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119"
+ }
+ }
+ """;
+
+ // TEST
+ final DataResult copy = objectMapper.readValue(originalJson, DataResult.class);
+
+ // VERIFY
+ final String msg = "Vendor with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found";
+ assertThat(copy.getCode()).isEqualTo("DDD4J-AGGREGATE_NOT_FOUND");
+ assertThat(copy.getType()).isEqualTo(ResultType.ERROR);
+ assertThat(copy.getMessage()).isEqualTo(msg);
+ assertThat(copy.getData()).isInstanceOf(AggregateNotFoundExceptionData.class);
+ final AggregateNotFoundException anfe = copy.getData().toException();
+ assertThat(anfe.getMessage()).isEqualTo(msg);
+ assertThat(anfe.getType()).isEqualTo("Vendor");
+ assertThat(anfe.getId()).isEqualTo("4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119");
+
+ // TEST
+ final String copyJson = objectMapper.writeValueAsString(copy);
+
+ // VERIFY
+ assertThatJson(copyJson).isEqualTo(originalJson);
+
+ }
+
+ private static class TestId implements AggregateRootId {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private UUID id = UUID.randomUUID();
+
+ @Override
+ public EntityType getType() {
+ return TEST_TYPE;
+ }
+
+ @Override
+ public String asTypedString() {
+ return TEST_TYPE + " " + id;
+ }
+
+ @Override
+ public String asString() {
+ return id.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof TestId)) {
+ return false;
+ }
+ TestId other = (TestId) obj;
+ if (id == null) {
+ if (other.id != null) {
+ return false;
+ }
+ } else if (!id.equals(other.id)) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/Invoice.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/Invoice.java
new file mode 100644
index 0000000..f0726ef
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/Invoice.java
@@ -0,0 +1,76 @@
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.fuin.objects4j.common.MarshalInformation;
+import org.fuin.utils4j.TestOmitted;
+
+@TestOmitted("This is only a test class")
+public class Invoice implements MarshalInformation {
+
+ @JsonProperty("id")
+ private String id;
+
+ protected Invoice() {
+ super();
+ }
+
+ public Invoice(String id) {
+ super();
+ this.id = id;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Invoice other = (Invoice) obj;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+
+ @Override
+ @JsonIgnore
+ public Class getDataClass() {
+ return Invoice.class;
+ }
+
+ @Override
+ @JsonIgnore
+ public String getDataElement() {
+ return Invoice.class.getName();
+ }
+
+ @Override
+ @JsonIgnore
+ public Invoice getData() {
+ return this;
+ }
+
+ @JsonIgnore
+ public String getId() {
+ return id;
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/MyIdFactory.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/MyIdFactory.java
new file mode 100644
index 0000000..18c51d9
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/MyIdFactory.java
@@ -0,0 +1,41 @@
+package org.fuin.cqrs4j.jackson;
+
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.EntityIdFactory;
+import org.fuin.utils4j.TestOmitted;
+
+@TestOmitted("This is only a test class")
+final class MyIdFactory implements EntityIdFactory {
+ @Override
+ public EntityId createEntityId(final String type, final String id) {
+ if (type.equals("A")) {
+ return new AId(Long.valueOf(id));
+ }
+ if (type.equals("B")) {
+ return new BId(Long.valueOf(id));
+ }
+ if (type.equals("C")) {
+ return new CId(Long.valueOf(id));
+ }
+ throw new IllegalArgumentException("Unknown type: '" + type + "'");
+ }
+
+ @Override
+ public boolean containsType(final String type) {
+ if (type.equals("A")) {
+ return true;
+ }
+ if (type.equals("B")) {
+ return true;
+ }
+ if (type.equals("C")) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isValid(String type, String id) {
+ return true;
+ }
+}
diff --git a/src/test/java/org/fuin/cqrs4j/SimpleResultTest.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/SimpleResultTest.java
similarity index 65%
rename from src/test/java/org/fuin/cqrs4j/SimpleResultTest.java
rename to jackson/src/test/java/org/fuin/cqrs4j/jackson/SimpleResultTest.java
index 1e3dd19..e9958f2 100644
--- a/src/test/java/org/fuin/cqrs4j/SimpleResultTest.java
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/SimpleResultTest.java
@@ -1,187 +1,200 @@
-/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
- * http://www.fuin.org/
- *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option) any
- * later version.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library. If not, see http://www.gnu.org/licenses/.
- */
-package org.fuin.cqrs4j;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.fuin.utils4j.jaxb.JaxbUtils.marshal;
-import static org.fuin.utils4j.jaxb.JaxbUtils.unmarshal;
-
-import java.io.IOException;
-import java.util.UUID;
-
-import org.apache.commons.io.IOUtils;
-import org.fuin.ddd4j.ddd.AggregateNotFoundException;
-import org.fuin.ddd4j.ddd.AggregateRootId;
-import org.fuin.ddd4j.ddd.EntityType;
-import org.fuin.ddd4j.ddd.StringBasedEntityType;
-import org.junit.jupiter.api.Test;
-import org.xmlunit.builder.DiffBuilder;
-import org.xmlunit.diff.Diff;
-
-// CHECKSTYLE:OFF
-public final class SimpleResultTest {
-
- private static final EntityType TEST_TYPE = new StringBasedEntityType("Test");
-
- @Test
- public final void testConstructorAll() {
-
- // TEST
- final SimpleResult testee = new SimpleResult(ResultType.WARNING, "X1", "Yes!");
-
- // VERIFY
- assertThat(testee.getType()).isEqualTo(ResultType.WARNING);
- assertThat(testee.getCode()).isEqualTo("X1");
- assertThat(testee.getMessage()).isEqualTo("Yes!");
-
- }
-
- @Test
- public final void testConstructorException() {
-
- // PREPARE
- final TestId id = new TestId();
- final AggregateNotFoundException ex = new AggregateNotFoundException(TEST_TYPE, id);
-
- // TEST
- final SimpleResult testee = new SimpleResult(ex);
-
- // VERIFY
- assertThat(testee.getType()).isEqualTo(ResultType.ERROR);
- assertThat(testee.getCode()).isEqualTo("DDD4J-AGGREGATE_NOT_FOUND");
- assertThat(testee.getMessage()).isEqualTo(TEST_TYPE + " with id " + id.asString() + " not found");
-
- }
-
- @Test
- public final void testMarshalUnmarshal() {
-
- // PREPARE
- final SimpleResult original = SimpleResult.ok();
-
- // TEST
- final String xml = marshal(original, SimpleResult.class);
- final SimpleResult copy = unmarshal(xml, SimpleResult.class);
-
- // VERIFY
- assertThat(original).isEqualTo(copy);
-
- }
-
- @Test
- public final void testUnmarshalMarshalOkResult() throws IOException {
-
- // PREPARE
- final String xml = IOUtils.toString(this.getClass().getResourceAsStream("/simple-result-ok.xml"), "utf-8");
-
- // TEST
- final SimpleResult copy = unmarshal(xml, SimpleResult.class);
-
- // VERIFY
- assertThat(copy.getType()).isEqualTo(ResultType.OK);
-
- // TEST
- final String copyXml = marshal(copy, SimpleResult.class);
-
- // VERIFY
- final Diff documentDiff = DiffBuilder.compare(xml).withTest(copyXml).ignoreWhitespace().build();
-
- assertThat(documentDiff.hasDifferences()).describedAs(documentDiff.toString()).isFalse();
-
- }
-
- @Test
- public final void testUnmarshalExceptionResult() throws IOException {
-
- // PREPARE
- final String xml = IOUtils.toString(this.getClass().getResourceAsStream("/simple-result-exception.xml"), "utf-8");
-
- // TEST
- final SimpleResult copy = unmarshal(xml, SimpleResult.class);
-
- // VERIFY
- final String msg = "Invoice with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found";
- assertThat(copy.getCode()).isEqualTo("DDD4J-AGGREGATE_NOT_FOUND");
- assertThat(copy.getType()).isEqualTo(ResultType.ERROR);
- assertThat(copy.getMessage()).isEqualTo(msg);
-
- // TEST
- final String copyXml = marshal(copy, SimpleResult.class, AggregateNotFoundException.class);
-
- // VERIFY
- final Diff documentDiff = DiffBuilder.compare(xml).withTest(copyXml).ignoreWhitespace().build();
-
- assertThat(documentDiff.hasDifferences()).describedAs(documentDiff.toString()).isFalse();
-
- }
-
- private static class TestId implements AggregateRootId {
-
- private static final long serialVersionUID = 1L;
-
- private UUID id = UUID.randomUUID();
-
- @Override
- public EntityType getType() {
- return TEST_TYPE;
- }
-
- @Override
- public String asTypedString() {
- return TEST_TYPE + " " + id;
- }
-
- @Override
- public String asString() {
- return id.toString();
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((id == null) ? 0 : id.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (!(obj instanceof TestId)) {
- return false;
- }
- TestId other = (TestId) obj;
- if (id == null) {
- if (other.id != null) {
- return false;
- }
- } else if (!id.equals(other.id)) {
- return false;
- }
- return true;
- }
-
- }
-
-}
-// CHECKSTYLE:ON
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.ddd4j.core.AggregateNotFoundException;
+import org.fuin.ddd4j.core.AggregateRootId;
+import org.fuin.ddd4j.core.EntityType;
+import org.fuin.ddd4j.core.StringBasedEntityType;
+import org.junit.jupiter.api.Test;
+
+import java.io.Serial;
+import java.util.UUID;
+
+import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public final class SimpleResultTest {
+
+ private static final EntityType TEST_TYPE = new StringBasedEntityType("Test");
+
+ @Test
+ public final void testEqualsHashCode() {
+ EqualsVerifier.simple().forClass(SimpleResult.class).verify();
+ }
+
+ @Test
+ public final void testConstructorAll() {
+
+ // TEST
+ final SimpleResult testee = new SimpleResult(ResultType.WARNING, "X1", "Yes!");
+
+ // VERIFY
+ assertThat(testee.getType()).isEqualTo(ResultType.WARNING);
+ assertThat(testee.getCode()).isEqualTo("X1");
+ assertThat(testee.getMessage()).isEqualTo("Yes!");
+
+ }
+
+ @Test
+ public final void testConstructorException() {
+
+ // PREPARE
+ final TestId id = new TestId();
+ final AggregateNotFoundException ex = new AggregateNotFoundException(TEST_TYPE, id);
+
+ // TEST
+ final SimpleResult testee = new SimpleResult(ex);
+
+ // VERIFY
+ assertThat(testee.getType()).isEqualTo(ResultType.ERROR);
+ assertThat(testee.getCode()).isEqualTo("DDD4J-AGGREGATE_NOT_FOUND");
+ assertThat(testee.getMessage()).isEqualTo(TEST_TYPE + " with id " + id.asString() + " not found");
+
+ }
+
+ @Test
+ public final void testMarshalUnmarshal() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final SimpleResult original = SimpleResult.ok();
+
+ // TEST
+ final String json = objectMapper.writeValueAsString(original);
+ final SimpleResult copy = objectMapper.readValue(json, SimpleResult.class);
+
+ // VERIFY
+ assertThat(original).isEqualTo(copy);
+
+ }
+
+ @Test
+ public final void testUnmarshalMarshalOkResult() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final String originalJson = """
+ { "type": "OK" }
+ """;
+
+ // TEST
+ final SimpleResult copy = objectMapper.readValue(originalJson, SimpleResult.class);
+
+ // VERIFY
+ assertThat(copy.getType()).isEqualTo(ResultType.OK);
+
+ // TEST
+ final String copyJson = objectMapper.writeValueAsString(copy);
+
+ // VERIFY
+ assertThatJson(copyJson).isEqualTo(originalJson);
+
+ }
+
+ @Test
+ public final void testUnmarshalExceptionResult() throws Exception {
+
+ final ObjectMapper objectMapper = TestUtils.objectMapper();
+
+ // PREPARE
+ final String originalJson = """
+ {
+ "type": "ERROR",
+ "code": "DDD4J-AGGREGATE_NOT_FOUND",
+ "message": "Invoice with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found"
+ }
+ """;
+
+ // TEST
+ final SimpleResult copy = objectMapper.readValue(originalJson, SimpleResult.class);
+
+ // VERIFY
+ final String msg = "Invoice with id 4dcf4c2c-10e1-4db9-ba9e-d1e644e9d119 not found";
+ assertThat(copy.getCode()).isEqualTo("DDD4J-AGGREGATE_NOT_FOUND");
+ assertThat(copy.getType()).isEqualTo(ResultType.ERROR);
+ assertThat(copy.getMessage()).isEqualTo(msg);
+
+ // TEST
+ final String copyJson = objectMapper.writeValueAsString(copy);
+
+ // VERIFY
+ assertThatJson(copyJson).isEqualTo(originalJson);
+
+ }
+
+ private static class TestId implements AggregateRootId {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private final UUID id = UUID.randomUUID();
+
+ @Override
+ public EntityType getType() {
+ return TEST_TYPE;
+ }
+
+ @Override
+ public String asTypedString() {
+ return TEST_TYPE + " " + id;
+ }
+
+ @Override
+ public String asString() {
+ return id.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof TestId)) {
+ return false;
+ }
+ TestId other = (TestId) obj;
+ if (id == null) {
+ if (other.id != null) {
+ return false;
+ }
+ } else if (!id.equals(other.id)) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+}
diff --git a/jackson/src/test/java/org/fuin/cqrs4j/jackson/TestUtils.java b/jackson/src/test/java/org/fuin/cqrs4j/jackson/TestUtils.java
new file mode 100644
index 0000000..45dd456
--- /dev/null
+++ b/jackson/src/test/java/org/fuin/cqrs4j/jackson/TestUtils.java
@@ -0,0 +1,34 @@
+package org.fuin.cqrs4j.jackson;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.fuin.ddd4j.core.EntityIdFactory;
+import org.fuin.ddd4j.jackson.Ddd4JacksonModule;
+import org.fuin.objects4j.jackson.Objects4JJacksonModule;
+import org.fuin.utils4j.TestOmitted;
+
+/**
+ * Utils for the package.
+ */
+@TestOmitted("This is only a test class")
+final class TestUtils {
+
+ private static final EntityIdFactory ENTITY_ID_FACTORY = new MyIdFactory();
+
+ private TestUtils() {
+ }
+
+ /**
+ * Creates an instance with the configured values.
+ *
+ * @return New instance.
+ */
+ public static ObjectMapper objectMapper() {
+ return new ObjectMapper()
+ .setSerializationInclusion(JsonInclude.Include.NON_NULL)
+ .registerModule(new Cqrs4JacksonModule())
+ .registerModule(new Objects4JJacksonModule())
+ .registerModule(new Ddd4JacksonModule(ENTITY_ID_FACTORY));
+ }
+
+}
diff --git a/jacoco/pom.xml b/jacoco/pom.xml
new file mode 100644
index 0000000..a55941c
--- /dev/null
+++ b/jacoco/pom.xml
@@ -0,0 +1,86 @@
+
+
+
+ 4.0.0
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java
+ 0.6.0-SNAPSHOT
+ ../pom.xml
+
+
+ cqrs-4-java-jacoco
+ ${description} (JACOCO)
+
+
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java-core
+
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java-esc
+
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java-jaxb
+
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java-jsonb
+
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java-jackson
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 3.1.1
+
+ true
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ report-aggregate
+ verify
+
+ report-aggregate
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+ true
+
+
+
+
+
+
+
+
diff --git a/jacoco/src/main/java/org/fuin/cqrs4j/jacoco/Dummy.java b/jacoco/src/main/java/org/fuin/cqrs4j/jacoco/Dummy.java
new file mode 100644
index 0000000..3d7f7b6
--- /dev/null
+++ b/jacoco/src/main/java/org/fuin/cqrs4j/jacoco/Dummy.java
@@ -0,0 +1,8 @@
+package org.fuin.cqrs4j.jacoco;
+
+@SuppressWarnings("java:S2094") // Empty by intention
+public class Dummy {
+
+ // Required to have a valid Java project for the JaCoCo module
+
+}
diff --git a/jaxb/pom.xml b/jaxb/pom.xml
new file mode 100644
index 0000000..c769ba3
--- /dev/null
+++ b/jaxb/pom.xml
@@ -0,0 +1,221 @@
+
+
+
+ 4.0.0
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java
+ 0.6.0-SNAPSHOT
+ ../pom.xml
+
+
+ cqrs-4-java-jaxb
+ jar
+ ${description} (JAXB)
+
+
+
+
+
+
+ org.fuin.cqrs4j
+ cqrs-4-java-core
+
+
+
+ org.fuin.ddd4j
+ ddd-4-java-core
+
+
+
+ org.fuin.ddd4j
+ ddd-4-java-jaxb
+
+
+
+ org.fuin.objects4j
+ objects4j-common
+
+
+
+ org.fuin.objects4j
+ objects4j-ui
+
+
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+
+ jakarta.annotation
+ jakarta.annotation-api
+
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+
+
+
+ org.fuin
+ utils4j
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+ org.fuin
+ units4j
+ test
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+ test
+
+
+
+ org.glassfish.expressly
+ expressly
+ test
+
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ test
+
+
+
+ org.fuin.esc
+ esc-api
+ test
+
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+ org.xmlunit
+ xmlunit-core
+ test
+
+
+
+ nl.jqno.equalsverifier
+ equalsverifier
+ test
+
+
+
+ com.tngtech.archunit
+ archunit
+ test
+
+
+
+ com.tngtech.archunit
+ archunit-junit5
+ test
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ **/*
+
+
+
+ org.fuin.cqrs4j.jaxb
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jdeps-plugin
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ jakarta.el:jakarta.el-api
+ org.glassfish.expressly:expressly
+ org.hibernate.validator:hibernate-validator
+ org.glassfish.jaxb:jaxb-runtime
+ com.tngtech.archunit:archunit-junit5
+ org.junit.jupiter:junit-jupiter
+
+
+ com.tngtech.archunit:archunit-junit5-api
+ org.junit.jupiter:junit-jupiter-api
+
+
+ org.fuin:utils4j
+
+
+
+
+
+
+
+
+
diff --git a/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractAggregateCommand.java b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractAggregateCommand.java
new file mode 100644
index 0000000..9e7b9e7
--- /dev/null
+++ b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractAggregateCommand.java
@@ -0,0 +1,234 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jaxb;
+
+import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import org.fuin.cqrs4j.core.AggregateCommand;
+import org.fuin.ddd4j.core.AggregateRootId;
+import org.fuin.ddd4j.core.AggregateVersion;
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.EntityIdPath;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventId;
+import org.fuin.ddd4j.jaxb.AggregateVersionXmlAdapter;
+import org.fuin.ddd4j.jaxb.EntityIdPathXmlAdapter;
+import org.fuin.objects4j.common.Contract;
+
+import java.io.Serial;
+
+/**
+ * Base class for all commands that are directed to an existing aggregate.
+ *
+ * @param Type of the aggregate root identifier.
+ * @param Type of the identifier (the last one in the path).
+ */
+public abstract class AbstractAggregateCommand extends AbstractCommand
+ implements AggregateCommand {
+
+ @Serial
+ private static final long serialVersionUID = 1000L;
+
+ @NotNull
+ @XmlJavaTypeAdapter(EntityIdPathXmlAdapter.class)
+ @XmlElement(name = "entity-id-path")
+ private EntityIdPath entityIdPath;
+
+ @Nullable
+ @XmlJavaTypeAdapter(AggregateVersionXmlAdapter.class)
+ @XmlElement(name = "aggregate-version")
+ private AggregateVersion aggregateVersion;
+
+ /**
+ * Default constructor for JAXB.
+ */
+ protected AbstractAggregateCommand() { // NOSONAR Ignore uninitialized fields
+ super();
+ }
+
+ /**
+ * Constructor with aggregate root id and version.
+ *
+ * @param aggregateRootId Aggregate root identifier.
+ * @param aggregateVersion Expected aggregate version.
+ */
+ public AbstractAggregateCommand(@NotNull final AggregateRootId aggregateRootId, @Nullable final AggregateVersion aggregateVersion) {
+ this(new EntityIdPath(aggregateRootId), aggregateVersion);
+ }
+
+ /**
+ * Constructor with entitiy id path and version.
+ *
+ * @param entityIdPath Path from root aggregate to target entity.
+ * @param aggregateVersion Expected aggregate version.
+ */
+ public AbstractAggregateCommand(@NotNull final EntityIdPath entityIdPath, @Nullable final AggregateVersion aggregateVersion) {
+ super();
+ Contract.requireArgNotNull("entityIdPath", entityIdPath);
+ this.entityIdPath = entityIdPath;
+ this.aggregateVersion = aggregateVersion;
+ }
+
+ /**
+ * Constructor with event this one responds to. Convenience method to set the correlation and causation identifiers correctly.
+ *
+ * @param entityIdPath Path from root aggregate to target entity.
+ * @param aggregateVersion Expected aggregate version.
+ * @param respondTo Causing event.
+ */
+ public AbstractAggregateCommand(@NotNull final EntityIdPath entityIdPath, @Nullable final AggregateVersion aggregateVersion,
+ @NotNull final Event respondTo) {
+ super(respondTo);
+ Contract.requireArgNotNull("entityIdPath", entityIdPath);
+ this.entityIdPath = entityIdPath;
+ this.aggregateVersion = aggregateVersion;
+ }
+
+ /**
+ * Constructor with optional data.
+ *
+ * @param entityIdPath Path from root aggregate to target entity.
+ * @param aggregateVersion Expected aggregate version.
+ * @param correlationId Correlation ID.
+ * @param causationId ID of the event that caused this one.
+ */
+ public AbstractAggregateCommand(@NotNull final EntityIdPath entityIdPath, @Nullable final AggregateVersion aggregateVersion,
+ @Nullable final EventId correlationId, @Nullable final EventId causationId) {
+ super(correlationId, causationId);
+ Contract.requireArgNotNull("entityIdPath", entityIdPath);
+ this.entityIdPath = entityIdPath;
+ this.aggregateVersion = aggregateVersion;
+ }
+
+ @Override
+ @NotNull
+ public final EntityIdPath getEntityIdPath() {
+ return entityIdPath;
+ }
+
+ @Override
+ @NotNull
+ public final ENTITY_ID getEntityId() {
+ return entityIdPath.last();
+ }
+
+ @Override
+ @NotNull
+ public final ROOT_ID getAggregateRootId() {
+ return entityIdPath.first();
+ }
+
+ @Override
+ @Nullable
+ public final AggregateVersion getAggregateVersion() {
+ return aggregateVersion;
+ }
+
+ @Override
+ @Nullable
+ public final Integer getAggregateVersionInteger() {
+ if (aggregateVersion == null) {
+ return null;
+ }
+ return aggregateVersion.asBaseType();
+ }
+
+ /**
+ * Base class for event builders.
+ *
+ * @param Type of the aggregate identifier.
+ * @param Type of the entity identifier.
+ * @param Type of the event.
+ * @param Type of the builder.
+ */
+ protected abstract static class Builder, BUILDER extends AbstractCommand.Builder>
+ extends AbstractCommand.Builder {
+
+ private AbstractAggregateCommand delegate;
+
+ /**
+ * Constructor with event.
+ *
+ * @param delegate Event to populate with data.
+ */
+ public Builder(final TYPE delegate) {
+ super(delegate);
+ this.delegate = delegate;
+ }
+
+ /**
+ * Sets the identifier path from aggregate root to the entity that emitted the event.
+ *
+ * @param entityIdPath Path of entity identifiers.
+ * @return This builder.
+ */
+ @SuppressWarnings("unchecked")
+ public final BUILDER entityIdPath(@NotNull final EntityIdPath entityIdPath) {
+ Contract.requireArgNotNull("entityIdPath", entityIdPath);
+ delegate.entityIdPath = entityIdPath;
+ return (BUILDER) this;
+ }
+
+ /**
+ * Convenience method to set the entity identifier path if the path has only the aggregate root identifier.
+ *
+ * @param id Aggregate root identifier that will be used to create the entity id path.
+ * @return This builder.
+ */
+ @SuppressWarnings("unchecked")
+ public final BUILDER entityIdPath(@NotNull AggregateRootId id) {
+ Contract.requireArgNotNull("id", id);
+ delegate.entityIdPath = new EntityIdPath(id);
+ return (BUILDER) this;
+ }
+
+ /**
+ * Sets the expected aggregate version.
+ *
+ * @param aggregateVersion Expected aggregate version.
+ * @return This builder.
+ */
+ @SuppressWarnings("unchecked")
+ public final BUILDER aggregateVersion(@Nullable final AggregateVersion aggregateVersion) {
+ delegate.aggregateVersion = aggregateVersion;
+ return (BUILDER) this;
+ }
+
+ /**
+ * Ensures that everything is set up for building the object or throws a runtime exception otherwise.
+ */
+ protected final void ensureBuildableAbstractAggregateCommand() {
+ ensureBuildableAbstractCommand();
+ ensureNotNull("entityIdPath", delegate.entityIdPath);
+ }
+
+ /**
+ * Sets the internal instance to a new one. This must be called within the build method.
+ *
+ * @param delegate Delegate to use.
+ */
+ protected final void resetAbstractAggregateCommand(final TYPE delegate) {
+ resetAbstractCommand(delegate);
+ this.delegate = delegate;
+ }
+
+ }
+
+}
diff --git a/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractCommand.java b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractCommand.java
new file mode 100644
index 0000000..2c3a147
--- /dev/null
+++ b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractCommand.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jaxb;
+
+import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import org.fuin.cqrs4j.core.Command;
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventId;
+import org.fuin.ddd4j.jaxb.AbstractEvent;
+
+import java.io.Serial;
+
+/**
+ * Base class for all commands.
+ */
+public abstract class AbstractCommand extends AbstractEvent implements Command {
+
+ @Serial
+ private static final long serialVersionUID = 1000L;
+
+ /**
+ * Default constructor.
+ */
+ public AbstractCommand() {
+ super();
+ }
+
+ /**
+ * Constructor with event this one responds to. Convenience method to set the correlation and causation identifiers correctly.
+ *
+ * @param respondTo
+ * Causing event.
+ */
+ public AbstractCommand(@NotNull final Event respondTo) {
+ super(respondTo);
+ }
+
+ /**
+ * Constructor with optional data.
+ *
+ * @param correlationId
+ * Correlation ID.
+ * @param causationId
+ * ID of the event that caused this one.
+ */
+ public AbstractCommand(@Nullable final EventId correlationId, @Nullable final EventId causationId) {
+ super(correlationId, causationId);
+ }
+
+ /**
+ * Base class for event builders.
+ *
+ * @param
+ * Type of the entity identifier.
+ * @param
+ * Type of the event.
+ * @param
+ * Type of the builder.
+ */
+ protected abstract static class Builder>
+ extends AbstractEvent.Builder {
+
+ /**
+ * Constructor with event.
+ *
+ * @param delegate
+ * Event to populate with data.
+ */
+ public Builder(final TYPE delegate) {
+ super(delegate);
+ }
+
+ /**
+ * Ensures that everything is set up for building the object or throws a runtime exception otherwise.
+ */
+ protected final void ensureBuildableAbstractCommand() {
+ ensureBuildableAbstractEvent();
+ }
+
+ /**
+ * Sets the internal instance to a new one. This must be called within the build method.
+ *
+ * @param delegate
+ * Delegate to use.
+ */
+ protected final void resetAbstractCommand(final TYPE delegate) {
+ resetAbstractEvent(delegate);
+ }
+
+ }
+
+}
diff --git a/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractResult.java b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractResult.java
new file mode 100644
index 0000000..ab90ef9
--- /dev/null
+++ b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/AbstractResult.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jaxb;
+
+import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import jakarta.xml.bind.annotation.XmlElement;
+import org.fuin.cqrs4j.core.Result;
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.objects4j.common.Contract;
+import org.fuin.objects4j.common.ExceptionShortIdentifable;
+import org.fuin.objects4j.ui.Label;
+import org.fuin.objects4j.ui.Prompt;
+import org.fuin.objects4j.ui.ShortLabel;
+import org.fuin.objects4j.ui.Tooltip;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Result of a request. The type signals if the execution was successful or not. In case the result is not {@link ResultType#OK}, the
+ * fields code and message should contain unique information to help the user identifying the cause of the problem. A result may carry some
+ * optional data.
+ *
+ * @param Type of data returned.
+ */
+public abstract class AbstractResult implements Result, Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1000L;
+
+ static final String TYPE_PROPERTY = "type";
+
+ static final String CODE_PROPERTY = "code";
+
+ static final String MESSAGE_PROPERTY = "message";
+
+ @Label("Result Type")
+ @ShortLabel("TYPE")
+ @Tooltip("Type of the result")
+ @Prompt("ERROR")
+ @NotNull
+ @XmlElement(name = TYPE_PROPERTY)
+ private ResultType type;
+
+ @Label("Result Code")
+ @ShortLabel("CODE")
+ @Tooltip("Code that uniquely identifies the result. Mostly used in case of warnings or errors.")
+ @Prompt("E00001")
+ @Nullable
+ @XmlElement(name = CODE_PROPERTY)
+ private String code;
+
+ @Label("Result Message")
+ @ShortLabel("MSG")
+ @Tooltip("Message that describes the result. Mostly used in case of warnings or errors.")
+ @Prompt("The field 'Xyz' is mandatory")
+ @Nullable
+ @XmlElement(name = MESSAGE_PROPERTY)
+ private String message;
+
+ /**
+ * Protected default constructor for de-serialization.
+ */
+ protected AbstractResult() { // NOSONAR Ignore uninitialized fields
+ super();
+ }
+
+ /**
+ * Constructor with all data.
+ *
+ * @param type Type.
+ * @param code Code.
+ * @param message Message.
+ */
+ public AbstractResult(@NotNull final ResultType type, @Nullable final String code, @Nullable final String message) {
+ Contract.requireArgNotNull("type", type);
+ this.type = type;
+ this.code = code;
+ this.message = message;
+ }
+
+ /**
+ * Constructor with exception. An exception of type {@link ExceptionShortIdentifable} will be used to fill the code field
+ * with the identifier value. If it's not a {@link ExceptionShortIdentifable} the code field will be set using the full
+ * qualified class name of the exception.
+ *
+ * @param exception The message for the result is equal to the exception message or the simple name of the exception class if the exception
+ * message is null.
+ */
+ public AbstractResult(@NotNull final Exception exception) {
+ super();
+ Contract.requireArgNotNull("exception", exception);
+ this.type = ResultType.ERROR;
+ if (exception instanceof ExceptionShortIdentifable) {
+ this.code = ((ExceptionShortIdentifable) exception).getShortId();
+ } else {
+ this.code = exception.getClass().getName();
+ }
+ if (exception.getMessage() == null) {
+ this.message = "";
+ } else {
+ this.message = exception.getMessage();
+ }
+ }
+
+ @Override
+ public final ResultType getType() {
+ return type;
+ }
+
+ @Override
+ public final String getCode() {
+ return code;
+ }
+
+ @Override
+ public final String getMessage() {
+ return message;
+ }
+
+}
diff --git a/src/main/java/org/fuin/cqrs4j/DataResult.java b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/DataResult.java
similarity index 78%
rename from src/main/java/org/fuin/cqrs4j/DataResult.java
rename to jaxb/src/main/java/org/fuin/cqrs4j/jaxb/DataResult.java
index b267bce..04043cc 100644
--- a/src/main/java/org/fuin/cqrs4j/DataResult.java
+++ b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/DataResult.java
@@ -15,24 +15,25 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.jaxb;
-import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.annotation.Nullable;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.xml.bind.annotation.XmlAnyElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlTransient;
-
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.ddd4j.core.ExceptionData;
import org.fuin.objects4j.common.Contract;
-import org.fuin.objects4j.common.ExceptionShortIdentifable;
import org.fuin.objects4j.common.MarshalInformation;
-import org.fuin.objects4j.common.Nullable;
import org.fuin.objects4j.ui.Label;
import org.fuin.objects4j.ui.Prompt;
import org.fuin.objects4j.ui.ShortLabel;
import org.fuin.objects4j.ui.Tooltip;
+import java.io.Serial;
+
/**
* Result of a request that contains data in addition to the standard result fields. The type signals if the execution was successful or
* not. In case the the result is not {@link ResultType#OK}, the fields code and message should contain unique information to help the user
@@ -44,17 +45,16 @@
@XmlRootElement(name = "result")
public final class DataResult extends AbstractResult {
+ @Serial
private static final long serialVersionUID = 1000L;
static final String DATA_CLASS_PROPERTY = "data-class";
static final String DATA_ELEMENT_PROPERTY = "data-element";
- @JsonbProperty(DATA_CLASS_PROPERTY)
@XmlTransient
private String dataClass;
- @JsonbProperty(DATA_ELEMENT_PROPERTY)
@XmlTransient
private String dataElement;
@@ -64,6 +64,7 @@ public final class DataResult extends AbstractResult {
@Prompt("Optional Data")
@Valid
@XmlAnyElement(lax = true)
+ @SuppressWarnings("java:S1948") // We assume the unknown data is serializable
private Object data;
/**
@@ -86,7 +87,7 @@ protected DataResult() { // NOSONAR Ignore uninitialized fields
* Optional result data.
*/
public DataResult(@NotNull final ResultType type, @Nullable final String code, @Nullable final String message,
- @Nullable final DATA data) {
+ @Nullable final DATA data) {
super(type, code, message);
if (data instanceof MarshalInformation) {
final MarshalInformation mui = (MarshalInformation) data;
@@ -128,34 +129,20 @@ public DataResult(@NotNull final ResultType type, @Nullable final String code, @
}
/**
- * Constructor with exception. If the exception is type {@link MarshalInformation} then it will be used as data
- * field, if not data will be null. An exception of type {@link ExceptionShortIdentifable} will be used to fill the
- * code field with the identifier value. If it's not a {@link ExceptionShortIdentifable} the code field will
- * be set using the full qualified class name of the exception.
- *
- * @param exception
- * The message for the result is equal to the exception message or the simple name of the exception class if the exception
- * message is null.
+ * Constructor with exception data.
+ *
+ * @param exceptionData .
*/
- // CHECKSTYLE:OFF:AvoidInlineConditionals
- public DataResult(@NotNull final Exception exception) {
- // CHECKSTYLE:ON
- super(exception);
- if (exception instanceof MarshalInformation) {
- final MarshalInformation mui = (MarshalInformation) exception;
- this.data = mui.getData();
- this.dataClass = mui.getDataClass().getName();
- this.dataElement = mui.getDataElement();
- } else {
- this.data = null;
- this.dataClass = null;
- this.dataElement = null;
- }
+ public DataResult(@NotNull final ExceptionData extends Exception> exceptionData) {
+ super(exceptionData.toException());
+ this.data = exceptionData;
+ this.dataClass = exceptionData.getClass().getName();
+ this.dataElement = exceptionData.getDataElement();
}
/**
* Returns the name of the class contained in the data element.
- *
+ *
* @return Full qualified class name.
*/
public final String getDataClass() {
@@ -164,7 +151,7 @@ public final String getDataClass() {
/**
* Returns the name of the data attribute.
- *
+ *
* @return Data element name.
*/
public final String getDataElement() {
@@ -173,7 +160,7 @@ public final String getDataElement() {
/**
* Returns the result data.
- *
+ *
* @return Response data.
*/
@SuppressWarnings("unchecked")
@@ -183,13 +170,12 @@ public final DATA getData() {
}
@Override
- // CHECKSTYLE:OFF Generated code
public final int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getCode() == null) ? 0 : getCode().hashCode());
result = prime * result + ((getMessage() == null) ? 0 : getMessage().hashCode());
- result = prime * result + ((getType() == null) ? 0 : getType().hashCode());
+ result = prime * result + getType().hashCode();
result = prime * result + ((dataClass == null) ? 0 : dataClass.hashCode());
result = prime * result + ((dataElement == null) ? 0 : dataElement.hashCode());
result = prime * result + ((data == null) ? 0 : data.hashCode());
@@ -249,7 +235,6 @@ public final boolean equals(final Object obj) {
return true;
}
- // CHECKSTYLE:ON
@Override
public final String toString() {
@@ -259,7 +244,7 @@ public final String toString() {
/**
* Create a success result without any data.
- *
+ *
* @return Result with type {@link ResultType#OK}.
*/
public static DataResult ok() {
@@ -268,14 +253,10 @@ public static DataResult ok() {
/**
* Create a success result with some data.
- *
- * @param data
- * Optional data.
- *
+ *
+ * @param data Optional data.
+ * @param Type of data.
* @return Result with type {@link ResultType#OK}.
- *
- * @param
- * Type of data.
*/
public static DataResult ok(@Nullable final T data) {
return new DataResult<>(ResultType.OK, null, null, data);
@@ -283,16 +264,11 @@ public static DataResult ok(@Nullable final T data) {
/**
* Create a success result with some data.
- *
- * @param data
- * Optional data.
- * @param dataElement
- * Optional name of the data element.
- *
+ *
+ * @param data Optional data.
+ * @param dataElement Optional name of the data element.
+ * @param Type of data.
* @return Result with type {@link ResultType#OK}.
- *
- * @param
- * Type of data.
*/
public static DataResult ok(@Nullable final T data, final String dataElement) {
return new DataResult<>(ResultType.OK, null, null, data, dataElement);
@@ -300,16 +276,11 @@ public static DataResult ok(@Nullable final T data, final String dataElem
/**
* Create an error result without any data.
- *
- * @param code
- * Code.
- * @param message
- * Message.
- *
+ *
+ * @param code Code.
+ * @param message Message.
+ * @param Not used.
* @return Error result with.
- *
- * @param
- * Not used.
*/
public static DataResult error(@NotNull final String code, @NotNull final String message) {
Contract.requireArgNotNull("code", code);
diff --git a/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/SimpleResult.java b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/SimpleResult.java
new file mode 100644
index 0000000..cbd2505
--- /dev/null
+++ b/jaxb/src/main/java/org/fuin/cqrs4j/jaxb/SimpleResult.java
@@ -0,0 +1,165 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jaxb;
+
+import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import org.fuin.cqrs4j.core.ResultType;
+import org.fuin.objects4j.common.Contract;
+import org.fuin.objects4j.common.ExceptionShortIdentifable;
+
+import java.io.Serial;
+
+/**
+ * Result of a request. The type signals if the execution was successful or not. In case the result is not {@link ResultType#OK}, the
+ * fields code and message should contain unique information to help the user identifying the cause of the problem. A simple result does not
+ * carry any additional data.
+ */
+@XmlRootElement(name = "result")
+public final class SimpleResult extends AbstractResult {
+
+ @Serial
+ private static final long serialVersionUID = 1000L;
+
+ /**
+ * Protected default constructor for de-serialization.
+ */
+ protected SimpleResult() { // NOSONAR Ignore uninitialized fields
+ super();
+ }
+
+ /**
+ * Constructor with all data.
+ *
+ * @param type
+ * Type.
+ * @param code
+ * Code.
+ * @param message
+ * Message.
+ */
+ public SimpleResult(@NotNull final ResultType type, @Nullable final String code, @Nullable final String message) {
+ super(type, code, message);
+ }
+
+ /**
+ * Constructor with exception. An exception of type {@link ExceptionShortIdentifable} will be used to fill the code field
+ * with the identifier value. If it's not a {@link ExceptionShortIdentifable} the code field will be set using the full
+ * qualified class name of the exception.
+ *
+ * @param exception
+ * The message for the result is equal to the exception message or the simple name of the exception class if the exception
+ * message is null.
+ */
+ public SimpleResult(@NotNull final Exception exception) {
+ super(exception);
+ }
+
+ @Override
+ public Void getData() {
+ return null;
+ }
+
+ @Override
+ public final int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((getCode() == null) ? 0 : getCode().hashCode());
+ result = prime * result + ((getMessage() == null) ? 0 : getMessage().hashCode());
+ result = prime * result + getType().hashCode();
+ return result;
+ }
+
+ @Override
+ public final boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final SimpleResult other = (SimpleResult) obj;
+ if (getCode() == null) {
+ if (other.getCode() != null) {
+ return false;
+ }
+ } else if (!getCode().equals(other.getCode())) {
+ return false;
+ }
+ if (getMessage() == null) {
+ if (other.getMessage() != null) {
+ return false;
+ }
+ } else if (!getMessage().equals(other.getMessage())) {
+ return false;
+ }
+ return getType() == other.getType();
+ }
+
+
+ @Override
+ public final String toString() {
+ return "Result [type=" + getType() + ", code=" + getCode() + ", message=" + getMessage() + "]";
+ }
+
+ /**
+ * Create a success result.
+ *
+ * @return Result with type {@link ResultType#OK}.
+ */
+ public static SimpleResult ok() {
+ return new SimpleResult(ResultType.OK, null, null);
+ }
+
+ /**
+ * Create a warning result.
+ *
+ * @param code
+ * Code.
+ * @param message
+ * Message.
+ *
+ * @return Result with type {@link ResultType#WARNING}.
+ */
+ public static SimpleResult warning(@NotNull final String code, @NotNull final String message) {
+ Contract.requireArgNotNull("code", code);
+ Contract.requireArgNotNull("message", message);
+ return new SimpleResult(ResultType.WARNING, code, message);
+ }
+
+ /**
+ * Create an error result.
+ *
+ * @param code
+ * Code.
+ * @param message
+ * Message.
+ *
+ * @return Result with type {@link ResultType#ERROR}.
+ */
+ public static SimpleResult error(@NotNull final String code, @NotNull final String message) {
+ Contract.requireArgNotNull("code", code);
+ Contract.requireArgNotNull("message", message);
+ return new SimpleResult(ResultType.ERROR, code, message);
+ }
+
+}
diff --git a/jaxb/src/test/java/org/fuin/cqrs4j/jaxb/ACreatedEvent.java b/jaxb/src/test/java/org/fuin/cqrs4j/jaxb/ACreatedEvent.java
new file mode 100644
index 0000000..9526395
--- /dev/null
+++ b/jaxb/src/test/java/org/fuin/cqrs4j/jaxb/ACreatedEvent.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jaxb;
+
+import org.fuin.ddd4j.core.EntityIdPath;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.ddd4j.jaxb.AbstractDomainEvent;
+import org.fuin.esc.api.HasSerializedDataTypeConstant;
+import org.fuin.esc.api.SerializedDataType;
+import org.fuin.esc.api.TypeName;
+import org.fuin.utils4j.TestOmitted;
+
+import java.io.Serial;
+
+@TestOmitted("This is only a test class")
+@HasSerializedDataTypeConstant
+public class ACreatedEvent extends AbstractDomainEvent {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /** Unique name of the event. */
+ public static final TypeName TYPE = new TypeName("ACreatedEvent");
+
+ /** Unique name of the serialized event. */
+ public static final SerializedDataType SER_TYPE = new SerializedDataType(TYPE.asBaseType());
+
+ private static final EventType EVENT_TYPE = new EventType(TYPE.asBaseType());
+
+ private AId id;
+
+ public ACreatedEvent(final AId id) {
+ super(new EntityIdPath(id));
+ this.id = id;
+ }
+
+ public AId getId() {
+ return id;
+ }
+
+ @Override
+ public EventType getEventType() {
+ return EVENT_TYPE;
+ }
+
+}
diff --git a/src/test/java/org/fuin/cqrs4j/AId.java b/jaxb/src/test/java/org/fuin/cqrs4j/jaxb/AId.java
similarity index 83%
rename from src/test/java/org/fuin/cqrs4j/AId.java
rename to jaxb/src/test/java/org/fuin/cqrs4j/jaxb/AId.java
index a197249..e64b926 100644
--- a/src/test/java/org/fuin/cqrs4j/AId.java
+++ b/jaxb/src/test/java/org/fuin/cqrs4j/jaxb/AId.java
@@ -1,58 +1,61 @@
-/**
- * Copyright (C) 2015 Michael Schnell. All rights reserved.
- * http://www.fuin.org/
- *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option) any
- * later version.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library. If not, see http://www.gnu.org/licenses/.
- */
-package org.fuin.cqrs4j;
-
-import org.fuin.ddd4j.ddd.AggregateRootId;
-import org.fuin.ddd4j.ddd.EntityType;
-import org.fuin.ddd4j.ddd.StringBasedEntityType;
-
-//CHECKSTYLE:OFF
-public class AId implements AggregateRootId {
-
- private static final long serialVersionUID = 1L;
-
- public static final EntityType TYPE = new StringBasedEntityType("A");
-
- private final long id;
-
- public AId(final long id) {
- this.id = id;
- }
-
- @Override
- public EntityType getType() {
- return TYPE;
- }
-
- @Override
- public String asString() {
- return "" + id;
- }
-
- @Override
- public String asTypedString() {
- return getType() + " " + asString();
- }
-
- @Override
- public String toString() {
- return "AId [id=" + id + "]";
- }
-
-}
-// CHECKSTYLE:ON
+/**
+ * Copyright (C) 2015 Michael Schnell. All rights reserved.
+ * http://www.fuin.org/
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see http://www.gnu.org/licenses/.
+ */
+package org.fuin.cqrs4j.jaxb;
+
+import org.fuin.ddd4j.core.AggregateRootId;
+import org.fuin.ddd4j.core.EntityType;
+import org.fuin.ddd4j.core.StringBasedEntityType;
+import org.fuin.utils4j.TestOmitted;
+
+import java.io.Serial;
+
+@TestOmitted("This is only a test class")
+public class AId implements AggregateRootId {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public static final EntityType TYPE = new StringBasedEntityType("A");
+
+ private final long id;
+
+ public AId(final long id) {
+ this.id = id;
+ }
+
+ @Override
+ public EntityType getType() {
+ return TYPE;
+ }
+
+ @Override
+ public String asString() {
+ return "" + id;
+ }
+
+ @Override
+ public String asTypedString() {
+ return getType() + " " + asString();
+ }
+
+ @Override
+ public String toString() {
+ return "AId [id=" + id + "]";
+ }
+
+}
diff --git a/src/test/java/org/fuin/cqrs4j/AbstractAggregateCommandTest.java b/jaxb/src/test/java/org/fuin/cqrs4j/jaxb/AbstractAggregateCommandTest.java
similarity index 89%
rename from src/test/java/org/fuin/cqrs4j/AbstractAggregateCommandTest.java
rename to jaxb/src/test/java/org/fuin/cqrs4j/jaxb/AbstractAggregateCommandTest.java
index f31aef1..761e921 100644
--- a/src/test/java/org/fuin/cqrs4j/AbstractAggregateCommandTest.java
+++ b/jaxb/src/test/java/org/fuin/cqrs4j/jaxb/AbstractAggregateCommandTest.java
@@ -1,31 +1,33 @@
-package org.fuin.cqrs4j;
+package org.fuin.cqrs4j.jaxb;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.fuin.utils4j.jaxb.JaxbUtils.marshal;
-import static org.fuin.utils4j.jaxb.JaxbUtils.unmarshal;
-import static org.fuin.utils4j.Utils4J.deserialize;
-import static org.fuin.utils4j.Utils4J.serialize;
+import jakarta.validation.Validation;
+import jakarta.xml.bind.Marshaller;
+import jakarta.xml.bind.Unmarshaller;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.adapters.XmlAdapter;
+import org.fuin.ddd4j.core.AggregateVersion;
+import org.fuin.ddd4j.core.EntityId;
+import org.fuin.ddd4j.core.EntityIdFactory;
+import org.fuin.ddd4j.core.EntityIdPath;
+import org.fuin.ddd4j.core.Event;
+import org.fuin.ddd4j.core.EventId;
+import org.fuin.ddd4j.core.EventType;
+import org.fuin.ddd4j.jaxb.AbstractEvent;
+import org.fuin.ddd4j.jaxb.EntityIdPathXmlAdapter;
+import org.fuin.objects4j.common.Contract;
+import org.fuin.utils4j.Utils4J;
+import org.fuin.utils4j.jaxb.JaxbUtils;
+import org.fuin.utils4j.jaxb.MarshallerBuilder;
+import org.fuin.utils4j.jaxb.UnmarshallerBuilder;
+import org.junit.jupiter.api.Test;
+import java.io.Serial;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.UUID;
-import jakarta.validation.Validation;
-import jakarta.xml.bind.annotation.XmlRootElement;
-import jakarta.xml.bind.annotation.adapters.XmlAdapter;
-
-import org.fuin.ddd4j.ddd.AbstractEvent;
-import org.fuin.ddd4j.ddd.AggregateVersion;
-import org.fuin.ddd4j.ddd.EntityId;
-import org.fuin.ddd4j.ddd.EntityIdFactory;
-import org.fuin.ddd4j.ddd.EntityIdPath;
-import org.fuin.ddd4j.ddd.EntityIdPathConverter;
-import org.fuin.ddd4j.ddd.Event;
-import org.fuin.ddd4j.ddd.EventId;
-import org.fuin.ddd4j.ddd.EventType;
-import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
-//CHECKSTYLE:OFF Test code
public class AbstractAggregateCommandTest {
private static final EventType MY_EVENT_TYPE = new EventType("MyEvent");
@@ -203,7 +205,7 @@ public final void testSerializeDeserialize() {
final MyCommand original = new MyCommand(entityIdPath, version, correlationId, causationId);
// TEST
- final MyCommand copy = deserialize(serialize(original));
+ final MyCommand copy = Utils4J.deserialize(Utils4J.serialize(original));
// VERIFY
assertThat(copy).isEqualTo(original);
@@ -229,8 +231,10 @@ public final void testMarshalUnmarshal() {
final MyCommand original = new MyCommand(entityIdPath, version, correlationId, causationId);
// TEST
- final String xml = marshal(original, createXmlAdapter(), MyCommand.class);
- final MyCommand copy = unmarshal(xml, createXmlAdapter(), MyCommand.class);
+ final Marshaller marshaller = new MarshallerBuilder().addClassesToBeBound(MyCommand.class).addAdapters(createXmlAdapter()).build();
+ final Unmarshaller unmarshaller = new UnmarshallerBuilder().addClassesToBeBound(MyCommand.class).addAdapters(createXmlAdapter()).build();
+ final String xml = JaxbUtils.marshal(marshaller, original);
+ final MyCommand copy = JaxbUtils.unmarshal(unmarshaller, xml);
// VERIFY
assertThat(copy).isEqualTo(original);
@@ -254,11 +258,13 @@ public final void testUnmarshal() {
+ "2a5893a9-00da-4003-b280-98324eccdef1"
+ "f13d3481-51b7-423f-8fe7-5c342f7d7c46";
+ final Unmarshaller unmarshaller = new UnmarshallerBuilder().addClassesToBeBound(MyCommand.class).addAdapters(createXmlAdapter()).build();
+
// TEST
- final MyCommand copy = unmarshal(xml, createXmlAdapter(), MyCommand.class);
+ final MyCommand copy = JaxbUtils.unmarshal(unmarshaller, xml);
// VERIFY
- Cqrs4JUtils.verifyPrecondition(Validation.buildDefaultValidatorFactory().getValidator(), copy);
+ Contract.requireValid(Validation.buildDefaultValidatorFactory().getValidator(), copy);
assertThat(copy.getEntityIdPath()).isEqualTo(new EntityIdPath(new AId(1L), new BId(2L), new CId(3L)));
assertThat(copy.getAggregateVersion()).isEqualTo(new AggregateVersion(1));
assertThat(copy.getCausationId()).isEqualTo(new EventId(UUID.fromString("f13d3481-51b7-423f-8fe7-5c342f7d7c46")));
@@ -279,11 +285,13 @@ public final void testUnmarshalNullVersion() {
+ "2a5893a9-00da-4003-b280-98324eccdef1"
+ "f13d3481-51b7-423f-8fe7-5c342f7d7c46";
+ final Unmarshaller unmarshaller = new UnmarshallerBuilder().addClassesToBeBound(MyCommand.class).addAdapters(createXmlAdapter()).build();
+
// TEST
- final MyCommand copy = unmarshal(xml, createXmlAdapter(), MyCommand.class);
+ final MyCommand copy = JaxbUtils.unmarshal(unmarshaller, xml);
// VERIFY
- Cqrs4JUtils.verifyPrecondition(Validation.buildDefaultValidatorFactory().getValidator(), copy);
+ Contract.requireValid(Validation.buildDefaultValidatorFactory().getValidator(), copy);
assertThat(copy.getEntityIdPath()).isEqualTo(new EntityIdPath(new AId(1L), new BId(2L), new CId(3L)));
assertThat(copy.getAggregateVersion()).isNull();
assertThat(copy.getCausationId()).isEqualTo(new EventId(UUID.fromString("f13d3481-51b7-423f-8fe7-5c342f7d7c46")));
@@ -297,6 +305,7 @@ public final void testUnmarshalNullVersion() {
@XmlRootElement(name = "my-command")
public static class MyCommand extends AbstractAggregateCommand {
+ @Serial
private static final long serialVersionUID = 1L;
public MyCommand() {
@@ -344,6 +353,7 @@ public MyCommand build() {
@XmlRootElement(name = "my-command-2")
public static class MyCommand2 extends AbstractAggregateCommand {
+ @Serial
private static final long serialVersionUID = 1L;
public MyCommand2() {
@@ -372,6 +382,7 @@ public EventType getEventType() {
@XmlRootElement(name = "my-command-3")
public static class MyCommand3 extends AbstractAggregateCommand