diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..024b500 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +survey-api/build/ +survey-common/build/ +survey-read/build/ +survey-write/build/ +.idea/ +.gradle/ +.vertx/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..eabe9c2 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Survey Gorilla +Survey monkey clone using vertx + +## Todo + + - Statistics + - Event sourcing + - Docker + +## Components + +survey-api + +survey-common + +survey-read + +survey-write + + +## How to run + +`docker-compose up` + +`curl -X GET \ + http://localhost:8080/api/polls` + +`curl -X POST \ + http://localhost:8080/api/polls \ + -H 'content-type: application/json' \ + -d '{ +"question" : "When!??????", +"options" : { + "1": "First option", + "2": "another option" + } +}'` + +`curl -X POST \ + http://localhost:8080/api/polls/1/answers \ + -H 'cache-control: no-cache' \ + -H 'content-type: application/json' \ + -H 'postman-token: 236ef2c5-c62d-0ff5-2d17-b16e81323076' \ + -d '{ + "pollID" : "1", + "answers" : [1,2] + }'` diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..7ec00ed --- /dev/null +++ b/build.gradle @@ -0,0 +1,31 @@ +group 'gorilla' +version '1.0-SNAPSHOT' + +subprojects { + + apply plugin: 'java' + + sourceCompatibility = 1.8 + + repositories { + mavenCentral() + } + + group = rootProject.group + version = rootProject.version + + plugins.withType(JavaPlugin) { + jar { + version = project.version + } + } + + dependencies { + // https://mvnrepository.com/artifact/io.vertx/vertx-core + compile group: 'io.vertx', name: 'vertx-core', version: '3.4.2' + compile group: 'io.vertx', name: 'vertx-web', version: '3.4.2' + compile group: 'io.vertx', name: 'vertx-hazelcast', version: '3.4.2' + + testCompile group: 'junit', name: 'junit', version: '4.12' + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..6e25c38 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..523b360 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Aug 15 10:44:51 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4453cce --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +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 +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +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 + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..c9e4755 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,7 @@ +rootProject.name = 'survey-gorilla' +include 'survey-read' +include 'survey-write' +include 'survey-api' +include 'survey-common' +include 'survey-api' + diff --git a/survey-api/build.gradle b/survey-api/build.gradle new file mode 100644 index 0000000..ea46462 --- /dev/null +++ b/survey-api/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile project(":survey-common") +} diff --git a/survey-api/src/main/java/com/surveygorilla/api/MainApi.java b/survey-api/src/main/java/com/surveygorilla/api/MainApi.java new file mode 100644 index 0000000..a938ec6 --- /dev/null +++ b/survey-api/src/main/java/com/surveygorilla/api/MainApi.java @@ -0,0 +1,18 @@ +package com.surveygorilla.api; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; + +/** + * Created by fabio.pires on 17.08.17. + */ +public class MainApi { + public static void main(String[] args) { + Vertx.clusteredVertx(new VertxOptions().setClustered(true), cluster -> { + if (cluster.succeeded()) { + final Vertx vertx = cluster.result(); + vertx.deployVerticle(SurveyApiVerticle.class.getName()); + } + }); + } +} diff --git a/survey-api/src/main/java/com/surveygorilla/api/SurveyApiVerticle.java b/survey-api/src/main/java/com/surveygorilla/api/SurveyApiVerticle.java new file mode 100644 index 0000000..d408ba5 --- /dev/null +++ b/survey-api/src/main/java/com/surveygorilla/api/SurveyApiVerticle.java @@ -0,0 +1,84 @@ +package com.surveygorilla.api; + +import com.surveygorilla.common.command.CreatePollCommand; +import com.surveygorilla.common.command.SubmitAnswerCommand; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.eventbus.Message; +import io.vertx.core.json.Json; +import io.vertx.core.logging.Logger; +import io.vertx.core.logging.LoggerFactory; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.BodyHandler; + + +/** + * Created by fabio.pires on 17.08.17. + */ +public class SurveyApiVerticle extends AbstractVerticle { + + private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + @Override + public void start(Future start) throws Exception { + logger.info("SurveyApiVerticle started"); + + Router router = Router.router(vertx); + router.route("/api/polls*").handler(BodyHandler.create()); + + router.get("/api/polls").handler(this::getAllPolls); + router.get("/api/polls/:id").handler(this::getPollById); + router.post("/api/polls").handler(this::addPoll); + router.post("/api/polls/:id/answers").handler(this::submitAnswer); + + vertx.createHttpServer(). + requestHandler(router::accept). + listen(8080, "0.0.0.0"); + + } + + private void getAllPolls(RoutingContext ctx) { + vertx.eventBus(). + send("GET:/api/polls", "", + responseHandler -> requestResponse(ctx, responseHandler)); + } + + private void addPoll(RoutingContext ctx) { + final CreatePollCommand createPollCommand = Json.decodeValue(ctx.getBodyAsString(), + CreatePollCommand.class); + + vertx.eventBus(). + send("POST:/api/polls", Json.encode(createPollCommand), + responseHandler -> { + requestResponse(ctx, responseHandler); + }); + } + + private void getPollById(RoutingContext ctx) { + vertx.eventBus(). + send("GET:/api/polls/:id", ctx.get("id"), + responseHandler -> requestResponse(ctx, responseHandler)); + } + + private void submitAnswer(RoutingContext ctx) { + final SubmitAnswerCommand submitAnswerCommand = Json.decodeValue(ctx.getBodyAsString(), + SubmitAnswerCommand.class); + + vertx.eventBus(). + send("POST:/api/polls/:id/answer", Json.encode(submitAnswerCommand), + responseHandler -> requestResponse(ctx, responseHandler)); + } + + private void requestResponse(RoutingContext ctx, AsyncResult> responseHandler) { + if (responseHandler.failed()) { + logger.info("SurveyApiVerticle.requestResponse.failed=" + responseHandler.cause().getMessage()); + + ctx.fail(500); + } else { + final Message result = responseHandler.result(); + ctx.response().end((String) result.body()); + } + } +} diff --git a/survey-api/src/main/resources/cluster.xml b/survey-api/src/main/resources/cluster.xml new file mode 100644 index 0000000..e9276ca --- /dev/null +++ b/survey-api/src/main/resources/cluster.xml @@ -0,0 +1,133 @@ + + + + false + false + 0 + jdk + + 2 + + 5 + 10 + 10 + 10 + 10 + 2 + 10 + 3 + + + 5701 + + 0 + + + + + + 127.0.0.1 + + + + + + 10.10.1.* + + + + + + PBEWithMD5AndDES + + thesalt + + thepass + + 19 + + + + + 16 + + 0 + + + + + 1 + + 0 + + 0 + + NONE + + 0 + + 25 + + com.hazelcast.map.merge.LatestUpdateMapMergePolicy + + + + + + 1 + + + diff --git a/survey-common/build.gradle b/survey-common/build.gradle new file mode 100644 index 0000000..e69de29 diff --git a/survey-common/src/main/java/com/surveygorilla/common/Poll.java b/survey-common/src/main/java/com/surveygorilla/common/Poll.java new file mode 100644 index 0000000..ed46f71 --- /dev/null +++ b/survey-common/src/main/java/com/surveygorilla/common/Poll.java @@ -0,0 +1,47 @@ +package com.surveygorilla.common; + +import com.surveygorilla.common.exception.PollCreationException; + +import java.util.Map; + +/** + * Created by fabio.pires on 16.08.17. + */ +public class Poll { + + private Integer pollID; + + private String question; + + private Map options; + + private Map answers; + + public Poll(Integer pollID, String question, Map options, Map answers) throws PollCreationException { + + if (pollID == null || question == null || options == null || answers == null) { + throw new PollCreationException("Unable to create a new poll"); + } + + this.pollID = pollID; + this.question = question; + this.options = options; + this.answers = answers; + } + + public Integer getPollID() { + return pollID; + } + + public String getQuestion() { + return question; + } + + public Map getOptions() { + return options; + } + + public Map getAnswers() { + return answers; + } +} diff --git a/survey-common/src/main/java/com/surveygorilla/common/command/CreatePollCommand.java b/survey-common/src/main/java/com/surveygorilla/common/command/CreatePollCommand.java new file mode 100644 index 0000000..5a34ff5 --- /dev/null +++ b/survey-common/src/main/java/com/surveygorilla/common/command/CreatePollCommand.java @@ -0,0 +1,45 @@ +package com.surveygorilla.common.command; + +import java.time.Instant; +import java.util.Map; + +/** + * Created by fabio.pires on 17.08.17. + */ +public class CreatePollCommand { + + private Integer pollID = Instant.now().getNano(); + + private String question; + + private Map options; + + public Integer getPollID() { + return pollID; + } + + public String getQuestion() { + return question; + } + + public void setQuestion(String question) { + this.question = question; + } + + public Map getOptions() { + return options; + } + + public void setOptions(Map options) { + this.options = options; + } + + @Override + public String toString() { + return "CreatePollCommand{" + + "pollID=" + pollID + + ", question='" + question + '\'' + + ", options=" + options + + '}'; + } +} diff --git a/survey-common/src/main/java/com/surveygorilla/common/command/SubmitAnswerCommand.java b/survey-common/src/main/java/com/surveygorilla/common/command/SubmitAnswerCommand.java new file mode 100644 index 0000000..92a01ab --- /dev/null +++ b/survey-common/src/main/java/com/surveygorilla/common/command/SubmitAnswerCommand.java @@ -0,0 +1,29 @@ +package com.surveygorilla.common.command; + +import java.util.List; + +/** + * Created by fabio.pires on 18.08.17. + */ +public class SubmitAnswerCommand { + + private Integer pollID; + + private List answers; + + public Integer getPollID() { + return pollID; + } + + public void setPollID(Integer pollID) { + this.pollID = pollID; + } + + public List getAnswers() { + return answers; + } + + public void setAnswers(List answers) { + this.answers = answers; + } +} diff --git a/survey-common/src/main/java/com/surveygorilla/common/event/AnswerSubmitted.java b/survey-common/src/main/java/com/surveygorilla/common/event/AnswerSubmitted.java new file mode 100644 index 0000000..0fe1f00 --- /dev/null +++ b/survey-common/src/main/java/com/surveygorilla/common/event/AnswerSubmitted.java @@ -0,0 +1,36 @@ +package com.surveygorilla.common.event; + +import java.util.Map; + +/** + * Created by fabio.pires on 18.08.17. + */ +public class AnswerSubmitted { + private Integer pollID; + + private Map answers; + + public AnswerSubmitted() { + } + + public AnswerSubmitted(Integer pollID, Map answers) { + this.pollID = pollID; + this.answers = answers; + } + + public Integer getPollID() { + return pollID; + } + + public void setPollID(Integer pollID) { + this.pollID = pollID; + } + + public Map getAnswers() { + return answers; + } + + public void setAnswers(Map answers) { + this.answers = answers; + } +} diff --git a/survey-common/src/main/java/com/surveygorilla/common/event/PollCreatedEvent.java b/survey-common/src/main/java/com/surveygorilla/common/event/PollCreatedEvent.java new file mode 100644 index 0000000..fa466e4 --- /dev/null +++ b/survey-common/src/main/java/com/surveygorilla/common/event/PollCreatedEvent.java @@ -0,0 +1,57 @@ +package com.surveygorilla.common.event; + +import java.util.Map; + +/** + * Created by fabio.pires on 17.08.17. + */ +public class PollCreatedEvent { + + private Integer pollID; + + private String question; + + private Map options; + + public PollCreatedEvent() { + } + + public PollCreatedEvent(Integer pollID, String question, Map options) { + this.pollID = pollID; + this.question = question; + this.options = options; + } + + public Integer getPollID() { + return pollID; + } + + public void setPollID(Integer pollID) { + this.pollID = pollID; + } + + public String getQuestion() { + return question; + } + + public void setQuestion(String question) { + this.question = question; + } + + public Map getOptions() { + return options; + } + + public void setOptions(Map options) { + this.options = options; + } + + @Override + public String toString() { + return "PollCreatedEvent{" + + "pollID=" + pollID + + ", question='" + question + '\'' + + ", options=" + options + + '}'; + } +} diff --git a/survey-common/src/main/java/com/surveygorilla/common/exception/PollCreationException.java b/survey-common/src/main/java/com/surveygorilla/common/exception/PollCreationException.java new file mode 100644 index 0000000..e72c843 --- /dev/null +++ b/survey-common/src/main/java/com/surveygorilla/common/exception/PollCreationException.java @@ -0,0 +1,10 @@ +package com.surveygorilla.common.exception; + +/** + * Created by fabio.pires on 17.08.17. + */ +public class PollCreationException extends Exception { + public PollCreationException(String message) { + super(message); + } +} diff --git a/survey-read/build.gradle b/survey-read/build.gradle new file mode 100644 index 0000000..ea46462 --- /dev/null +++ b/survey-read/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile project(":survey-common") +} diff --git a/survey-read/src/main/java/com/surveygorilla/read/MainRead.java b/survey-read/src/main/java/com/surveygorilla/read/MainRead.java new file mode 100644 index 0000000..d809537 --- /dev/null +++ b/survey-read/src/main/java/com/surveygorilla/read/MainRead.java @@ -0,0 +1,19 @@ +package com.surveygorilla.read; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; + +/** + * Created by fabio.pires on 16.08.17. + */ +public class MainRead { + public static void main(String[] args) { + Vertx.clusteredVertx(new VertxOptions().setClustered(true), cluster -> { + if (cluster.succeeded()) { + final Vertx vertx = cluster.result(); + vertx.deployVerticle(SurveyReadVerticle.class.getName()); + } + }); + } +} + diff --git a/survey-read/src/main/java/com/surveygorilla/read/SurveyReadVerticle.java b/survey-read/src/main/java/com/surveygorilla/read/SurveyReadVerticle.java new file mode 100644 index 0000000..b86fbcb --- /dev/null +++ b/survey-read/src/main/java/com/surveygorilla/read/SurveyReadVerticle.java @@ -0,0 +1,79 @@ +package com.surveygorilla.read; + +import com.surveygorilla.common.Poll; +import com.surveygorilla.common.event.AnswerSubmitted; +import com.surveygorilla.common.event.PollCreatedEvent; +import com.surveygorilla.common.exception.PollCreationException; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.eventbus.Message; +import io.vertx.core.json.Json; +import io.vertx.core.logging.Logger; +import io.vertx.core.logging.LoggerFactory; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Created by fabio.pires on 15.08.17. + */ +public class SurveyReadVerticle extends AbstractVerticle { + + private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + private Map polls = new LinkedHashMap<>(); + + @Override + public void start(Future start) throws Exception { + logger.info("SurveyReadVerticle started"); + + vertx.eventBus().consumer("GET:/api/polls", getAllPolls()); + vertx.eventBus().consumer("GET:/api/polls/:id", getPollById()); + vertx.eventBus().consumer("pollCreatedEvent", pollCreatedEventListener()); + vertx.eventBus().consumer("answerSubmittedEvent", answerSubmittedEventListener()); + start.complete(); + } + + private Handler> getAllPolls() { + return handler -> handler.reply(Json.encodePrettily(polls.values())); + } + + private Handler> getPollById() { + return handler -> handler.reply(Json.encodePrettily(polls.get(handler.body()))); + } + + private Handler> pollCreatedEventListener() { + return handler -> { + final PollCreatedEvent pollCreatedEvent = Json.decodeValue( + handler.body(), + PollCreatedEvent.class); + + try { + polls.put(pollCreatedEvent.getPollID(), new Poll(pollCreatedEvent.getPollID(), pollCreatedEvent.getQuestion(), pollCreatedEvent.getOptions(), new HashMap<>())); + } catch (PollCreationException e) { + e.printStackTrace(); + } + }; + } + + private Handler> answerSubmittedEventListener() { + return handler -> { + final AnswerSubmitted answerSubmitted = Json.decodeValue( + handler.body(), + AnswerSubmitted.class); + + Poll p = polls.get(answerSubmitted.getPollID()); + + if (p!=null){ + p.getAnswers().putAll(answerSubmitted.getAnswers()); + polls.put(p.getPollID(), p); + } + }; + } +} + + + + diff --git a/survey-read/src/main/resources/cluster.xml b/survey-read/src/main/resources/cluster.xml new file mode 100644 index 0000000..e9276ca --- /dev/null +++ b/survey-read/src/main/resources/cluster.xml @@ -0,0 +1,133 @@ + + + + false + false + 0 + jdk + + 2 + + 5 + 10 + 10 + 10 + 10 + 2 + 10 + 3 + + + 5701 + + 0 + + + + + + 127.0.0.1 + + + + + + 10.10.1.* + + + + + + PBEWithMD5AndDES + + thesalt + + thepass + + 19 + + + + + 16 + + 0 + + + + + 1 + + 0 + + 0 + + NONE + + 0 + + 25 + + com.hazelcast.map.merge.LatestUpdateMapMergePolicy + + + + + + 1 + + + diff --git a/survey-write/build.gradle b/survey-write/build.gradle new file mode 100644 index 0000000..ea46462 --- /dev/null +++ b/survey-write/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile project(":survey-common") +} diff --git a/survey-write/src/main/java/com/surveygorilla/write/MainWrite.java b/survey-write/src/main/java/com/surveygorilla/write/MainWrite.java new file mode 100644 index 0000000..ede2c5b --- /dev/null +++ b/survey-write/src/main/java/com/surveygorilla/write/MainWrite.java @@ -0,0 +1,18 @@ +package com.surveygorilla.write; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; + +/** + * Created by fabio.pires on 16.08.17. + */ +public class MainWrite { + public static void main(String[] args) { + Vertx.clusteredVertx(new VertxOptions().setClustered(true), cluster -> { + if (cluster.succeeded()) { + final Vertx vertx = cluster.result(); + vertx.deployVerticle(SurveyWriteVerticle.class.getName()); + } + }); + } +} diff --git a/survey-write/src/main/java/com/surveygorilla/write/SurveyWriteVerticle.java b/survey-write/src/main/java/com/surveygorilla/write/SurveyWriteVerticle.java new file mode 100644 index 0000000..9efead0 --- /dev/null +++ b/survey-write/src/main/java/com/surveygorilla/write/SurveyWriteVerticle.java @@ -0,0 +1,96 @@ +package com.surveygorilla.write; + +import com.surveygorilla.common.Poll; +import com.surveygorilla.common.command.CreatePollCommand; +import com.surveygorilla.common.command.SubmitAnswerCommand; +import com.surveygorilla.common.event.AnswerSubmitted; +import com.surveygorilla.common.event.PollCreatedEvent; +import com.surveygorilla.common.exception.PollCreationException; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.eventbus.Message; +import io.vertx.core.json.Json; +import io.vertx.core.logging.Logger; +import io.vertx.core.logging.LoggerFactory; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + + +/** + * Created by fabio.pires on 16.08.17. + */ +public class SurveyWriteVerticle extends AbstractVerticle { + + private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + private AtomicInteger count = new AtomicInteger(); + + private Map polls = new LinkedHashMap<>(); + + @Override + public void start(Future start) throws Exception { + logger.info("SurveyWriteVerticle started"); + vertx.eventBus().consumer("POST:/api/polls", addPoll()); + vertx.eventBus().consumer("POST:/api/polls/:id/answer", submitAnswer()); + } + + private Handler> addPoll() { + + return handler -> { + final CreatePollCommand createPollCommand = Json.decodeValue( + handler.body().toString(), + CreatePollCommand.class); + + // validations skipped + + try { + Poll p = new Poll( + count.addAndGet(1), + createPollCommand.getQuestion(), + createPollCommand.getOptions(), + new HashMap<>() + ); + + createPollCommand.getOptions().keySet().forEach(o -> p.getAnswers().merge(o, 0, Integer::sum)); + + polls.put(count.get(), p); + + // create and propagate events + final PollCreatedEvent pollCreatedEvent = new PollCreatedEvent(p.getPollID(), p.getQuestion(), p.getOptions()); + vertx.eventBus().send("pollCreatedEvent", Json.encode(pollCreatedEvent)); + + handler.reply(Json.encodePrettily(p)); + + } catch (PollCreationException e) { + e.printStackTrace(); + handler.fail(500, "Unable to create poll " + e.getMessage()); + } + + }; + } + + private Handler> submitAnswer() { + + return handler -> { + final SubmitAnswerCommand submitAnswerCommand = Json.decodeValue( + handler.body().toString(), + SubmitAnswerCommand.class); + + Poll p = polls.get(submitAnswerCommand.getPollID()); + if (p != null){ + submitAnswerCommand.getAnswers().forEach(a -> p.getAnswers().merge(a, 1, Integer::sum)); + polls.put(p.getPollID(), p); + + final AnswerSubmitted answerSubmitted = new AnswerSubmitted(p.getPollID(), p.getAnswers()); + vertx.eventBus().send("answerSubmittedEvent", Json.encode(answerSubmitted)); + + handler.reply(Json.encodePrettily(p)); + } + handler.fail(500, "Unable to submit answer"); + }; + } +} diff --git a/survey-write/src/main/resources/cluster.xml b/survey-write/src/main/resources/cluster.xml new file mode 100644 index 0000000..e9276ca --- /dev/null +++ b/survey-write/src/main/resources/cluster.xml @@ -0,0 +1,133 @@ + + + + false + false + 0 + jdk + + 2 + + 5 + 10 + 10 + 10 + 10 + 2 + 10 + 3 + + + 5701 + + 0 + + + + + + 127.0.0.1 + + + + + + 10.10.1.* + + + + + + PBEWithMD5AndDES + + thesalt + + thepass + + 19 + + + + + 16 + + 0 + + + + + 1 + + 0 + + 0 + + NONE + + 0 + + 25 + + com.hazelcast.map.merge.LatestUpdateMapMergePolicy + + + + + + 1 + + +