diff --git a/quarkus/command/mvnw b/quarkus/command/mvnw new file mode 100755 index 0000000..d2f0ea3 --- /dev/null +++ b/quarkus/command/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/quarkus/command/mvnw.cmd b/quarkus/command/mvnw.cmd new file mode 100644 index 0000000..b26ab24 --- /dev/null +++ b/quarkus/command/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/quarkus/command/pom.xml b/quarkus/command/pom.xml new file mode 100644 index 0000000..dbf5123 --- /dev/null +++ b/quarkus/command/pom.xml @@ -0,0 +1,247 @@ + + + + 4.0.0 + + org.fuin.cqrs4j.example.quarkus + cqrs4j-quarkus-example-command + 1.0-SNAPSHOT + + + 3.8.1 + true + 1.8 + 1.8 + UTF-8 + UTF-8 + 1.0.1.Final + quarkus-universe-bom + io.quarkus + 1.0.1.Final + 2.22.1 + 0.3.1-SNAPSHOT + + + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + + + + + + io.quarkus + quarkus-hibernate-validator + + + + io.quarkus + quarkus-resteasy + + + + io.quarkus + quarkus-resteasy-jsonb + + + + io.quarkus + quarkus-resteasy-jackson + + + + jakarta.security.jacc + jakarta.security.jacc-api + + + + io.quarkus + quarkus-smallrye-context-propagation + + + + io.quarkus + quarkus-mailer + + + + org.fuin.cqrs4j.example.quarkus + cqrs4j-quarkus-example-shared + 0.1.0-SNAPSHOT + + + logback-classic + ch.qos.logback + + + + + + org.fuin.esc + esc-esjc + ${esc.version} + + + + org.fuin + cqrs-4-java + 0.2.1-SNAPSHOT + + + + javax.mail + mail + 1.4.7 + + + + + + io.quarkus + quarkus-junit5 + test + + + + io.rest-assured + rest-assured + test + + + + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus-plugin.version} + + + + build + + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jboss.jandex + jandex-maven-plugin + [1.0.7,) + + jandex + + + + + + + + + + + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + native + + + + + + + + + sonatype.oss.snapshots + Sonatype OSS Snapshot Repository + https://oss.sonatype.org/content/repositories/snapshots + + false + + + always + true + + + + + + diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateAlreadyExistsExceptionMapper.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateAlreadyExistsExceptionMapper.java new file mode 100644 index 0000000..948a0a6 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateAlreadyExistsExceptionMapper.java @@ -0,0 +1,47 @@ +/** + * 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.example.quarkus.command.api; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.fuin.cqrs4j.SimpleResult; +import org.fuin.ddd4j.ddd.AggregateAlreadyExistsException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Maps the exceptions into a HTTP status. + */ +@Provider +public class AggregateAlreadyExistsExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(AggregateAlreadyExistsExceptionMapper.class); + + @Context + private HttpHeaders headers; + + @Override + public Response toResponse(final AggregateAlreadyExistsException ex) { + + LOG.info("{} {}", ex.getShortId(), ex.getMessage()); + + return Response.status(Status.CONFLICT).entity(SimpleResult.error(ex.getShortId(), ex.getMessage())) + .type(headers.getMediaType()).build(); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateDeletedExceptionMapper.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateDeletedExceptionMapper.java new file mode 100644 index 0000000..692bea4 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateDeletedExceptionMapper.java @@ -0,0 +1,47 @@ +/** + * 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.example.quarkus.command.api; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.fuin.cqrs4j.SimpleResult; +import org.fuin.ddd4j.ddd.AggregateDeletedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Maps the exceptions into a HTTP status. + */ +@Provider +public class AggregateDeletedExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(AggregateDeletedExceptionMapper.class); + + @Context + private HttpHeaders headers; + + @Override + public Response toResponse(final AggregateDeletedException ex) { + + LOG.info("{} {}", ex.getShortId(), ex.getMessage()); + + return Response.status(Status.GONE).entity(SimpleResult.error(ex.getShortId(), ex.getMessage())) + .type(headers.getMediaType()).build(); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateNotFoundExceptionMapper.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateNotFoundExceptionMapper.java new file mode 100644 index 0000000..9e37eee --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateNotFoundExceptionMapper.java @@ -0,0 +1,47 @@ +/** + * 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.example.quarkus.command.api; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.fuin.cqrs4j.SimpleResult; +import org.fuin.ddd4j.ddd.AggregateNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Maps the exceptions into a HTTP status. + */ +@Provider +public class AggregateNotFoundExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(AggregateNotFoundExceptionMapper.class); + + @Context + private HttpHeaders headers; + + @Override + public Response toResponse(final AggregateNotFoundException ex) { + + LOG.info("{} {}", ex.getShortId(), ex.getMessage()); + + return Response.status(Status.NOT_FOUND).entity(SimpleResult.error(ex.getShortId(), ex.getMessage())) + .type(headers.getMediaType()).build(); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateVersionConflictExceptionMapper.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateVersionConflictExceptionMapper.java new file mode 100644 index 0000000..a63c9aa --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateVersionConflictExceptionMapper.java @@ -0,0 +1,47 @@ +/** + * 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.example.quarkus.command.api; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.fuin.cqrs4j.SimpleResult; +import org.fuin.ddd4j.ddd.AggregateVersionConflictException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Maps the exceptions into a HTTP status. + */ +@Provider +public class AggregateVersionConflictExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(AggregateVersionConflictExceptionMapper.class); + + @Context + private HttpHeaders headers; + + @Override + public Response toResponse(final AggregateVersionConflictException ex) { + + LOG.info("{} {}", ex.getShortId(), ex.getMessage()); + + return Response.status(Status.CONFLICT).entity(SimpleResult.error(ex.getShortId(), ex.getMessage())) + .type(headers.getMediaType()).build(); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateVersionNotFoundExceptionMapper.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateVersionNotFoundExceptionMapper.java new file mode 100644 index 0000000..a3ae7b2 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/AggregateVersionNotFoundExceptionMapper.java @@ -0,0 +1,47 @@ +/** + * 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.example.quarkus.command.api; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.fuin.cqrs4j.SimpleResult; +import org.fuin.ddd4j.ddd.AggregateVersionNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Maps the exceptions into a HTTP status. + */ +@Provider +public class AggregateVersionNotFoundExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(AggregateVersionNotFoundExceptionMapper.class); + + @Context + private HttpHeaders headers; + + @Override + public Response toResponse(final AggregateVersionNotFoundException ex) { + + LOG.error("{} {}", ex.getShortId(), ex.getMessage()); + + return Response.status(Status.NOT_FOUND).entity(SimpleResult.error(ex.getShortId(), ex.getMessage())) + .type(headers.getMediaType()).build(); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/CommandExecutionFailedExceptionMapper.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/CommandExecutionFailedExceptionMapper.java new file mode 100644 index 0000000..e65fa6d --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/CommandExecutionFailedExceptionMapper.java @@ -0,0 +1,56 @@ +/** + * 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.example.quarkus.command.api; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.fuin.cqrs4j.CommandExecutionFailedException; +import org.fuin.cqrs4j.SimpleResult; +import org.fuin.objects4j.common.ExceptionShortIdentifable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Maps the exceptions into a HTTP status. + */ +@Provider +public class CommandExecutionFailedExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(CommandExecutionFailedExceptionMapper.class); + + @Context + private HttpHeaders headers; + + @Override + public Response toResponse(final CommandExecutionFailedException ex) { + + final String shortId; + if (ex.getCause() instanceof ExceptionShortIdentifable) { + final ExceptionShortIdentifable esi = (ExceptionShortIdentifable) ex.getCause(); + shortId = esi.getShortId(); + } else { + shortId = ex.getCause().getClass().getName(); + } + + LOG.info("{} {}", shortId, ex.getCause().getMessage()); + + return Response.status(Status.BAD_REQUEST).entity(SimpleResult.error(shortId, ex.getMessage())).type(headers.getMediaType()) + .build(); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/ConstraintViolationExceptionMapper.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/ConstraintViolationExceptionMapper.java new file mode 100644 index 0000000..07dcebb --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/ConstraintViolationExceptionMapper.java @@ -0,0 +1,68 @@ +/** + * 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.example.quarkus.command.api; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.fuin.cqrs4j.SimpleResult; +import org.fuin.objects4j.common.Contract; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Maps the exceptions into a HTTP status. + */ +@Provider +public class ConstraintViolationExceptionMapper implements ExceptionMapper { + + private static final String CONSTRAINT_VIOLATION = "CONSTRAINT_VIOLATION"; + + private static final Logger LOG = LoggerFactory.getLogger(ConstraintViolationExceptionMapper.class); + + @Context + private HttpHeaders headers; + + @Override + public Response toResponse(final ConstraintViolationException ex) { + + LOG.info("{} {}", CONSTRAINT_VIOLATION, "ConstraintViolationException"); + + return Response.status(Status.BAD_REQUEST).entity(SimpleResult.error(CONSTRAINT_VIOLATION, asString(ex.getConstraintViolations()))) + .build(); + + } + + private static String asString(@Nullable final Set> constraintViolations) { + if (constraintViolations == null || constraintViolations.size() == 0) { + return ""; + } + final List list = new ArrayList<>(); + for (final ConstraintViolation constraintViolation : constraintViolations) { + list.add(Contract.asString(constraintViolation)); + } + return list.toString(); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/PersonResource.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/PersonResource.java new file mode 100644 index 0000000..6db13af --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/api/PersonResource.java @@ -0,0 +1,80 @@ +/** + * 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.example.quarkus.command.api; + +import java.util.Set; + +import javax.inject.Inject; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.fuin.cqrs4j.CommandExecutionFailedException; +import org.fuin.cqrs4j.SimpleResult; +import org.fuin.cqrs4j.example.quarkus.command.domain.DuplicatePersonNameException; +import org.fuin.cqrs4j.example.quarkus.command.domain.Person; +import org.fuin.cqrs4j.example.quarkus.command.domain.PersonRepository; +import org.fuin.cqrs4j.example.quarkus.shared.CreatePersonCommand; +import org.fuin.ddd4j.ddd.AggregateAlreadyExistsException; +import org.fuin.ddd4j.ddd.AggregateDeletedException; + +@Path("/persons") +public class PersonResource { + + @Inject + PersonRepository repo; + + @Inject + Validator validator; + + @Context + UriInfo uriInfo; + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("/create") + public Response hello(final CreatePersonCommand cmd) + throws AggregateAlreadyExistsException, AggregateDeletedException, CommandExecutionFailedException { + + // Verify preconditions + final Set> violations = validator.validate(cmd); + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + + try { + // Create aggregate + final Person person = new Person(cmd.getAggregateRootId(), cmd.getName(), (name) -> { + // TODO Execute a call to the query side to verify if the name already exists + return null; + }); + repo.add(person); + + // Send OK response + return Response.ok(SimpleResult.ok()).build(); + } catch (final DuplicatePersonNameException ex) { + throw new CommandExecutionFailedException(ex); + } + + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/Config.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/Config.java new file mode 100644 index 0000000..df5ae7f --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/Config.java @@ -0,0 +1,148 @@ +/** + * 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.example.quarkus.command.app; + +import java.net.MalformedURLException; +import java.net.URL; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * Application configuration. + */ +@ApplicationScoped +public class Config { + + private static final String EVENT_STORE_HOST = "127.0.0.1"; + + private static final int EVENT_STORE_HTTP_PORT = 2113; + + private static final int EVENT_STORE_TCP_PORT = 1113; + + private static final String EVENT_STORE_USER = "admin"; + + private static final String EVENT_STORE_PASSWORD = "changeit"; + + @Inject + @ConfigProperty(name = "EVENT_STORE_HOST", defaultValue = EVENT_STORE_HOST) + String eventStoreHost; + + @Inject + @ConfigProperty(name = "EVENT_STORE_HTTP_PORT", defaultValue = "" + EVENT_STORE_HTTP_PORT) + int eventStoreHttpPort; + + @Inject + @ConfigProperty(name = "EVENT_STORE_TCP_PORT", defaultValue = "" + EVENT_STORE_TCP_PORT) + int eventStoreTcpPort; + + @Inject + @ConfigProperty(name = "EVENT_STORE_USER", defaultValue = EVENT_STORE_USER) + String eventStoreUser; + + @Inject + @ConfigProperty(name = "EVENT_STORE_PASSWORD", defaultValue = EVENT_STORE_PASSWORD) + String eventStorePassword; + + /** + * Constructor using default values internally. + */ + public Config() { + super(); + this.eventStoreHost = EVENT_STORE_HOST; + this.eventStoreHttpPort = EVENT_STORE_HTTP_PORT; + this.eventStoreTcpPort = EVENT_STORE_TCP_PORT; + this.eventStoreUser = EVENT_STORE_USER; + this.eventStorePassword = EVENT_STORE_PASSWORD; + } + + /** + * Constructor with all data. + * + * @param eventStoreHost Host. + * @param eventStoreHttpPort HTTP port + * @param eventStoreTcpPort TCP port. + * @param eventStoreUser User. + * @param eventStorePassword Password. + */ + public Config(final String eventStoreHost, final int eventStoreHttpPort, final int eventStoreTcpPort, + final String eventStoreUser, final String eventStorePassword) { + super(); + this.eventStoreHost = eventStoreHost; + this.eventStoreHttpPort = eventStoreHttpPort; + this.eventStoreTcpPort = eventStoreTcpPort; + this.eventStoreUser = eventStoreUser; + this.eventStorePassword = eventStorePassword; + } + + /** + * Returns the host name of the event store. + * + * @return Name. + */ + public String getEventStoreHost() { + return eventStoreHost; + } + + /** + * Returns the HTTP port of the event store. + * + * @return Port. + */ + public int getEventStoreHttpPort() { + return eventStoreHttpPort; + } + + /** + * Returns the TCP port of the event store. + * + * @return Port. + */ + public int getEventStoreTcpPort() { + return eventStoreTcpPort; + } + + /** + * Returns the username of the event store. + * + * @return Username. + */ + public String getEventStoreUser() { + return eventStoreUser; + } + + /** + * Returns the password of the event store. + * + * @return Password. + */ + public String getEventStorePassword() { + return eventStorePassword; + } + + /** + * Creates a URL with parameters from the config. + * + * @return Event store base URL. + */ + public URL getEventStoreURL() { + try { + return new URL("http", eventStoreHost, eventStoreHttpPort, "/"); + } catch (final MalformedURLException ex) { + throw new RuntimeException("Failed to create event store URL", ex); + } + } + +} \ No newline at end of file diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/EsjcEventStoreFactory.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/EsjcEventStoreFactory.java new file mode 100644 index 0000000..f5b81b7 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/EsjcEventStoreFactory.java @@ -0,0 +1,37 @@ +/** + * 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.example.quarkus.command.app; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; + +import org.eclipse.microprofile.context.ManagedExecutor; + +import com.github.msemys.esjc.EventStore; +import com.github.msemys.esjc.EventStoreBuilder; + +/** + * CDI producer that creates a {@link EventStore}. + */ +@ApplicationScoped +public class EsjcEventStoreFactory { + + @Produces + @ApplicationScoped + public EventStore createESJC(final ManagedExecutor executor, final Config config) { + return EventStoreBuilder.newBuilder().singleNodeAddress(config.getEventStoreHost(), config.getEventStoreTcpPort()) + .executor(executor).userCredentials(config.getEventStoreUser(), config.getEventStorePassword()) + .build(); + } + +} \ No newline at end of file diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/EventStoreFactory.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/EventStoreFactory.java new file mode 100644 index 0000000..591c1b7 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/EventStoreFactory.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.example.quarkus.command.app; + +import java.nio.charset.Charset; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; + +import org.fuin.esc.api.EventStore; +import org.fuin.esc.esjc.ESJCEventStore; +import org.fuin.esc.spi.EnhancedMimeType; +import org.fuin.esc.spi.SerDeserializerRegistry; + +/** + * CDI factory that creates an event store connection and repositories. + */ +@ApplicationScoped +public class EventStoreFactory { + + /** + * Creates an event store.
+ *
+ * CAUTION: The returned event store instance is NOT thread safe. + * + * @param es Native event store API. + * @param registry Serialization registry. + * + * @return Dependent scope event store. + */ + @Produces + @RequestScoped + public EventStore createEventStore(final com.github.msemys.esjc.EventStore es, + final SerDeserializerRegistry registry) { + + final EventStore eventstore = new ESJCEventStore(es, registry, registry, + EnhancedMimeType.create("application", "json", Charset.forName("utf-8"))); + eventstore.open(); + return eventstore; + + } + + /** + * Closes the event store when the context is disposed. + * + * @param es Event store to close. + */ + public void closeEventStore(@Disposes final EventStore es) { + es.close(); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/JsonbFactory.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/JsonbFactory.java new file mode 100644 index 0000000..d192f75 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/JsonbFactory.java @@ -0,0 +1,44 @@ +/** + * 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.example.quarkus.command.app; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbConfig; + +import org.eclipse.yasson.FieldAccessStrategy; +import org.fuin.cqrs4j.example.quarkus.shared.SharedUtils; + +/** + * CDI factory that creates a JSON-B instance. + */ +@ApplicationScoped +public class JsonbFactory { + + /** + * Creates a JSON-B instance. + * + * @return Fully configured instance. + */ + @Produces + public Jsonb createJsonb() { + final JsonbConfig config = new JsonbConfig() + .withAdapters(SharedUtils.JSONB_ADAPTERS) + .withPropertyVisibilityStrategy(new FieldAccessStrategy()); + final Jsonb jsonb = JsonbBuilder.create(config); + return jsonb; + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/SerDeserializerRegistryFactory.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/SerDeserializerRegistryFactory.java new file mode 100644 index 0000000..74b126f --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/app/SerDeserializerRegistryFactory.java @@ -0,0 +1,47 @@ +/** + * 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.example.quarkus.command.app; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; + +import org.fuin.cqrs4j.example.quarkus.shared.SharedUtils; +import org.fuin.esc.spi.JsonbDeSerializer; +import org.fuin.esc.spi.SerDeserializerRegistry; +import org.fuin.esc.spi.SerializedDataTypeRegistry; + +/** + * CDI bean that creates a {@link SerDeserializerRegistry}. + */ +@ApplicationScoped +public class SerDeserializerRegistryFactory { + + @Produces + @ApplicationScoped + public SerDeserializerRegistry createRegistry() { + + // Knows about all types for usage with JSON-B + final SerializedDataTypeRegistry typeRegistry = SharedUtils.createTypeRegistry(); + + // Does the actual marshalling/unmarshalling + final JsonbDeSerializer jsonbDeSer = SharedUtils.createJsonbDeSerializer(); + + // Registry connects the type with the appropriate serializer and de-serializer + final SerDeserializerRegistry serDeserRegistry = SharedUtils.createSerDeserializerRegistry(typeRegistry, + jsonbDeSer); + + return serDeserRegistry; + + } + +} \ No newline at end of file diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/DuplicatePersonNameException.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/DuplicatePersonNameException.java new file mode 100644 index 0000000..47eb0b4 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/DuplicatePersonNameException.java @@ -0,0 +1,63 @@ +/** + * 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.example.quarkus.command.domain; + +import javax.validation.constraints.NotNull; + +import org.fuin.cqrs4j.example.quarkus.shared.PersonId; +import org.fuin.cqrs4j.example.quarkus.shared.PersonName; + +/** + * A name that should be unique does already exist. + */ +public final class DuplicatePersonNameException extends Exception { + + private static final long serialVersionUID = 1000L; + + private PersonId personId; + + private PersonName name; + + /** + * Constructor with mandatory data. + * + * @param personId + * Identifier of the resource that caused the problem. + * @param name + * Name of the resource that caused the problem. + */ + public DuplicatePersonNameException(@NotNull final PersonId personId, @NotNull final PersonName name) { + super("The name '" + name + "' already exists: " + personId.asString()); + this.personId = personId; + this.name = name; + } + + /** + * Returns the identifier of the entity that has the name. + * + * @return Identifier. + */ + public final PersonId getPersonId() { + return personId; + } + + /** + * Returns the name that already exists. + * + * @return Name. + */ + public final PersonName getName() { + return name; + } + +} \ No newline at end of file diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/Person.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/Person.java new file mode 100644 index 0000000..41d2f96 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/Person.java @@ -0,0 +1,113 @@ +/** + * 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.example.quarkus.command.domain; + +import java.io.Serializable; + +import javax.validation.constraints.NotNull; + +import org.fuin.cqrs4j.example.quarkus.shared.PersonCreatedEvent; +import org.fuin.cqrs4j.example.quarkus.shared.PersonId; +import org.fuin.cqrs4j.example.quarkus.shared.PersonName; +import org.fuin.ddd4j.ddd.AbstractAggregateRoot; +import org.fuin.ddd4j.ddd.ApplyEvent; +import org.fuin.ddd4j.ddd.EntityType; +import org.fuin.objects4j.common.Contract; + +/** + * A medical practitioner most likely also holder of an accredited academic degree. + */ +public class Person extends AbstractAggregateRoot implements Serializable { + + private static final long serialVersionUID = 1000L; + + @NotNull + private PersonId id; + + @NotNull + private PersonName name; + + /** + * Default constructor that is mandatory for aggregate roots. + */ + public Person() { + super(); + } + + /** + * Constructor with all data. + * + * @param id + * Unique identifier of the person. + * @param name + * Unique name of the person. + * @param service + * Service required by the method. + * + * @throws DuplicatePersonNameException + * The name already exists for another person. + */ + public Person(@NotNull final PersonId id, @NotNull final PersonName name, final PersonService service) throws DuplicatePersonNameException { + super(); + + // VERIFY PRECONDITIONS + Contract.requireArgNotNull("id", id); + Contract.requireArgNotNull("name", name); + + // VERIFY BUSINESS RULES + + // Rule 1: The name of the person must be unique + final PersonId otherId = service.loadPersonIdByName(name); + if (otherId != null) { + throw new DuplicatePersonNameException(otherId, name); + } + + // CREATE EVENT + apply(new PersonCreatedEvent(id, name)); + + } + + @Override + public PersonId getId() { + return id; + } + + @Override + public EntityType getType() { + return PersonId.TYPE; + } + + @ApplyEvent + public void applyEvent(final PersonCreatedEvent event) { + this.id = event.getEntityId(); + this.name = event.getName(); + } + + /** + * Service for the constructor. + */ + public static interface PersonService { + + /** + * Loads the person's identifier for a given name. + * + * @param name + * Person's name. + * + * @return Office identifier or {@literal null} if not found. + */ + public PersonId loadPersonIdByName(@NotNull PersonName name); + + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/PersonRepository.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/PersonRepository.java new file mode 100644 index 0000000..c7a9d53 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/PersonRepository.java @@ -0,0 +1,63 @@ +/** + * 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.example.quarkus.command.domain; + +import javax.annotation.concurrent.NotThreadSafe; +import javax.validation.constraints.NotNull; + +import org.fuin.cqrs4j.example.quarkus.shared.PersonId; +import org.fuin.ddd4j.ddd.EntityType; +import org.fuin.ddd4j.esrepo.EventStoreRepository; +import org.fuin.esc.api.EventStore; + +/** + * Event sourced repository for storing a {@link Person} aggregate. + */ +@NotThreadSafe +public class PersonRepository extends EventStoreRepository { + + /** + * Constructor all mandatory data. + * + * @param eventStore + * Event store. + */ + public PersonRepository(final EventStore eventStore) { + super(eventStore); + } + + @Override + @NotNull + public Class getAggregateClass() { + return Person.class; + } + + @Override + @NotNull + public EntityType getAggregateType() { + return PersonId.TYPE; + } + + @Override + @NotNull + public Person create() { + return new Person(); + } + + @Override + @NotNull + public String getIdParamName() { + return "personId"; + } + +} \ No newline at end of file diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/PersonRepositoryFactory.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/PersonRepositoryFactory.java new file mode 100644 index 0000000..4d7eb7f --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/PersonRepositoryFactory.java @@ -0,0 +1,41 @@ +/** + * 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.example.quarkus.command.domain; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; + +import org.fuin.esc.api.EventStore; + +/** + * CDI factory that creates an event store connection and repositories. + */ +@ApplicationScoped +public class PersonRepositoryFactory { + + /** + * Creates a repository. + * + * @param eventStore + * Event store implementation. + * + * @return Request scoped project repository. + */ + @Produces + @Dependent + public PersonRepository create(final EventStore eventStore) { + return new PersonRepository(eventStore); + } + +} diff --git a/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/package-info.java b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/package-info.java new file mode 100644 index 0000000..040b9c5 --- /dev/null +++ b/quarkus/command/src/main/java/org/fuin/cqrs4j/example/quarkus/command/domain/package-info.java @@ -0,0 +1,17 @@ +/** + * 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.example.quarkus.command.domain; + +/** + * Domain specific code like aggregate root classes and their repositories. + */ diff --git a/quarkus/command/src/main/resources/META-INF/resources/index.html b/quarkus/command/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000..cdd702d --- /dev/null +++ b/quarkus/command/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,11 @@ + + + + + CQRS Quarkus Example Command Module + + +

CQRS Quarkus Example Command Module

+

This page is served by Quarkus. The source is in src/main/resources/META-INF/resources/index.html.

+ + diff --git a/quarkus/command/src/main/resources/application.properties b/quarkus/command/src/main/resources/application.properties new file mode 100644 index 0000000..5750fa9 --- /dev/null +++ b/quarkus/command/src/main/resources/application.properties @@ -0,0 +1,8 @@ +# Configuration file +# key = value + +quarkus.native.additional-build-args=-H:ReflectionConfigurationFiles=reflection-config.json,--report-unsupported-elements-at-runtime + +quarkus.index-dependency.ddd4j.group-id=org.fuin +quarkus.index-dependency.ddd4j.artifact-id=ddd-4-java + \ No newline at end of file diff --git a/quarkus/command/src/main/resources/reflection-config.json b/quarkus/command/src/main/resources/reflection-config.json new file mode 100644 index 0000000..0fa9976 --- /dev/null +++ b/quarkus/command/src/main/resources/reflection-config.json @@ -0,0 +1,11 @@ +[ + { + "name" : "org.fuin.cqrs4j.SimpleResult", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + } +]