diff --git a/ServerBenchmark/build.gradle b/ServerBenchmark/build.gradle
new file mode 100644
index 0000000..000cbf3
--- /dev/null
+++ b/ServerBenchmark/build.gradle
@@ -0,0 +1,33 @@
+plugins {
+ id 'java-library'
+}
+
+dependencies {
+ compile group: 'com.google.guava', name: 'guava', version: '25.0-jre'
+ compile group: 'org.jetbrains', name: 'annotations', version: '15.0'
+ compile group: 'org.projectlombok', name: 'lombok', version: '1.16.20'
+ compile group: 'commons-io', name: 'commons-io', version: '2.6'
+ compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.5.1'
+ compile group: 'jfree', name: 'jfreechart', version: '1.0.12'
+ compile group: 'com.jcraft', name: 'jsch', version: '0.1.52'
+
+ testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3'
+ testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.1.0'
+ testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.0.0-M4'
+ testCompile group: 'org.mockito', name: 'mockito-all', version: '1.9.5'
+
+ testRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.1.0'
+ testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '5.1.0'
+
+ testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.1.0'
+}
+
+
+repositories {
+ jcenter()
+}
+
+task wrapper(type: Wrapper) {
+ description = 'Generates gradlew[.bat] scripts'
+ gradleVersion = '4.6'
+}
diff --git a/ServerBenchmark/gradle/wrapper/gradle-wrapper.jar b/ServerBenchmark/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/ServerBenchmark/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/ServerBenchmark/gradle/wrapper/gradle-wrapper.properties b/ServerBenchmark/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bf3de21
--- /dev/null
+++ b/ServerBenchmark/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/ServerBenchmark/gradlew b/ServerBenchmark/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/ServerBenchmark/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/ServerBenchmark/gradlew.bat b/ServerBenchmark/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/ServerBenchmark/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/ServerBenchmark/results/logs/Blocking_ArraySize.txt b/ServerBenchmark/results/logs/Blocking_ArraySize.txt
new file mode 100644
index 0000000..f1ff8f7
--- /dev/null
+++ b/ServerBenchmark/results/logs/Blocking_ArraySize.txt
@@ -0,0 +1,42 @@
+Server type: BlockingArray size: 5000
+Clients number: 25
+Delay: 200
+Queries number: 25
+Altering option: ArraySize
+1000 10000 1000
+TaskTime
+10
+1000 5.9872
+2000 15.216
+3000 18.0544
+4000 34.2496
+5000 46.7072
+6000 60.9648
+7000 80.0992
+8000 103.2432
+9000 139.2432
+10000 181.6352
+ClientTime
+10
+1000 349.44
+2000 483.28
+3000 701.44
+4000 727.16
+5000 875.68
+6000 794.8
+7000 983.44
+8000 1034.72
+9000 1391.64
+10000 1522.8
+ServerTime
+10
+1000 18.1664
+2000 36.9168
+3000 49.0976
+4000 141.0704
+5000 271.4112
+6000 199.9296
+7000 229.4272
+8000 451.0144
+9000 607.3536
+10000 1110.0096
diff --git a/ServerBenchmark/results/logs/Blocking_ClientsNumber.txt b/ServerBenchmark/results/logs/Blocking_ClientsNumber.txt
new file mode 100644
index 0000000..2db0e89
--- /dev/null
+++ b/ServerBenchmark/results/logs/Blocking_ClientsNumber.txt
@@ -0,0 +1,42 @@
+Server type: BlockingArray size: 10000
+Clients number: 25
+Delay: 200
+Queries number: 20
+Altering option: ClientsNumber
+10 100 10
+TaskTime
+10
+10 184.64646464646464
+20 183.985
+30 182.46333333333334
+40 180.06125
+50 180.081
+60 179.75416666666666
+70 178.325
+80 186.181875
+90 192.13055555555556
+100 190.952
+ClientTime
+10
+10 781.2
+20 1322.25
+30 2061.8
+40 2612.1
+50 3160.82
+60 3489.0333333333333
+70 4196.0142857142855
+80 4858.5125
+90 5631.3
+100 6458.75
+ServerTime
+10
+10 415.89
+20 718.695
+30 971.3816666666667
+40 1503.8775
+50 1324.76
+60 1937.2608333333333
+70 2147.4785714285713
+80 3223.5475
+90 5015.8415786548085
+100 5291.949
diff --git a/ServerBenchmark/results/logs/Blocking_Delay.txt b/ServerBenchmark/results/logs/Blocking_Delay.txt
new file mode 100644
index 0000000..457a562
--- /dev/null
+++ b/ServerBenchmark/results/logs/Blocking_Delay.txt
@@ -0,0 +1,42 @@
+Server type: BlockingArray size: 5000
+Clients number: 25
+Delay: 200
+Queries number: 25
+Altering option: Delay
+0 900 100
+TaskTime
+10
+0 48.6016
+100 48.4048
+200 51.4096
+300 50.6192
+400 50.1168
+500 54.112
+600 49.6976
+700 51.904
+800 51.6992
+900 50.6736
+ClientTime
+10
+0 752.4
+100 656.12
+200 708.32
+300 835.64
+400 936.04
+500 1168.2
+600 1059.4
+700 1266.64
+800 1307.84
+900 1526.64
+ServerTime
+10
+0 382.68
+100 126.872
+200 221.1824
+300 129.624
+400 135.1968
+500 285.848
+600 137.4
+700 145.7808
+800 179.184
+900 151.3024
diff --git a/ServerBenchmark/results/logs/NonBlocking_ArraySize.txt b/ServerBenchmark/results/logs/NonBlocking_ArraySize.txt
new file mode 100644
index 0000000..4253963
--- /dev/null
+++ b/ServerBenchmark/results/logs/NonBlocking_ArraySize.txt
@@ -0,0 +1,42 @@
+Server type: NonBlockingArray size: 5000
+Clients number: 25
+Delay: 200
+Queries number: 25
+Altering option: ArraySize
+1000 10000 1000
+TaskTime
+10
+1000 1.3152
+2000 4.6496
+3000 11.0544
+4000 22.2288
+5000 37.1488
+6000 58.3408
+7000 83.5168
+8000 116.3872
+9000 152.832
+10000 184.3952
+ClientTime
+10
+1000 238.4
+2000 304.68
+3000 411.4
+4000 517.68
+5000 600.12
+6000 698.04
+7000 875.4
+8000 1294.68
+9000 1326.96
+10000 1733.64
+ServerTime
+10
+1000 10.976
+2000 19.2896
+3000 55.5168
+4000 116.2928
+5000 169.6288
+6000 220.6256
+7000 342.656
+8000 741.624
+9000 776.712
+10000 1264.2256
diff --git a/ServerBenchmark/results/logs/NonBlocking_ClientsNumber.txt b/ServerBenchmark/results/logs/NonBlocking_ClientsNumber.txt
new file mode 100644
index 0000000..d7e4a48
--- /dev/null
+++ b/ServerBenchmark/results/logs/NonBlocking_ClientsNumber.txt
@@ -0,0 +1,42 @@
+Server type: NonBlockingArray size: 10000
+Clients number: 25
+Delay: 200
+Queries number: 20
+Altering option: ClientsNumber
+10 100 10
+TaskTime
+10
+10 207.0964467005076
+20 203.203007518797
+30 207.125
+40 204.82559598494353
+50 212.47294589178355
+60 202.46166666666667
+70 206.64285714285714
+80 202.205625
+90 196.43444444444444
+100 190.4385
+ClientTime
+10
+10 823.9
+20 1393.85
+30 2281.0666666666666
+40 3053.85
+50 4020.5
+60 4029.9
+70 4764.971428571429
+80 5477.65
+90 6299.711111111111
+100 6592.06
+ServerTime
+10
+10 492.685
+20 989.8175
+30 1729.435
+40 2430.08125
+50 3349.428
+60 3546.2716666666665
+70 3983.267142857143
+80 4993.553125
+90 5843.877777777778
+100 5947.957
diff --git a/ServerBenchmark/results/logs/NonBlocking_Delay.txt b/ServerBenchmark/results/logs/NonBlocking_Delay.txt
new file mode 100644
index 0000000..3f4e55c
--- /dev/null
+++ b/ServerBenchmark/results/logs/NonBlocking_Delay.txt
@@ -0,0 +1,42 @@
+Server type: NonBlockingArray size: 5000
+Clients number: 25
+Delay: 200
+Queries number: 25
+Altering option: Delay
+0 900 100
+TaskTime
+10
+0 40.912
+100 40.9072
+200 41.6096
+300 41.1232
+400 40.3872
+500 40.0736
+600 39.9856
+700 40.3424
+800 41.3232
+900 42.064
+ClientTime
+10
+0 590.6
+100 699.4
+200 824.48
+300 658.6
+400 750.88
+500 882.64
+600 958.32
+700 1186.56
+800 1291.96
+900 1305.36
+ServerTime
+10
+0 247.7376
+100 292.9776
+200 333.5824
+300 151.5584
+400 138.5808
+500 82.8752
+600 95.1392
+700 176.808
+800 161.9392
+900 138.3488
diff --git a/ServerBenchmark/results/logs/Simple_ArraySize.txt b/ServerBenchmark/results/logs/Simple_ArraySize.txt
new file mode 100644
index 0000000..1426d96
--- /dev/null
+++ b/ServerBenchmark/results/logs/Simple_ArraySize.txt
@@ -0,0 +1,42 @@
+Server type: SimpleArray size: 5000
+Clients number: 25
+Delay: 200
+Queries number: 25
+Altering option: ArraySize
+1000 10000 1000
+TaskTime
+10
+1000 17.804173354735152
+2000 39.3856
+3000 51.584
+4000 51.3072
+5000 78.1184
+6000 69.6352
+7000 117.54019292604502
+8000 135.24679487179486
+9000 208.9984
+10000 366.2371794871795
+ClientTime
+10
+1000 327.92
+2000 428.8
+3000 512.24
+4000 626.64
+5000 788.88
+6000 1365.16
+7000 1024.24
+8000 1214.16
+9000 1484.48
+10000 1865.52
+ServerTime
+10
+1000 36.02247191011236
+2000 52.4208
+3000 82.3701923076923
+4000 108.944
+5000 173.0448
+6000 267.8512
+7000 392.2215088282504
+8000 284.5872
+9000 543.5776
+10000 964.776886035313
diff --git a/ServerBenchmark/results/logs/Simple_ClientsNumber.txt b/ServerBenchmark/results/logs/Simple_ClientsNumber.txt
new file mode 100644
index 0000000..7446203
--- /dev/null
+++ b/ServerBenchmark/results/logs/Simple_ClientsNumber.txt
@@ -0,0 +1,42 @@
+Server type: SimpleArray size: 10000
+Clients number: 25
+Delay: 200
+Queries number: 20
+Altering option: ClientsNumber
+10 100 10
+TaskTime
+10
+10 259.075
+20 367.8725
+30 360.36622073578593
+40 420.2120451693852
+50 515.871
+60 541.045
+70 491.1660701503221
+80 686.3289555972483
+90 623.1728738187882
+100 616.5450450450451
+ClientTime
+10
+10 891.8
+20 2287.75
+30 2999.366666666667
+40 2572.45
+50 2969.4
+60 3627.2833333333333
+70 5813.914285714286
+80 4832.6375
+90 4982.211111111111
+100 5561.11
+ServerTime
+10
+10 488.0
+20 1325.915
+30 1444.6761268781302
+40 1153.360552763819
+50 1293.895
+60 1751.1191666666666
+70 2411.175375805297
+80 2319.101875
+90 1639.6703724291274
+100 2274.55
diff --git a/ServerBenchmark/results/logs/Simple_Delay.txt b/ServerBenchmark/results/logs/Simple_Delay.txt
new file mode 100644
index 0000000..d0b0b30
--- /dev/null
+++ b/ServerBenchmark/results/logs/Simple_Delay.txt
@@ -0,0 +1,42 @@
+Server type: SimpleArray size: 5000
+Clients number: 25
+Delay: 200
+Queries number: 25
+Altering option: Delay
+0 900 100
+TaskTime
+10
+0 77.7888
+100 63.8512
+200 60.37019230769231
+300 79.9824
+400 71.472
+500 83.32
+600 65.1392
+700 75.62339743589743
+800 101.7184
+900 60.8464
+ClientTime
+10
+0 681.08
+100 686.04
+200 814.28
+300 828.92
+400 933.32
+500 1082.84
+600 1159.6
+700 1263.36
+800 1199.0
+900 1564.36
+ServerTime
+10
+0 237.5632
+100 123.0304
+200 112.31410256410257
+300 116.8512
+400 116.1136
+500 188.0624
+600 107.5792
+700 119.08173076923077
+800 181.4096
+900 132.336
diff --git a/ServerBenchmark/results/plots/ClientTime(ArraySize).png b/ServerBenchmark/results/plots/ClientTime(ArraySize).png
new file mode 100644
index 0000000..b6123ec
Binary files /dev/null and b/ServerBenchmark/results/plots/ClientTime(ArraySize).png differ
diff --git a/ServerBenchmark/results/plots/ClientTime(ClientsNumber).png b/ServerBenchmark/results/plots/ClientTime(ClientsNumber).png
new file mode 100644
index 0000000..f535747
Binary files /dev/null and b/ServerBenchmark/results/plots/ClientTime(ClientsNumber).png differ
diff --git a/ServerBenchmark/results/plots/ClientTime(Delay).png b/ServerBenchmark/results/plots/ClientTime(Delay).png
new file mode 100644
index 0000000..6a28e01
Binary files /dev/null and b/ServerBenchmark/results/plots/ClientTime(Delay).png differ
diff --git a/ServerBenchmark/results/plots/ServerTime(ArraySize).png b/ServerBenchmark/results/plots/ServerTime(ArraySize).png
new file mode 100644
index 0000000..ce1a6b7
Binary files /dev/null and b/ServerBenchmark/results/plots/ServerTime(ArraySize).png differ
diff --git a/ServerBenchmark/results/plots/ServerTime(ClientsNumber).png b/ServerBenchmark/results/plots/ServerTime(ClientsNumber).png
new file mode 100644
index 0000000..472902a
Binary files /dev/null and b/ServerBenchmark/results/plots/ServerTime(ClientsNumber).png differ
diff --git a/ServerBenchmark/results/plots/ServerTime(Delay).png b/ServerBenchmark/results/plots/ServerTime(Delay).png
new file mode 100644
index 0000000..9b26bc7
Binary files /dev/null and b/ServerBenchmark/results/plots/ServerTime(Delay).png differ
diff --git a/ServerBenchmark/results/plots/TaskTime(ArraySize).png b/ServerBenchmark/results/plots/TaskTime(ArraySize).png
new file mode 100644
index 0000000..50ed61b
Binary files /dev/null and b/ServerBenchmark/results/plots/TaskTime(ArraySize).png differ
diff --git a/ServerBenchmark/results/plots/TaskTime(ClientsNumber).png b/ServerBenchmark/results/plots/TaskTime(ClientsNumber).png
new file mode 100644
index 0000000..d8d1ee8
Binary files /dev/null and b/ServerBenchmark/results/plots/TaskTime(ClientsNumber).png differ
diff --git a/ServerBenchmark/results/plots/TaskTime(Delay).png b/ServerBenchmark/results/plots/TaskTime(Delay).png
new file mode 100644
index 0000000..153f5ed
Binary files /dev/null and b/ServerBenchmark/results/plots/TaskTime(Delay).png differ
diff --git a/ServerBenchmark/settings b/ServerBenchmark/settings
new file mode 100644
index 0000000..d791ba4
--- /dev/null
+++ b/ServerBenchmark/settings
@@ -0,0 +1,4 @@
+1000
+1
+10
+1
\ No newline at end of file
diff --git a/ServerBenchmark/settings.gradle b/ServerBenchmark/settings.gradle
new file mode 100644
index 0000000..d4fae04
--- /dev/null
+++ b/ServerBenchmark/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'ServerBenchmark'
+
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ArrayMessage.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ArrayMessage.java
new file mode 100644
index 0000000..39a2355
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ArrayMessage.java
@@ -0,0 +1,607 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: src/main/resources/array.proto
+
+package com.mit.spbau.kirakosian;
+
+public final class ArrayMessage {
+ private ArrayMessage() {}
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistryLite registry) {
+ }
+
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistry registry) {
+ registerAllExtensions(
+ (com.google.protobuf.ExtensionRegistryLite) registry);
+ }
+ public interface ArrayOrBuilder extends
+ // @@protoc_insertion_point(interface_extends:tutorial.Array)
+ com.google.protobuf.MessageOrBuilder {
+
+ /**
+ * repeated int32 data = 1;
+ */
+ java.util.List getDataList();
+ /**
+ * repeated int32 data = 1;
+ */
+ int getDataCount();
+ /**
+ * repeated int32 data = 1;
+ */
+ int getData(int index);
+ }
+ /**
+ * Protobuf type {@code tutorial.Array}
+ */
+ public static final class Array extends
+ com.google.protobuf.GeneratedMessageV3 implements
+ // @@protoc_insertion_point(message_implements:tutorial.Array)
+ ArrayOrBuilder {
+ private static final long serialVersionUID = 0L;
+ // Use Array.newBuilder() to construct.
+ private Array(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
+ super(builder);
+ }
+ private Array() {
+ data_ = java.util.Collections.emptyList();
+ }
+
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private Array(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ this();
+ if (extensionRegistry == null) {
+ throw new java.lang.NullPointerException();
+ }
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ default: {
+ if (!parseUnknownField(
+ input, unknownFields, extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ case 8: {
+ if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
+ data_ = new java.util.ArrayList();
+ mutable_bitField0_ |= 0x00000001;
+ }
+ data_.add(input.readInt32());
+ break;
+ }
+ case 10: {
+ int length = input.readRawVarint32();
+ int limit = input.pushLimit(length);
+ if (!((mutable_bitField0_ & 0x00000001) == 0x00000001) && input.getBytesUntilLimit() > 0) {
+ data_ = new java.util.ArrayList();
+ mutable_bitField0_ |= 0x00000001;
+ }
+ while (input.getBytesUntilLimit() > 0) {
+ data_.add(input.readInt32());
+ }
+ input.popLimit(limit);
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e).setUnfinishedMessage(this);
+ } finally {
+ if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
+ data_ = java.util.Collections.unmodifiableList(data_);
+ }
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ com.mit.spbau.kirakosian.ArrayMessage.Array.class, com.mit.spbau.kirakosian.ArrayMessage.Array.Builder.class);
+ }
+
+ public static final int DATA_FIELD_NUMBER = 1;
+ private java.util.List data_;
+ /**
+ * repeated int32 data = 1;
+ */
+ public java.util.List
+ getDataList() {
+ return data_;
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public int getDataCount() {
+ return data_.size();
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public int getData(int index) {
+ return data_.get(index);
+ }
+
+ private byte memoizedIsInitialized = -1;
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized == 1) return true;
+ if (isInitialized == 0) return false;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ public void writeTo(com.google.protobuf.CodedOutputStream output)
+ throws java.io.IOException {
+ for (int i = 0; i < data_.size(); i++) {
+ output.writeInt32(1, data_.get(i));
+ }
+ unknownFields.writeTo(output);
+ }
+
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ {
+ int dataSize = 0;
+ for (int i = 0; i < data_.size(); i++) {
+ dataSize += com.google.protobuf.CodedOutputStream
+ .computeInt32SizeNoTag(data_.get(i));
+ }
+ size += dataSize;
+ size += 1 * getDataList().size();
+ }
+ size += unknownFields.getSerializedSize();
+ memoizedSize = size;
+ return size;
+ }
+
+ @java.lang.Override
+ public boolean equals(final java.lang.Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof com.mit.spbau.kirakosian.ArrayMessage.Array)) {
+ return super.equals(obj);
+ }
+ com.mit.spbau.kirakosian.ArrayMessage.Array other = (com.mit.spbau.kirakosian.ArrayMessage.Array) obj;
+
+ boolean result = true;
+ result = result && getDataList()
+ .equals(other.getDataList());
+ result = result && unknownFields.equals(other.unknownFields);
+ return result;
+ }
+
+ @java.lang.Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ int hash = 41;
+ hash = (19 * hash) + getDescriptor().hashCode();
+ if (getDataCount() > 0) {
+ hash = (37 * hash) + DATA_FIELD_NUMBER;
+ hash = (53 * hash) + getDataList().hashCode();
+ }
+ hash = (29 * hash) + unknownFields.hashCode();
+ memoizedHashCode = hash;
+ return hash;
+ }
+
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(
+ java.nio.ByteBuffer data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(
+ java.nio.ByteBuffer data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input, extensionRegistry);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseDelimitedWithIOException(PARSER, input);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input);
+ }
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder() {
+ return DEFAULT_INSTANCE.toBuilder();
+ }
+ public static Builder newBuilder(com.mit.spbau.kirakosian.ArrayMessage.Array prototype) {
+ return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+ }
+ public Builder toBuilder() {
+ return this == DEFAULT_INSTANCE
+ ? new Builder() : new Builder().mergeFrom(this);
+ }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(
+ com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+ /**
+ * Protobuf type {@code tutorial.Array}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessageV3.Builder implements
+ // @@protoc_insertion_point(builder_implements:tutorial.Array)
+ com.mit.spbau.kirakosian.ArrayMessage.ArrayOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ com.mit.spbau.kirakosian.ArrayMessage.Array.class, com.mit.spbau.kirakosian.ArrayMessage.Array.Builder.class);
+ }
+
+ // Construct using com.mit.spbau.kirakosian.ArrayMessage.Array.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(
+ com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessageV3
+ .alwaysUseFieldBuilders) {
+ }
+ }
+ public Builder clear() {
+ super.clear();
+ data_ = java.util.Collections.emptyList();
+ bitField0_ = (bitField0_ & ~0x00000001);
+ return this;
+ }
+
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_descriptor;
+ }
+
+ public com.mit.spbau.kirakosian.ArrayMessage.Array getDefaultInstanceForType() {
+ return com.mit.spbau.kirakosian.ArrayMessage.Array.getDefaultInstance();
+ }
+
+ public com.mit.spbau.kirakosian.ArrayMessage.Array build() {
+ com.mit.spbau.kirakosian.ArrayMessage.Array result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ public com.mit.spbau.kirakosian.ArrayMessage.Array buildPartial() {
+ com.mit.spbau.kirakosian.ArrayMessage.Array result = new com.mit.spbau.kirakosian.ArrayMessage.Array(this);
+ int from_bitField0_ = bitField0_;
+ if (((bitField0_ & 0x00000001) == 0x00000001)) {
+ data_ = java.util.Collections.unmodifiableList(data_);
+ bitField0_ = (bitField0_ & ~0x00000001);
+ }
+ result.data_ = data_;
+ onBuilt();
+ return result;
+ }
+
+ public Builder clone() {
+ return (Builder) super.clone();
+ }
+ public Builder setField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ java.lang.Object value) {
+ return (Builder) super.setField(field, value);
+ }
+ public Builder clearField(
+ com.google.protobuf.Descriptors.FieldDescriptor field) {
+ return (Builder) super.clearField(field);
+ }
+ public Builder clearOneof(
+ com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+ return (Builder) super.clearOneof(oneof);
+ }
+ public Builder setRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ int index, java.lang.Object value) {
+ return (Builder) super.setRepeatedField(field, index, value);
+ }
+ public Builder addRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ java.lang.Object value) {
+ return (Builder) super.addRepeatedField(field, value);
+ }
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof com.mit.spbau.kirakosian.ArrayMessage.Array) {
+ return mergeFrom((com.mit.spbau.kirakosian.ArrayMessage.Array)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(com.mit.spbau.kirakosian.ArrayMessage.Array other) {
+ if (other == com.mit.spbau.kirakosian.ArrayMessage.Array.getDefaultInstance()) return this;
+ if (!other.data_.isEmpty()) {
+ if (data_.isEmpty()) {
+ data_ = other.data_;
+ bitField0_ = (bitField0_ & ~0x00000001);
+ } else {
+ ensureDataIsMutable();
+ data_.addAll(other.data_);
+ }
+ onChanged();
+ }
+ this.mergeUnknownFields(other.unknownFields);
+ onChanged();
+ return this;
+ }
+
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ public Builder mergeFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ com.mit.spbau.kirakosian.ArrayMessage.Array parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (com.mit.spbau.kirakosian.ArrayMessage.Array) e.getUnfinishedMessage();
+ throw e.unwrapIOException();
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+ private int bitField0_;
+
+ private java.util.List data_ = java.util.Collections.emptyList();
+ private void ensureDataIsMutable() {
+ if (!((bitField0_ & 0x00000001) == 0x00000001)) {
+ data_ = new java.util.ArrayList(data_);
+ bitField0_ |= 0x00000001;
+ }
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public java.util.List
+ getDataList() {
+ return java.util.Collections.unmodifiableList(data_);
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public int getDataCount() {
+ return data_.size();
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public int getData(int index) {
+ return data_.get(index);
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public Builder setData(
+ int index, int value) {
+ ensureDataIsMutable();
+ data_.set(index, value);
+ onChanged();
+ return this;
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public Builder addData(int value) {
+ ensureDataIsMutable();
+ data_.add(value);
+ onChanged();
+ return this;
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public Builder addAllData(
+ java.lang.Iterable extends java.lang.Integer> values) {
+ ensureDataIsMutable();
+ com.google.protobuf.AbstractMessageLite.Builder.addAll(
+ values, data_);
+ onChanged();
+ return this;
+ }
+ /**
+ * repeated int32 data = 1;
+ */
+ public Builder clearData() {
+ data_ = java.util.Collections.emptyList();
+ bitField0_ = (bitField0_ & ~0x00000001);
+ onChanged();
+ return this;
+ }
+ public final Builder setUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.setUnknownFields(unknownFields);
+ }
+
+ public final Builder mergeUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.mergeUnknownFields(unknownFields);
+ }
+
+
+ // @@protoc_insertion_point(builder_scope:tutorial.Array)
+ }
+
+ // @@protoc_insertion_point(class_scope:tutorial.Array)
+ private static final com.mit.spbau.kirakosian.ArrayMessage.Array DEFAULT_INSTANCE;
+ static {
+ DEFAULT_INSTANCE = new com.mit.spbau.kirakosian.ArrayMessage.Array();
+ }
+
+ public static com.mit.spbau.kirakosian.ArrayMessage.Array getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ @java.lang.Deprecated public static final com.google.protobuf.Parser
+ PARSER = new com.google.protobuf.AbstractParser() {
+ public Array parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new Array(input, extensionRegistry);
+ }
+ };
+
+ public static com.google.protobuf.Parser parser() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ public com.mit.spbau.kirakosian.ArrayMessage.Array getDefaultInstanceForType() {
+ return DEFAULT_INSTANCE;
+ }
+
+ }
+
+ private static final com.google.protobuf.Descriptors.Descriptor
+ internal_static_tutorial_Array_descriptor;
+ private static final
+ com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internal_static_tutorial_Array_fieldAccessorTable;
+
+ public static com.google.protobuf.Descriptors.FileDescriptor
+ getDescriptor() {
+ return descriptor;
+ }
+ private static com.google.protobuf.Descriptors.FileDescriptor
+ descriptor;
+ static {
+ java.lang.String[] descriptorData = {
+ "\n\036src/main/resources/array.proto\022\010tutori" +
+ "al\"\025\n\005Array\022\014\n\004data\030\001 \003(\005B(\n\030com.mit.spb" +
+ "au.kirakosianB\014ArrayMessage"
+ };
+ com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
+ new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
+ public com.google.protobuf.ExtensionRegistry assignDescriptors(
+ com.google.protobuf.Descriptors.FileDescriptor root) {
+ descriptor = root;
+ return null;
+ }
+ };
+ com.google.protobuf.Descriptors.FileDescriptor
+ .internalBuildGeneratedFileFrom(descriptorData,
+ new com.google.protobuf.Descriptors.FileDescriptor[] {
+ }, assigner);
+ internal_static_tutorial_Array_descriptor =
+ getDescriptor().getMessageTypes().get(0);
+ internal_static_tutorial_Array_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+ internal_static_tutorial_Array_descriptor,
+ new java.lang.String[] { "Data", });
+ }
+
+ // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Main.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Main.java
new file mode 100644
index 0000000..12db9e4
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Main.java
@@ -0,0 +1,18 @@
+package com.mit.spbau.kirakosian;
+
+import com.mit.spbau.kirakosian.ui.SceneManager;
+import com.mit.spbau.kirakosian.ui.Window;
+
+import javax.swing.*;
+
+public class Main {
+
+ public static void main(final String[] args) {
+ SwingUtilities.invokeLater(() -> {
+ final Window window = new Window();
+ SceneManager.init(window);
+ window.setVisible(true);
+ });
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Protocol.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Protocol.java
new file mode 100644
index 0000000..bec995b
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Protocol.java
@@ -0,0 +1,8 @@
+package com.mit.spbau.kirakosian;
+
+public class Protocol {
+
+ public static final int NEW_QUERY = 0;
+ public static final int STOP = 1;
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Utils.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Utils.java
new file mode 100644
index 0000000..ced851a
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Utils.java
@@ -0,0 +1,72 @@
+package com.mit.spbau.kirakosian;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import org.apache.commons.io.input.BoundedInputStream;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+@SuppressWarnings("WeakerAccess")
+public class Utils {
+
+ public static void writeArray(final DataOutputStream os, final int[] array) throws IOException {
+ final byte[] bytes = makeArrayMessage(array);
+ os.writeInt(bytes.length);
+ os.write(bytes);
+ }
+
+ public static int[] readArray(final DataInputStream is) throws IOException {
+ final int size = is.readInt();
+ final BoundedInputStream bis = new BoundedInputStream(is, size);
+ return listToArray(ArrayMessage.Array.parseFrom(bis).getDataList());
+ }
+
+ public static int[] getArray(final byte[] array) throws InvalidProtocolBufferException {
+ return listToArray(ArrayMessage.Array.parseFrom(array).getDataList());
+ }
+
+ public static void sort(final int[] array) {
+ for (int i = 0; i < array.length; i++) {
+ for (int j = i; j < array.length; j++) {
+ if (array[i] > array[j]) {
+ final int tmp = array[j];
+ array[j] = array[i];
+ array[i] = tmp;
+ }
+ }
+ }
+ }
+
+ public static int[] generate(final int size) {
+ final Random random = new Random();
+ final int array[] = new int[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = random.nextInt();
+ }
+ return array;
+ }
+
+ public static byte[] makeArrayMessage(final int[] array) {
+ return ArrayMessage.Array.newBuilder().addAllData(arrayToList(array)).build().toByteArray();
+ }
+
+ public static int[] listToArray(final List list) {
+ final int[] array = new int[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ array[i] = list.get(i);
+ }
+ return array;
+ }
+
+ public static List arrayToList(final int[] array) {
+ final List list = new ArrayList<>();
+ for (final int i : array) {
+ list.add(i);
+ }
+ return list;
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/ArrayClient.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/ArrayClient.java
new file mode 100644
index 0000000..1474910
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/ArrayClient.java
@@ -0,0 +1,59 @@
+package com.mit.spbau.kirakosian.client;
+
+import com.mit.spbau.kirakosian.Utils;
+import com.mit.spbau.kirakosian.servers.impl.AbstractServer;
+
+import java.io.*;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import static com.mit.spbau.kirakosian.Protocol.*;
+
+class ArrayClient {
+
+ private long time;
+ private final Socket socket;
+
+ ArrayClient(final String ip) throws IOException {
+ socket = new Socket(InetAddress.getByName(ip), AbstractServer.PORT);
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public long getTime() {
+ return time;
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public void work(final int queries, final int size, final int delay) throws IOException {
+ try (final DataOutputStream os = new DataOutputStream(socket.getOutputStream());
+ final DataInputStream is = new DataInputStream(socket.getInputStream())) {
+ final long begin = System.currentTimeMillis();
+ for (int i = 0; i < queries; i++) {
+ final int[] array = Utils.generate(size);
+
+ os.writeInt(NEW_QUERY);
+ Utils.writeArray(os, array);
+ os.flush();
+
+ @SuppressWarnings("unused") final int[] result = Utils.readArray(is);
+
+ try {
+ Thread.sleep(delay);
+ } catch (final InterruptedException e) {
+ // unlucky
+ }
+ }
+ os.writeInt(STOP);
+ os.flush();
+ final long end = System.currentTimeMillis();
+ time = end - begin;
+ } catch (final IOException e) {
+ try {
+ socket.close();
+ } catch (final IOException e1) {
+ // do nothing
+ }
+ throw e;
+ }
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/AutoOptions.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/AutoOptions.java
new file mode 100644
index 0000000..fd43a7d
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/AutoOptions.java
@@ -0,0 +1,53 @@
+package com.mit.spbau.kirakosian.client;
+
+import com.mit.spbau.kirakosian.options.TestOptions;
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize;
+import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.Delay;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@SuppressWarnings("WeakerAccess")
+public class AutoOptions {
+
+ private final Map, Integer> map = new HashMap<>();
+ private final Class extends ParameterOptionMeta> altering;
+ private final int delta;
+ private final int max;
+
+ public AutoOptions(final TestOptions options) {
+ delta = options.delta();
+ max = options.upperBound();
+ map.putAll(options.options());
+ altering = options.alteringOption();
+ map.put(altering, options.lowerBound());
+ }
+
+ public void next() {
+ final int value = map.get(altering);
+ map.put(altering, value + delta);
+ }
+
+ public boolean notFinished() {
+ return map.get(altering) <= max;
+ }
+
+ public int clients() {
+ return map.get(ClientsNumber.class);
+ }
+
+ public int queries() {
+ return map.get(QueriesNumber.class);
+ }
+
+ public int arraySize() {
+ return map.get(ArraySize.class);
+ }
+
+ public int delay() {
+ return map.get(Delay.class);
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/TestingClient.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/TestingClient.java
new file mode 100644
index 0000000..ef86d3b
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/TestingClient.java
@@ -0,0 +1,83 @@
+package com.mit.spbau.kirakosian.client;
+
+import com.mit.spbau.kirakosian.connector.ApplicationConnector;
+import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException;
+import com.mit.spbau.kirakosian.options.TestOptions;
+
+import java.io.*;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.mit.spbau.kirakosian.connector.Protocol.*;
+
+public class TestingClient {
+
+ private final AutoOptions options;
+ final private ObjectOutputStream os;
+ final private ObjectInputStream is;
+
+ private final String ip;
+
+ private TestingClient(final String ip) throws IOException, ClassNotFoundException {
+ final Socket socket = new Socket(InetAddress.getByName(ip), ApplicationConnector.APPLICATION_PORT);
+ this.ip = ip;
+ os = new ObjectOutputStream(socket.getOutputStream());
+ is = new ObjectInputStream(socket.getInputStream());
+ if (is.readInt() != OPTIONS) {
+ throw new UnexpectedProtocolMessageException();
+ }
+ options = new AutoOptions((TestOptions) is.readObject());
+ }
+
+ private void startTest() throws IOException {
+ while (options.notFinished()) {
+ System.out.println(options.arraySize() + " " + options.clients() + " " + options.queries() + " " + options.delay());
+ if (is.readInt() != START_TEST_CASE) {
+ throw new UnexpectedProtocolMessageException();
+ }
+
+ final Map threads = new HashMap<>();
+ for (int i = 0; i < options.clients(); i++) {
+ final ArrayClient client = new ArrayClient(ip);
+ threads.put(new Thread(() -> {
+ try {
+ client.work(options.queries(), options.arraySize(), options.delay());
+ } catch (final IOException e) {
+ // do nothing
+ }
+ }), client);
+ }
+ for (final Thread thread : threads.keySet()) {
+ thread.start();
+ }
+ for (final Map.Entry entry : threads.entrySet()) {
+ try {
+ entry.getKey().join();
+ os.writeInt(CLIENT_TIME);
+ os.writeLong(entry.getValue().getTime() / options.queries());
+ os.flush();
+ } catch (final InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ os.writeInt(END_TEST_CASE);
+ os.flush();
+ options.next();
+ }
+ }
+
+ public static void main(final String[] args) throws IOException, ClassNotFoundException {
+ final String ip;
+ if (args.length == 0) {
+ ip = "127.0.0.1";
+ } else {
+ ip = args[0];
+ }
+ final TestingClient client = new TestingClient(ip);
+ client.startTest();
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/ApplicationConnector.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/ApplicationConnector.java
new file mode 100644
index 0000000..253ecfa
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/ApplicationConnector.java
@@ -0,0 +1,103 @@
+package com.mit.spbau.kirakosian.connector;
+
+import com.mit.spbau.kirakosian.options.TestOptions;
+import com.mit.spbau.kirakosian.testing.StatsListener;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import static com.mit.spbau.kirakosian.connector.Protocol.*;
+
+public class ApplicationConnector {
+
+ public final static int APPLICATION_PORT = 8093;
+
+ private final ServerSocket server;
+ private Socket socket;
+ private ObjectInputStream is;
+ private ObjectOutputStream os;
+
+ private final TestOptions options;
+ private final StatsListener stats;
+
+ public ApplicationConnector(final TestOptions options, final StatsListener stats) throws IOException {
+ this.options = options;
+ this.stats = stats;
+ server = new ServerSocket(APPLICATION_PORT);
+ }
+
+ public void waitForNewClient() throws IOException {
+ try {
+ socket = server.accept();
+ } catch (IOException e) {
+ try {
+ server.close();
+ } catch (IOException e1) {
+ // ignore error during close
+ }
+ throw e;
+ }
+ processSocket(socket);
+ }
+
+ private void processSocket(final Socket socket) throws IOException {
+ try {
+ is = new ObjectInputStream(socket.getInputStream());
+ os = new ObjectOutputStream(socket.getOutputStream());
+
+ os.writeInt(OPTIONS);
+ os.writeObject(options);
+ os.flush();
+ } catch (final IOException e) {
+ try {
+ closeAll();
+ } catch (final IOException e1) {
+ // ignore close exception
+ }
+ throw e;
+ }
+ }
+
+ private void closeAll() throws IOException {
+ is.close(); // try to close resources
+ os.close();
+ socket.close();
+ server.close();
+ }
+
+ public void startTestCase() throws IOException {
+ try {
+ os.writeInt(START_TEST_CASE);
+ os.flush();
+
+ int signal;
+ signal = is.readInt();
+ while (true) {
+ if (signal == END_TEST_CASE) {
+ stats.done();
+ break;
+ } else if (signal == CLIENT_TIME) {
+ final long time = is.readLong();
+ stats.timeForClientOnClient(time);
+ } else {
+ throw new UnexpectedProtocolMessageException();
+ }
+ signal = is.readInt();
+ }
+ } catch (final IOException e) {
+ try {
+ closeAll();
+ } catch (final IOException e1) {
+ // ignore close exception
+ }
+ throw e;
+ }
+ }
+
+ public void close() throws IOException {
+ closeAll();
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/Protocol.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/Protocol.java
new file mode 100644
index 0000000..d21553f
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/Protocol.java
@@ -0,0 +1,10 @@
+package com.mit.spbau.kirakosian.connector;
+
+public class Protocol {
+
+ public final static int OPTIONS = 0;
+ public final static int START_TEST_CASE = 1;
+ public static final int END_TEST_CASE = 2;
+ public static final int CLIENT_TIME = 3;
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/UnexpectedProtocolMessageException.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/UnexpectedProtocolMessageException.java
new file mode 100644
index 0000000..98b7f64
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/UnexpectedProtocolMessageException.java
@@ -0,0 +1,5 @@
+package com.mit.spbau.kirakosian.connector;
+
+public class UnexpectedProtocolMessageException extends RuntimeException {
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/Controller.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/Controller.java
new file mode 100644
index 0000000..4beba81
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/Controller.java
@@ -0,0 +1,26 @@
+package com.mit.spbau.kirakosian.controller;
+
+import com.mit.spbau.kirakosian.testing.TestResults;
+import com.mit.spbau.kirakosian.ui.SceneManager;
+
+import javax.swing.*;
+
+public class Controller {
+
+ public static void calculationsCompleted(final TestResults results) {
+ ResultsWriter.saveResults(results);
+ SceneManager.RESULTS_SCENE.acceptResults(results);
+ SwingUtilities.invokeLater(() -> SceneManager.setScene(SceneManager.RESULTS_SCENE));
+ }
+
+ public static void calculationsCompletedDebug(final TestResults results) {
+ ResultsWriter.saveResults(results);
+// SceneManager.RESULTS_SCENE.acceptResults(results);
+// SwingUtilities.invokeLater(() -> SceneManager.setScene(SceneManager.RESULTS_SCENE));
+ }
+
+ public static void cancel() {
+ SwingUtilities.invokeLater(() -> SceneManager.setScene(SceneManager.SETTINGS_SCENE));
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/ResultsWriter.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/ResultsWriter.java
new file mode 100644
index 0000000..779331d
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/ResultsWriter.java
@@ -0,0 +1,73 @@
+package com.mit.spbau.kirakosian.controller;
+
+import com.mit.spbau.kirakosian.options.TestOptions;
+import com.mit.spbau.kirakosian.options.Utils;
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+import com.mit.spbau.kirakosian.options.metrics.MetricResult;
+import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime;
+import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize;
+import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.Delay;
+import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber;
+import com.mit.spbau.kirakosian.testing.TestResults;
+
+import java.io.*;
+import java.util.Map;
+
+class ResultsWriter {
+
+ static void saveResults(final TestResults results) {
+ final String name = getName(results);
+ final File file = new File("results/logs/" + name);
+ if (!file.exists()) {
+ try {
+ //noinspection ResultOfMethodCallIgnored
+ file.createNewFile();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ }
+ final TestOptions options = results.options();
+ //noinspection TryWithIdenticalCatches
+ try (final Writer writer = new PrintWriter(file)) {
+ writer.write("Server type: " + options.serverType().name());
+ writer.write("Array size: " + options.getOption(ArraySize.class) + "\n");
+ writer.write("Clients number: " + options.getOption(ClientsNumber.class) + "\n");
+ writer.write("Delay: " + options.getOption(Delay.class) + "\n");
+ writer.write("Queries number: " + options.getOption(QueriesNumber.class) + "\n");
+
+ writeAlteringMetric(writer, options);
+
+ writeResultsForMetric(writer, results, TaskTime.class);
+ writeResultsForMetric(writer, results, ClientTime.class);
+ writeResultsForMetric(writer, results, ServerTime.class);
+ } catch (final FileNotFoundException e) {
+ // should never happen
+ } catch (final IOException e) {
+ // just ignore
+ }
+ }
+
+ private static void writeAlteringMetric(final Writer writer, final TestOptions options) throws IOException {
+ writer.write("Altering option: " + options.alteringOption().getSimpleName() + "\n");
+ writer.write(options.lowerBound() + " " + options.upperBound() + " " + options.delta() + "\n");
+ }
+
+ private static void writeResultsForMetric(final Writer writer,
+ final TestResults results,
+ final Class extends MetricMeta> meta) throws IOException {
+ writer.write(meta.getSimpleName() + "\n");
+ final MetricResult metricResult = results.getResultsForMetric(meta);
+ writer.write(metricResult.results().size() + "\n");
+ for (final Map.Entry entry : metricResult.results().entrySet()) {
+ writer.write(entry.getKey() + " " + entry.getValue() + "\n");
+ }
+ }
+
+ private static String getName(final TestResults results) {
+ return results.options().serverType() + "_" + results.options().alteringOption().getSimpleName() + ".txt";
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/TestOptions.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/TestOptions.java
new file mode 100644
index 0000000..cd9dbe5
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/TestOptions.java
@@ -0,0 +1,147 @@
+package com.mit.spbau.kirakosian.options;
+
+import com.mit.spbau.kirakosian.testing.ServerTestInitializer;
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+import com.mit.spbau.kirakosian.servers.Servers;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Stores all chosen in GUI options. This class is used to pass these options fro, GUI to logic.
+ * Every required by logic option should must be set.
+ *
+ * Also any valid option class must be taken correctly by logic. See {@link TestOptions#validate()}
+ * method for more information.
+ */
+public class TestOptions implements Serializable {
+
+ private final Map, Integer> options = new HashMap<>();
+ private Servers.ServerType serverType;
+
+ private final Set> metrics = new HashSet<>();
+
+ private Class extends ParameterOptionMeta> alteringOptionMeta;
+ private int lowerBound;
+ private int upperBound;
+ private int delta;
+
+ public Map, Integer> options() {
+ return options;
+ }
+
+ public Servers.ServerType serverType() {
+ return serverType;
+ }
+
+ public void setServerType(final Servers.ServerType serverType) {
+ this.serverType = serverType;
+ }
+
+ public Class extends ParameterOptionMeta> alteringOption() {
+ return alteringOptionMeta;
+ }
+
+ public void setAlteringOptionMeta(final Class extends ParameterOptionMeta> alteringOptionMeta) {
+ this.alteringOptionMeta = alteringOptionMeta;
+ }
+
+ public int lowerBound() {
+ return lowerBound;
+ }
+
+ public void setLowerBound(final int lowerBound) {
+ this.lowerBound = lowerBound;
+ }
+
+ public int upperBound() {
+ return upperBound;
+ }
+
+ public void setUpperBound(final int upperBound) {
+ this.upperBound = upperBound;
+ }
+
+ public int delta() {
+ return delta;
+ }
+
+ public void setDelta(final int delta) {
+ this.delta = delta;
+ }
+
+ public void setOption(final Class extends ParameterOptionMeta> option, final Integer value) {
+ options.put(option, value);
+ }
+
+ public Integer getOption(final Class extends ParameterOptionMeta> meta) {
+ return options.get(meta);
+ }
+
+ public void addMetric(final Class extends MetricMeta> metric) {
+ metrics.add(metric);
+ }
+
+ public boolean requiresMetric(final Class extends MetricMeta> metric) {
+ return metrics.contains(metric);
+ }
+
+ public Set> metrics() {
+ return metrics;
+ }
+
+ /**
+ * The method checks if provided options are legal (for example, altering
+ * property was provided).
+ *
+ * @return string with error message or null if data was legal.
+ */
+ public String validate() {
+ if (serverType == null) {
+ return "Server type was not provided.";
+ }
+ if (alteringOptionMeta == null) {
+ return "Altering property was not provided.";
+ }
+ final ParameterOptionMeta alteringOption = Utils.getOptionInstance(alteringOptionMeta);
+ if (!alteringOption.mayAlter()) {
+ return "Option " + alteringOptionMeta.getSimpleName() + " cannot alter.";
+ }
+ for (final Class extends ParameterOptionMeta> option : ServerTestInitializer.getRequiredOptions()) {
+ if (options.get(option) == null && alteringOptionMeta != option) {
+ return "Option " + option.getSimpleName() + " was not provided";
+ }
+ }
+ for (final Class extends MetricMeta> metric : metrics) {
+ if (!ServerTestInitializer.getSupportedMetrics().contains(metric)) {
+ return "Metric " + metric.getSimpleName() + " is unsupported";
+ }
+ }
+ if (lowerBound > upperBound) {
+ return "Min property value is greater then max value";
+ }
+ if (lowerBound < alteringOption.minValue()) {
+ return "Illegal max value";
+ }
+ if (upperBound > alteringOption.maxValue()) {
+ return "Illegal in value";
+ }
+ if (delta <= 0) {
+ return "Delta must be positive";
+ }
+ return null;
+ }
+
+ public void print() {
+ for (final Map.Entry, Integer> meta : options.entrySet()) {
+ System.out.println(meta.getKey().getSimpleName() + " has value: " + meta.getValue());
+ }
+ System.out.println(alteringOption().getSimpleName() + " is altering from "
+ + lowerBound + " to " + upperBound + " with delta " + delta);
+ System.out.println("Server type is: " + serverType.name());
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/UIOptions.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/UIOptions.java
new file mode 100644
index 0000000..d28eddd
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/UIOptions.java
@@ -0,0 +1,67 @@
+package com.mit.spbau.kirakosian.options;
+
+
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime;
+import com.mit.spbau.kirakosian.options.parameters.*;
+import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize;
+import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.Delay;
+import com.mit.spbau.kirakosian.servers.Servers;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class represents options supported by GUI. Not all described options/metrics might be
+ * supported by logic.
+ */
+public class UIOptions {
+
+ private static final List options = new ArrayList<>();
+ private static final Set serverTypes = new HashSet<>();
+
+ private static final List> metrics = new ArrayList<>();
+
+ static {
+ serverTypes.add(Servers.ServerType.Simple);
+ serverTypes.add(Servers.ServerType.Blocking);
+ serverTypes.add(Servers.ServerType.NonBlocking);
+
+ metrics.add(ServerTime.class);
+ metrics.add(ClientTime.class);
+ metrics.add(TaskTime.class);
+ try {
+ registerOption(ArraySize.class);
+ registerOption(ClientsNumber.class);
+ registerOption(Delay.class);
+ registerOption(QueriesNumber.class);
+ } catch (final NoSuchMethodException | InvocationTargetException |
+ IllegalAccessException | InstantiationException e) {
+ throw new RuntimeException("Error occurred during initialization", e);
+ }
+ }
+
+ private static void registerOption(final Class extends ParameterOptionMeta> clazz) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
+ options.add(clazz.getDeclaredConstructor().newInstance());
+ }
+
+ public static List options() {
+ return options;
+ }
+
+ public static Set serverOptions() {
+ return serverTypes;
+ }
+
+ public static List> metrics() {
+ return metrics;
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/Utils.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/Utils.java
new file mode 100644
index 0000000..7fe620e
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/Utils.java
@@ -0,0 +1,77 @@
+package com.mit.spbau.kirakosian.options;
+
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime;
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize;
+import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.Delay;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Utils {
+
+ private static final Map, MetricMeta> metricMap = new HashMap<>();
+ private static final Map, ParameterOptionMeta> optionsMap = new HashMap<>();
+
+ static {
+ try {
+ registerOption(ArraySize.class);
+ registerOption(ClientsNumber.class);
+ registerOption(Delay.class);
+ registerOption(QueriesNumber.class);
+
+ registerMetric(ClientTime.class);
+ registerMetric(ServerTime.class);
+ registerMetric(TaskTime.class);
+ } catch (final NoSuchMethodException | InvocationTargetException |
+ IllegalAccessException | InstantiationException e) {
+ throw new RuntimeException("Error occurred during initialization", e);
+ }
+ }
+
+ private static void registerOption(final Class extends ParameterOptionMeta> meta) throws NoSuchMethodException,
+ IllegalAccessException, InvocationTargetException, InstantiationException {
+ optionsMap.put(meta, meta.getDeclaredConstructor().newInstance());
+ }
+
+ private static void registerMetric(final Class extends MetricMeta> meta) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
+ metricMap.put(meta, meta.getDeclaredConstructor().newInstance());
+ }
+
+ public static MetricMeta getMetricInstance(final Class extends MetricMeta> meta) {
+ if (metricMap.containsKey(meta)) {
+ return metricMap.get(meta);
+ } else {
+ final MetricMeta instance;
+ try {
+ instance = meta.getConstructor().newInstance();
+ } catch (final InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new RuntimeException(); // should never happen
+ }
+ metricMap.put(meta, instance);
+ return instance;
+ }
+ }
+
+ public static ParameterOptionMeta getOptionInstance(final Class extends ParameterOptionMeta> meta) {
+ if (optionsMap.containsKey(meta)) {
+ return optionsMap.get(meta);
+ } else {
+ final ParameterOptionMeta instance;
+ try {
+ instance = meta.getConstructor().newInstance();
+ } catch (final InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new RuntimeException(); // should never happen
+ }
+ optionsMap.put(meta, instance);
+ return instance;
+ }
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricMeta.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricMeta.java
new file mode 100644
index 0000000..d86d50b
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricMeta.java
@@ -0,0 +1,10 @@
+package com.mit.spbau.kirakosian.options.metrics;
+
+public interface MetricMeta {
+
+ String name();
+
+ String description();
+
+ String shortName();
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricResult.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricResult.java
new file mode 100644
index 0000000..c6bc557
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricResult.java
@@ -0,0 +1,27 @@
+package com.mit.spbau.kirakosian.options.metrics;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+public class MetricResult {
+
+ private final Class extends MetricMeta> meta;
+
+ private final Map results = new TreeMap<>();
+
+ public MetricResult(final Class extends MetricMeta> meta) {
+ this.meta = meta;
+ }
+
+ public Class extends MetricMeta> meta() {
+ return meta;
+ }
+
+ public void addResult(final Integer parameter, final Number value) {
+ results.put(parameter, value);
+ }
+
+ public Map results() {
+ return results;
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ClientTime.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ClientTime.java
new file mode 100644
index 0000000..329af58
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ClientTime.java
@@ -0,0 +1,21 @@
+package com.mit.spbau.kirakosian.options.metrics.impl;
+
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+
+public class ClientTime implements MetricMeta {
+
+ @Override
+ public String name() {
+ return "Client time";
+ }
+
+ @Override
+ public String description() {
+ return "Time elapsed between sending task and getting response";
+ }
+
+ @Override
+ public String shortName() {
+ return "C";
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ServerTime.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ServerTime.java
new file mode 100644
index 0000000..0d65fb0
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ServerTime.java
@@ -0,0 +1,21 @@
+package com.mit.spbau.kirakosian.options.metrics.impl;
+
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+
+public class ServerTime implements MetricMeta {
+
+ @Override
+ public String name() {
+ return "Server time";
+ }
+
+ @Override
+ public String description() {
+ return "Time elapsed between getting task and sending answer";
+ }
+
+ @Override
+ public String shortName() {
+ return "S";
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/TaskTime.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/TaskTime.java
new file mode 100644
index 0000000..505e98b
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/TaskTime.java
@@ -0,0 +1,21 @@
+package com.mit.spbau.kirakosian.options.metrics.impl;
+
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+
+public class TaskTime implements MetricMeta {
+
+ @Override
+ public String name() {
+ return "Task time";
+ }
+
+ @Override
+ public String description() {
+ return "Time for completing one task";
+ }
+
+ @Override
+ public String shortName() {
+ return "T";
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/ParameterOptionMeta.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/ParameterOptionMeta.java
new file mode 100644
index 0000000..2898370
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/ParameterOptionMeta.java
@@ -0,0 +1,16 @@
+package com.mit.spbau.kirakosian.options.parameters;
+
+public interface ParameterOptionMeta {
+
+ String name();
+
+ int minValue();
+
+ int maxValue();
+
+ String description();
+
+ boolean mayAlter();
+
+ String shortName();
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ArraySize.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ArraySize.java
new file mode 100644
index 0000000..5844f2e
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ArraySize.java
@@ -0,0 +1,36 @@
+package com.mit.spbau.kirakosian.options.parameters.impl;
+
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+
+public class ArraySize implements ParameterOptionMeta {
+
+ @Override
+ public String name() {
+ return "Array size";
+ }
+
+ @Override
+ public String description() {
+ return "Size of array for each query";
+ }
+
+ @Override
+ public int minValue() {
+ return 0;
+ }
+
+ @Override
+ public int maxValue() {
+ return 10000;
+ }
+
+ @Override
+ public boolean mayAlter() {
+ return true;
+ }
+
+ @Override
+ public String shortName() {
+ return "A";
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ClientsNumber.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ClientsNumber.java
new file mode 100644
index 0000000..a2ef238
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ClientsNumber.java
@@ -0,0 +1,36 @@
+package com.mit.spbau.kirakosian.options.parameters.impl;
+
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+
+public class ClientsNumber implements ParameterOptionMeta {
+
+ @Override
+ public String name() {
+ return "Number of clients";
+ }
+
+ @Override
+ public String description() {
+ return "Number of clients working at the same time";
+ }
+
+ @Override
+ public int minValue() {
+ return 1;
+ }
+
+ @Override
+ public int maxValue() {
+ return 101;
+ }
+
+ @Override
+ public boolean mayAlter() {
+ return true;
+ }
+
+ @Override
+ public String shortName() {
+ return "C ";
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/Delay.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/Delay.java
new file mode 100644
index 0000000..1372ccf
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/Delay.java
@@ -0,0 +1,36 @@
+package com.mit.spbau.kirakosian.options.parameters.impl;
+
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+
+public class Delay implements ParameterOptionMeta {
+
+ @Override
+ public String name() {
+ return "Time space between queries";
+ }
+
+ @Override
+ public String description() {
+ return "Time before responce from server and new query";
+ }
+
+ @Override
+ public int minValue() {
+ return 0;
+ }
+
+ @Override
+ public int maxValue() {
+ return 10_000;
+ }
+
+ @Override
+ public boolean mayAlter() {
+ return true;
+ }
+
+ @Override
+ public String shortName() {
+ return "D";
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/QueriesNumber.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/QueriesNumber.java
new file mode 100644
index 0000000..1f71a5e
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/QueriesNumber.java
@@ -0,0 +1,36 @@
+package com.mit.spbau.kirakosian.options.parameters.impl;
+
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+
+public class QueriesNumber implements ParameterOptionMeta {
+
+ @Override
+ public String name() {
+ return "Number of queries";
+ }
+
+ @Override
+ public String description() {
+ return "Number of queries from each client";
+ }
+
+ @Override
+ public int minValue() {
+ return 1;
+ }
+
+ @Override
+ public int maxValue() {
+ return 53;
+ }
+
+ @Override
+ public boolean mayAlter() {
+ return false;
+ }
+
+ @Override
+ public String shortName() {
+ return "Q";
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Server.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Server.java
new file mode 100644
index 0000000..ec0d845
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Server.java
@@ -0,0 +1,21 @@
+package com.mit.spbau.kirakosian.servers;
+
+import com.mit.spbau.kirakosian.servers.exceptions.AbortException;
+
+/**
+ * Server interface. Might have different architectures.
+ */
+public interface Server {
+
+ void start();
+
+ /**
+ * Turns off the server. It is required, that any implementation releases
+
+ * every port, used by server.
+ */
+ void shutDown();
+
+ void setServerActionListener(ServerStatsListener actionListener);
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/ServerStatsListener.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/ServerStatsListener.java
new file mode 100644
index 0000000..ba274ff
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/ServerStatsListener.java
@@ -0,0 +1,12 @@
+package com.mit.spbau.kirakosian.servers;
+
+public interface ServerStatsListener {
+
+ default void done() {}
+
+ default void timeForTask(final long time) {}
+
+ default void timeForClientOnServer(final long time) {}
+
+ void fail(final Exception e);
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Servers.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Servers.java
new file mode 100644
index 0000000..6d41968
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Servers.java
@@ -0,0 +1,28 @@
+package com.mit.spbau.kirakosian.servers;
+
+import com.mit.spbau.kirakosian.servers.exceptions.AbortException;
+import com.mit.spbau.kirakosian.servers.impl.blockingServer.BlockingServer;
+import com.mit.spbau.kirakosian.servers.impl.nonBlockingServer.NonBlockingServer;
+import com.mit.spbau.kirakosian.servers.impl.simpleServer.SimpleServer;
+
+public class Servers {
+
+ public static Server createServer(final ServerType type) throws AbortException {
+ switch (type) {
+ case Simple:
+ return new SimpleServer();
+ case Blocking:
+ return new BlockingServer();
+ case NonBlocking:
+ return new NonBlockingServer();
+ }
+ throw new UnknownServerTypeException();
+ }
+
+ private static class UnknownServerTypeException extends RuntimeException {
+ }
+
+ public enum ServerType {
+ Simple, Blocking, NonBlocking
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/exceptions/AbortException.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/exceptions/AbortException.java
new file mode 100644
index 0000000..a1bef6b
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/exceptions/AbortException.java
@@ -0,0 +1,5 @@
+package com.mit.spbau.kirakosian.servers.exceptions;
+
+public class AbortException extends Throwable {
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractBlockingServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractBlockingServer.java
new file mode 100644
index 0000000..3e8116d
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractBlockingServer.java
@@ -0,0 +1,71 @@
+package com.mit.spbau.kirakosian.servers.impl;
+
+import com.mit.spbau.kirakosian.servers.exceptions.AbortException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+
+public abstract class AbstractBlockingServer extends AbstractServer {
+
+ private final @NotNull ServerSocket server;
+ private volatile boolean working;
+ private Thread mainThread;
+
+ public AbstractBlockingServer() throws AbortException {
+ try {
+ server = new ServerSocket(PORT);
+ } catch (final IOException e) {
+ throw new AbortException();
+ }
+ }
+
+ protected abstract void processConnection(final Socket socket);
+
+ @Override
+ public void start() {
+ working = true;
+ mainThread = new Thread(this::work);
+ mainThread.start();
+ }
+
+ private void work() {
+ while (working) {
+ final Socket socket;
+ try {
+ socket = server.accept();
+ new Thread(() -> processConnection(socket)).start();
+ } catch (final IOException e) {
+ if (e instanceof SocketException && !working) {
+ break;
+ }
+ listener.fail(e);
+ break;
+ }
+ }
+
+ if (!server.isClosed()) {
+ try {
+ server.close();
+ } catch (final IOException e) {
+ listener.fail(e);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void shutDown() {
+ if (!working) {
+ return;
+ }
+ working = false;
+ try {
+ server.close();
+ mainThread.join();
+ } catch (final IOException | InterruptedException e) {
+ listener.fail(e);
+ }
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractServer.java
new file mode 100644
index 0000000..817cc47
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractServer.java
@@ -0,0 +1,15 @@
+package com.mit.spbau.kirakosian.servers.impl;
+
+import com.mit.spbau.kirakosian.servers.Server;
+import com.mit.spbau.kirakosian.servers.ServerStatsListener;
+
+public abstract class AbstractServer implements Server {
+
+ public static final int PORT = 8099;
+ protected ServerStatsListener listener;
+
+ @Override
+ public void setServerActionListener(final ServerStatsListener actionListener) {
+ listener = actionListener;
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/BlockingServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/BlockingServer.java
new file mode 100644
index 0000000..9ae6e22
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/BlockingServer.java
@@ -0,0 +1,32 @@
+package com.mit.spbau.kirakosian.servers.impl.blockingServer;
+
+import com.mit.spbau.kirakosian.servers.ServerStatsListener;
+import com.mit.spbau.kirakosian.servers.exceptions.AbortException;
+import com.mit.spbau.kirakosian.servers.impl.AbstractBlockingServer;
+
+import java.net.Socket;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@SuppressWarnings("WeakerAccess")
+public class BlockingServer extends AbstractBlockingServer {
+
+ private final ExecutorService pool = Executors.newFixedThreadPool(3);
+
+ public BlockingServer() throws AbortException {
+ super();
+ }
+
+ @Override
+ protected void processConnection(final Socket socket) {
+ new Session(socket, this);
+ }
+
+ public ExecutorService getPool() {
+ return pool;
+ }
+
+ public ServerStatsListener getListener() {
+ return listener;
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/Session.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/Session.java
new file mode 100644
index 0000000..a5ef379
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/Session.java
@@ -0,0 +1,112 @@
+package com.mit.spbau.kirakosian.servers.impl.blockingServer;
+
+import com.mit.spbau.kirakosian.Utils;
+import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException;
+import com.mit.spbau.kirakosian.servers.ServerStatsListener;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static com.mit.spbau.kirakosian.Protocol.NEW_QUERY;
+import static com.mit.spbau.kirakosian.Protocol.STOP;
+
+class Session {
+
+ private final Executor writer = Executors.newSingleThreadExecutor();
+ private final ExecutorService pool;
+ private final ServerStatsListener listener;
+
+ private final Socket socket;
+ private DataOutputStream os;
+
+ Session(final Socket socket, final BlockingServer server) {
+ pool = server.getPool();
+ this.socket = socket;
+ listener = server.getListener();
+ try {
+ os = new DataOutputStream(socket.getOutputStream());
+ } catch (final IOException e) {
+ try {
+ socket.close();
+ } catch (final IOException e1) {
+ listener.fail(e1);
+ }
+ listener.fail(e);
+ }
+ new Thread(this::processReading).start();
+ }
+
+ private void processReading() {
+ try (final DataInputStream is = new DataInputStream(socket.getInputStream())) {
+ int signal;
+ signal = is.readInt();
+ while (signal != STOP) {
+ if (signal != NEW_QUERY) {
+ throw new UnexpectedProtocolMessageException();
+ }
+
+ final long queryBegin = System.currentTimeMillis();
+ final int[] array = Utils.readArray(is);
+ pool.submit(new SortTask(array, queryBegin));
+
+ signal = is.readInt();
+ }
+
+ } catch (final IOException e) {
+ try {
+ socket.close();
+ } catch (final IOException e1) {
+ listener.fail(e1);
+ }
+ listener.fail(e);
+ }
+ }
+
+ private class SortTask implements Runnable {
+
+ private final long queryBegin;
+ private final int[] array;
+
+ SortTask(final int[] array, final long queryBegin) {
+ this.queryBegin = queryBegin;
+ this.array = array;
+ }
+
+ @Override
+ public void run() {
+ final long begin = System.currentTimeMillis();
+ Utils.sort(array);
+ final long end = System.currentTimeMillis();
+ listener.timeForTask(end - begin);
+ writer.execute(new SendBackTask(array, queryBegin));
+ }
+ }
+
+ private class SendBackTask implements Runnable {
+
+ private final long queryBegin;
+ private final int[] array;
+
+ SendBackTask(final int[] array, final long queryBegin) {
+ this.queryBegin = queryBegin;
+ this.array = array;
+ }
+
+
+ @Override
+ public void run() {
+ try {
+ Utils.writeArray(os, array);
+ final long queryEnd = System.currentTimeMillis();
+ listener.timeForClientOnServer(queryEnd - queryBegin);
+ } catch (final IOException e) {
+ listener.fail(e);
+ }
+ }
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/Client.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/Client.java
new file mode 100644
index 0000000..797482f
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/Client.java
@@ -0,0 +1,125 @@
+package com.mit.spbau.kirakosian.servers.impl.nonBlockingServer;
+
+import com.mit.spbau.kirakosian.Utils;
+import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+
+import static com.mit.spbau.kirakosian.Protocol.NEW_QUERY;
+import static com.mit.spbau.kirakosian.Protocol.STOP;
+
+@SuppressWarnings("WeakerAccess")
+public class Client {
+
+ private static final int BUFFER_SIZE = 1000_000;
+
+ private final @NotNull SocketChannel channel;
+ private final NonBlockingServer server;
+
+ private final ByteBuffer reading = ByteBuffer.allocate(BUFFER_SIZE);
+ private final ByteBuffer writing = ByteBuffer.allocate(BUFFER_SIZE);
+ MessageReader reader = new MessageReader(reading);
+
+ private long queryBegin;
+ private State state;
+
+ public Client(@NotNull final SocketChannel channel, final NonBlockingServer server) {
+ this.channel = channel;
+ this.server = server;
+ state = State.READING;
+ writing.flip();
+ }
+
+ public void onWrite() throws IOException {
+ synchronized (writing) {
+ if (state != State.WRITING) {
+ return;
+ }
+ channel.write(writing);
+ processWroteInfo();
+ }
+ }
+
+ public void onRead() throws IOException {
+ if (state != State.READING) {
+ return;
+ }
+ //noinspection StatementWithEmptyBody
+ while (channel.read(reading) > 0) {}
+
+ processReadInfo();
+ }
+
+ private void processReadInfo() {
+ reading.flip();
+
+ while (!reader.completed() && reader.canRead()) {
+ reader.readNext();
+ }
+
+ if (reader.completed()) {
+ if (reader.getState() == STOP) {
+ server.unregisterRead(this);
+ server.unregisterWrite(this);
+ } else if (reader.getState() == NEW_QUERY) {
+ queryBegin = reader.queryBegin();
+ server.pool().submit(new SortTask(reader.getArray()));
+ } else {
+ throw new UnexpectedProtocolMessageException();
+ }
+ reader.refresh();
+ reading.clear();
+ return;
+ }
+
+ reading.compact();
+ }
+
+ private void processWroteInfo() {
+ if (writing.remaining() == 0) {
+ server.listener().timeForClientOnServer(System.currentTimeMillis() - queryBegin);
+ state = State.READING;
+ }
+ }
+
+ @NotNull
+ public SocketChannel getChannel() {
+ return channel;
+ }
+
+ public void sendNewMessage(final int[] array) {
+ synchronized (writing) {
+ final byte[] message = Utils.makeArrayMessage(array);
+ writing.compact();
+ writing.putInt(message.length);
+ writing.put(message);
+ writing.flip();
+ }
+ }
+
+ private class SortTask implements Runnable {
+
+ private final int[] innerArray;
+
+ public SortTask(final int[] innerArray) {
+ this.innerArray = innerArray;
+ }
+
+ @Override
+ public void run() {
+ final long begin = System.currentTimeMillis();
+ Utils.sort(innerArray);
+ final long end = System.currentTimeMillis();
+ server.listener().timeForTask(end - begin);
+ sendNewMessage(innerArray);
+ state = State.WRITING;
+ }
+ }
+
+ enum State {
+ READING, WRITING
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/MessageReader.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/MessageReader.java
new file mode 100644
index 0000000..9caf011
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/MessageReader.java
@@ -0,0 +1,91 @@
+package com.mit.spbau.kirakosian.servers.impl.nonBlockingServer;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.mit.spbau.kirakosian.Utils;
+import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException;
+
+import java.nio.ByteBuffer;
+
+import static com.mit.spbau.kirakosian.Protocol.NEW_QUERY;
+import static com.mit.spbau.kirakosian.Protocol.STOP;
+
+@SuppressWarnings("WeakerAccess")
+public class MessageReader {
+
+ private final ByteBuffer buffer;
+
+ private long queryBegin;
+
+ private int signal = -1;
+ private int size = - 1;
+ private int readSize = 0;
+ private byte[] array;
+
+ public MessageReader(final ByteBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ public boolean canRead() {
+ if (size == -1) {
+ return buffer.remaining() >= 4;
+ }
+ return buffer.remaining() >= 1;
+ }
+
+ public boolean completed() {
+ if (signal == -1) {
+ return false;
+ }
+ if (signal == STOP) {
+ return true;
+ }
+ if (signal == NEW_QUERY) {
+ return size != -1 && size == readSize;
+ }
+ throw new UnexpectedProtocolMessageException();
+ }
+
+ public void readNext() {
+ if (completed()) {
+ throw new RuntimeException(); // should never happen
+ }
+ if (signal == -1) {
+ queryBegin = System.currentTimeMillis();
+ signal = buffer.getInt();
+ return;
+ }
+ if (size == -1) {
+ size = buffer.getInt();
+ array = new byte[size];
+ return;
+ }
+ if (size != 0 && size == readSize) {
+ throw new RuntimeException();
+ }
+ array[readSize++] = buffer.get();
+ }
+
+ public int getState() {
+ return signal;
+ }
+
+ public int[] getArray() {
+ try {
+ return Utils.getArray(array);
+ } catch (final InvalidProtocolBufferException e) {
+ throw new RuntimeException(); // should never happen
+ }
+ }
+
+ public long queryBegin() {
+ return queryBegin;
+ }
+
+ public void refresh() {
+ array = null;
+ size = -1;
+ readSize = 0;
+ signal = -1;
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/NonBlockingServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/NonBlockingServer.java
new file mode 100644
index 0000000..5c98a28
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/NonBlockingServer.java
@@ -0,0 +1,218 @@
+package com.mit.spbau.kirakosian.servers.impl.nonBlockingServer;
+
+import com.mit.spbau.kirakosian.servers.ServerStatsListener;
+import com.mit.spbau.kirakosian.servers.exceptions.AbortException;
+import com.mit.spbau.kirakosian.servers.impl.AbstractServer;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.*;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@SuppressWarnings("WeakerAccess")
+public class NonBlockingServer extends AbstractServer {
+
+ private volatile boolean working = true;
+ private final ServerSocketChannel server;
+
+ private Thread mainThread;
+ private Thread readingThread;
+ private Thread writingThread;
+ private final ExecutorService pool = Executors.newFixedThreadPool(3);
+
+ private final Selector reader;
+ private final Selector writer;
+
+ private final ConcurrentLinkedQueue registerRead = new ConcurrentLinkedQueue<>();
+ private final ConcurrentLinkedQueue registerWrite = new ConcurrentLinkedQueue<>();
+ private final ConcurrentLinkedQueue unregisterRead = new ConcurrentLinkedQueue<>();
+ private final ConcurrentLinkedQueue unregisterWrite = new ConcurrentLinkedQueue<>();
+
+ public NonBlockingServer() throws AbortException {
+ try {
+ server = ServerSocketChannel.open();
+ server.bind(new InetSocketAddress(PORT));
+
+ reader = Selector.open();
+ writer = Selector.open();
+ } catch (final IOException e) {
+ throw new AbortException();
+ }
+ }
+
+ @Override
+ public void start() {
+ working = true;
+ mainThread = new Thread(this::work);
+ readingThread = new Thread(this::processRead);
+ writingThread = new Thread(this::processWrite);
+ mainThread.start();
+ readingThread.start();
+ writingThread.start();
+ }
+
+ private void work() {
+ while (working) {
+ try {
+ final SocketChannel channel = server.accept();
+ processConnection(channel);
+ } catch (final IOException e) {
+ if (working) {
+ listener.fail(e);
+ break;
+ }
+ break;
+ }
+ }
+
+ closeAll();
+ }
+
+ private void processConnection(final SocketChannel channel) {
+ final Client client = new Client(channel, this);
+ try {
+ channel.configureBlocking(false);
+ } catch (final IOException e) {
+ listener.fail(e);
+ }
+ registerRead(client);
+ registerWrite(client);
+ }
+
+ public void registerRead(final Client client) {
+ registerRead.add(client);
+ reader.wakeup();
+ }
+
+ public void registerWrite(final Client client) {
+ registerWrite.add(client);
+ writer.wakeup();
+ }
+
+ public void unregisterRead(final Client client) {
+ unregisterRead.add(client);
+ reader.wakeup();
+ }
+
+ public void unregisterWrite(final Client client) {
+ unregisterWrite.add(client);
+ writer.wakeup();
+ }
+
+ private void processRead() {
+ while (working) {
+ try {
+ while (!registerRead.isEmpty()) {
+ final Client client = registerRead.poll();
+ if (client == null) {
+ continue;
+ }
+ client.getChannel().register(reader, SelectionKey.OP_READ, client);
+ }
+ while (!unregisterRead.isEmpty()) {
+ final Client client = unregisterRead.poll();
+ if (client == null) {
+ continue;
+ }
+ client.getChannel().keyFor(reader).cancel();
+ }
+
+ final int ready = reader.select();
+ if (ready == 0) {
+ continue;
+ }
+ final Iterator iterator = reader.selectedKeys().iterator();
+ while (iterator.hasNext()) {
+ final SelectionKey key = iterator.next();
+ final Client client = (Client) key.attachment();
+ client.onRead();
+ iterator.remove();
+ }
+ } catch (final IOException e) {
+ if (working) {
+ listener.fail(e);
+ break;
+ }
+ break;
+ }
+ }
+
+ closeAll();
+ }
+
+ private void processWrite() {
+ while (working) {
+ try {
+ while (!registerWrite.isEmpty()) {
+ final Client client = registerWrite.poll();
+ if (client == null) {
+ continue;
+ }
+ client.getChannel().register(writer, SelectionKey.OP_WRITE, client);
+ }
+ while (!unregisterWrite.isEmpty()) {
+ final Client client = unregisterWrite.poll();
+ if (client == null) {
+ continue;
+ }
+ client.getChannel().keyFor(writer).cancel();
+ }
+
+ final int ready = writer.select();
+ if (ready == 0) {
+ continue;
+ }
+ final Iterator iterator = writer.selectedKeys().iterator();
+ while (iterator.hasNext()) {
+ final SelectionKey key = iterator.next();
+ final Client client = (Client) key.attachment();
+ client.onWrite();
+ iterator.remove();
+ }
+ } catch (final IOException e) {
+ if (working) {
+ listener.fail(e);
+ break;
+ }
+ break;
+ }
+ }
+
+ closeAll();
+ }
+
+ private void closeAll() {
+ if (working) {
+ working = false;
+ try {
+ reader.close();
+ writer.close();
+ server.close();
+ mainThread.join();
+ readingThread.join();
+ writingThread.join();
+ } catch (final IOException | InterruptedException e) {
+ listener.fail(e);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void shutDown() {
+ if (!working) {
+ return;
+ }
+ closeAll();
+ }
+
+ public ServerStatsListener listener() {
+ return listener;
+ }
+
+ public ExecutorService pool() {
+ return pool;
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/simpleServer/SimpleServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/simpleServer/SimpleServer.java
new file mode 100644
index 0000000..c7d7484
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/simpleServer/SimpleServer.java
@@ -0,0 +1,58 @@
+package com.mit.spbau.kirakosian.servers.impl.simpleServer;
+
+import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException;
+import com.mit.spbau.kirakosian.servers.exceptions.AbortException;
+import com.mit.spbau.kirakosian.servers.impl.AbstractBlockingServer;
+import com.mit.spbau.kirakosian.Utils;
+
+import java.io.*;
+import java.net.Socket;
+
+import static com.mit.spbau.kirakosian.Protocol.NEW_QUERY;
+import static com.mit.spbau.kirakosian.Protocol.STOP;
+
+public class SimpleServer extends AbstractBlockingServer {
+
+ public SimpleServer() throws AbortException {
+ super();
+ }
+
+ @Override
+ protected void processConnection(final Socket socket) {
+ try (final DataOutputStream os = new DataOutputStream(socket.getOutputStream());
+ final DataInputStream is = new DataInputStream(socket.getInputStream())) {
+
+ int signal;
+ signal = is.readInt();
+ while (signal != STOP) {
+ if (signal != NEW_QUERY) {
+ throw new UnexpectedProtocolMessageException();
+ }
+ final long queryBegin = System.currentTimeMillis();
+ final int[] array = Utils.readArray(is);
+
+ final long sortBegin = System.currentTimeMillis();
+ Utils.sort(array);
+ final long sortEnd = System.currentTimeMillis();
+
+ Utils.writeArray(os, array);
+ os.flush();
+ final long queryEnd = System.currentTimeMillis();
+
+ listener.timeForTask(sortEnd - sortBegin);
+ listener.timeForClientOnServer(queryEnd - queryBegin);
+
+ signal = is.readInt();
+ }
+
+ } catch (final IOException e) {
+ try {
+ socket.close();
+ } catch (final IOException e1) {
+ listener.fail(e1);
+ }
+ listener.fail(e);
+ }
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTest.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTest.java
new file mode 100644
index 0000000..61b369a
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTest.java
@@ -0,0 +1,79 @@
+package com.mit.spbau.kirakosian.testing;
+
+import com.mit.spbau.kirakosian.connector.ApplicationConnector;
+import com.mit.spbau.kirakosian.options.TestOptions;
+import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime;
+import com.mit.spbau.kirakosian.servers.Server;
+import com.mit.spbau.kirakosian.servers.Servers;
+import com.mit.spbau.kirakosian.servers.exceptions.AbortException;
+
+import java.io.IOException;
+
+class ServerTest {
+
+ private final TestOptions options;
+ private final StatsListener stats = new StatsListener();
+
+ ServerTest(final TestOptions options) {
+ this.options = options;
+ }
+
+ /**
+ * This function runs test and stores information in {@link TestResults} class.
+ */
+ void startTest(final TestResults results) throws IOException {
+ stats.clear();
+
+ // wait for second application
+ final ApplicationConnector connector = new ApplicationConnector(options, stats); // lethal error
+ connector.waitForNewClient(); // lethal error
+ System.out.println("Starting");
+
+ for (int currentValue = options.lowerBound();
+ currentValue <= options.upperBound();
+ currentValue += options.delta()) {
+
+ final Server server;
+ try {
+ server = Servers.createServer(options.serverType());
+ } catch (final AbortException e) {
+ // failed to create server, just skip test case
+ continue;
+ }
+ server.setServerActionListener(stats);
+ server.start();
+
+ System.out.println("Test case " + currentValue);
+ connector.startTestCase(); // lethal error
+
+ server.shutDown();
+ updateResult(results, currentValue);
+ stats.clear();
+ }
+
+ System.out.println("End of testing");
+ connector.close();
+ }
+
+ private void updateResult(final TestResults results, final int value) {
+ if (stats.anyErrorsOccurred()) {
+ System.out.println("Test case was skipped cause errors");
+ for (final Exception e : stats.getErrors()) {
+ e.printStackTrace();
+ }
+ return;
+ }
+ if (options.requiresMetric(TaskTime.class)) {
+ results.addPoint(TaskTime.class, value, stats.getTaskTime());
+ }
+ if (options.requiresMetric(ClientTime.class)) {
+ results.addPoint(ClientTime.class, value, stats.getClientTime());
+ }
+ if (options.requiresMetric(ServerTime.class)) {
+ results.addPoint(ServerTime.class, value, stats.getServerTime());
+ }
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTestInitializer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTestInitializer.java
new file mode 100644
index 0000000..8ae8fd8
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTestInitializer.java
@@ -0,0 +1,154 @@
+package com.mit.spbau.kirakosian.testing;
+
+import com.mit.spbau.kirakosian.controller.Controller;
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime;
+import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime;
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+import com.mit.spbau.kirakosian.options.TestOptions;
+import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize;
+import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber;
+import com.mit.spbau.kirakosian.options.parameters.impl.Delay;
+import com.mit.spbau.kirakosian.servers.Servers;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+public class ServerTestInitializer {
+
+ // this two fields must correspond with ServerTest class (actually, this class might have
+ // different implementations)
+ private final static Set> requiredOptions = new HashSet<>();
+ private final static Set> supportedMetrics = new HashSet<>();
+
+ static {
+ requiredOptions.add(ArraySize.class);
+ requiredOptions.add(ClientsNumber.class);
+ requiredOptions.add(Delay.class);
+ requiredOptions.add(QueriesNumber.class);
+
+ supportedMetrics.add(TaskTime.class);
+ supportedMetrics.add(ServerTime.class);
+ supportedMetrics.add(ClientTime.class);
+ }
+
+ public static Set> getRequiredOptions() {
+ return requiredOptions;
+ }
+
+ public static Set> getSupportedMetrics() {
+ return supportedMetrics;
+ }
+
+ public static void startTest(final TestOptions options) {
+ options.setServerType(Servers.ServerType.Simple);
+ options.print();
+ final ServerTest serverTest = new ServerTest(options);
+ final TestResults results = new TestResults(options);
+ try {
+ serverTest.startTest(results);
+ } catch (final IOException e) {
+ e.printStackTrace();
+ Controller.cancel();
+ }
+
+ Controller.calculationsCompleted(results);
+ }
+
+ public static void startTestDebug(final TestOptions options) {
+ options.setServerType(Servers.ServerType.Simple);
+ options.print();
+ ServerTest serverTest = new ServerTest(options);
+ TestResults results = new TestResults(options);
+ try {
+ serverTest.startTest(results);
+ } catch (final IOException e) {
+ e.printStackTrace();
+ Controller.cancel();
+ }
+
+ Controller.calculationsCompletedDebug(results);
+
+ options.setServerType(Servers.ServerType.Blocking);
+ options.print();
+ serverTest = new ServerTest(options);
+ results = new TestResults(options);
+ try {
+ serverTest.startTest(results);
+ } catch (final IOException e) {
+ e.printStackTrace();
+ Controller.cancel();
+ }
+
+ Controller.calculationsCompletedDebug(results);
+
+ options.setServerType(Servers.ServerType.NonBlocking);
+ options.print();
+ serverTest = new ServerTest(options);
+ results = new TestResults(options);
+ try {
+ serverTest.startTest(results);
+ } catch (final IOException e) {
+ e.printStackTrace();
+ Controller.cancel();
+ }
+
+ Controller.calculationsCompletedDebug(results);
+
+ }
+
+ public static void main(final String[] args) {
+ final TestOptions options = new TestOptions();
+ options.setAlteringOptionMeta(ArraySize.class);
+ options.setOption(QueriesNumber.class, 25);
+ options.setOption(ArraySize.class, 5000);
+ options.setOption(ClientsNumber.class, 25);
+ options.setOption(Delay.class, 200);
+ options.setDelta(1000);
+ options.setLowerBound(1000);
+ options.setUpperBound(10000);
+ options.setServerType(Servers.ServerType.Simple);
+
+ options.addMetric(TaskTime.class);
+ options.addMetric(ClientTime.class);
+ options.addMetric(ServerTime.class);
+
+ String error = options.validate();
+ if (error != null) {
+ System.out.println(error);
+ return;
+ }
+
+ startTestDebug(options);
+
+ options.setAlteringOptionMeta(ClientsNumber.class);
+ options.setDelta(10);
+ options.setLowerBound(10);
+ options.setUpperBound(100);
+
+ error = options.validate();
+ if (error != null) {
+ System.out.println(error);
+ return;
+ }
+
+ startTestDebug(options);
+
+ options.setAlteringOptionMeta(Delay.class);
+ options.setDelta(100);
+ options.setLowerBound(0);
+ options.setUpperBound(900);
+
+ error = options.validate();
+ if (error != null) {
+ System.out.println(error);
+ return;
+ }
+
+ startTestDebug(options);
+
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/StatsListener.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/StatsListener.java
new file mode 100644
index 0000000..626a060
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/StatsListener.java
@@ -0,0 +1,63 @@
+package com.mit.spbau.kirakosian.testing;
+
+import com.mit.spbau.kirakosian.servers.ServerStatsListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("WeakerAccess")
+public class StatsListener implements ServerStatsListener {
+
+ private final List errors = new ArrayList<>();
+
+ private final List taskTimes = new ArrayList<>();
+ private final List serverTimes = new ArrayList<>();
+ private final List clientTimes = new ArrayList<>();
+
+ public void clear() {
+ taskTimes.clear();
+ serverTimes.clear();
+ clientTimes.clear();
+ errors.clear();
+ }
+
+ public boolean anyErrorsOccurred() {
+ return errors.size() != 0;
+ }
+
+ public List getErrors() {
+ return errors;
+ }
+
+ public double getTaskTime() {
+ return taskTimes.stream().mapToLong(e -> e).average().orElse(0);
+ }
+
+ public double getServerTime() {
+ return serverTimes.stream().mapToLong(e -> e).average().orElse(0);
+ }
+
+ public double getClientTime() {
+ return clientTimes.stream().mapToLong(e -> e).average().orElse(0);
+ }
+
+ public void timeForClientOnClient(final long time) {
+ clientTimes.add(time);
+ }
+
+ @Override
+ public void timeForClientOnServer(final long time) {
+ serverTimes.add(time);
+ }
+
+ @Override
+ public void timeForTask(final long time) {
+ taskTimes.add(time);
+ }
+
+ @Override
+ public void fail(final Exception e) {
+ errors.add(e);
+ }
+
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/TestResults.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/TestResults.java
new file mode 100644
index 0000000..e2a27a4
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/TestResults.java
@@ -0,0 +1,37 @@
+package com.mit.spbau.kirakosian.testing;
+
+import com.mit.spbau.kirakosian.options.TestOptions;
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+import com.mit.spbau.kirakosian.options.metrics.MetricResult;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@SuppressWarnings("WeakerAccess")
+public class TestResults {
+
+ private final Map, MetricResult> results = new HashMap<>();
+
+ private final TestOptions options;
+
+ public TestResults(final TestOptions options) {
+ this.options = options;
+ for (final Class extends MetricMeta> meta : options.metrics()) {
+ results.put(meta, new MetricResult(meta));
+ }
+ }
+
+ public TestOptions options() {
+ return options;
+ }
+
+ public void addPoint(final Class extends MetricMeta> meta, final Integer parameter, final Number value) {
+ if (results.containsKey(meta)) {
+ results.get(meta).addResult(parameter, value);
+ }
+ }
+
+ public MetricResult getResultsForMetric(final Class extends MetricMeta> meta) {
+ return results.get(meta);
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Scene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Scene.java
new file mode 100644
index 0000000..3eae37d
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Scene.java
@@ -0,0 +1,15 @@
+package com.mit.spbau.kirakosian.ui;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+
+public abstract class Scene extends JPanel {
+
+ protected Scene() {
+ setBorder(new EmptyBorder(20, 20, 20, 20));
+ setLayout(new BorderLayout());
+
+ setVisible(false);
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/SceneManager.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/SceneManager.java
new file mode 100644
index 0000000..bba3809
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/SceneManager.java
@@ -0,0 +1,23 @@
+package com.mit.spbau.kirakosian.ui;
+
+import com.mit.spbau.kirakosian.ui.scenes.InitialScene;
+import com.mit.spbau.kirakosian.ui.scenes.ResultsScene;
+import com.mit.spbau.kirakosian.ui.scenes.SettingsScene;
+import com.mit.spbau.kirakosian.ui.scenes.TestingScene;
+
+public class SceneManager {
+
+ private static Window window;
+ public final static InitialScene INITIAL_SCENE = new InitialScene();
+ public final static SettingsScene SETTINGS_SCENE = new SettingsScene();
+ public final static TestingScene TESTING_SCENE = new TestingScene();
+ public static final ResultsScene RESULTS_SCENE = new ResultsScene();
+
+ public static void init(final Window w) {
+ window = w;
+ }
+
+ public static void setScene(final Scene scene) {
+ window.setScene(scene);
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Window.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Window.java
new file mode 100644
index 0000000..3f25e3c
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Window.java
@@ -0,0 +1,32 @@
+package com.mit.spbau.kirakosian.ui;
+
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class Window extends JFrame {
+
+ @SuppressWarnings("WeakerAccess")
+ @NotNull private Scene currentScene;
+
+ public Window() {
+ super("Server Benchmark");
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+ setResizable(false);
+ setSize(new Dimension(500, 600));
+
+ currentScene = SceneManager.SETTINGS_SCENE;
+ currentScene.setVisible(true);
+ setContentPane(currentScene);
+ }
+
+
+ void setScene(final Scene scene) {
+ currentScene.setVisible(false);
+ scene.setVisible(true);
+ setContentPane(scene);
+ revalidate();
+ currentScene = scene;
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/InitialScene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/InitialScene.java
new file mode 100644
index 0000000..28d5ef7
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/InitialScene.java
@@ -0,0 +1,16 @@
+package com.mit.spbau.kirakosian.ui.scenes;
+
+import com.mit.spbau.kirakosian.ui.Scene;
+import com.mit.spbau.kirakosian.ui.SceneManager;
+
+import javax.swing.*;
+
+public class InitialScene extends Scene {
+
+ public InitialScene() {
+ super();
+ final JButton button = new JButton();
+ button.addActionListener(e -> SceneManager.setScene(SceneManager.SETTINGS_SCENE));
+ add(button);
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/ResultsScene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/ResultsScene.java
new file mode 100644
index 0000000..6bdaf83
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/ResultsScene.java
@@ -0,0 +1,46 @@
+package com.mit.spbau.kirakosian.ui.scenes;
+
+import com.mit.spbau.kirakosian.options.Utils;
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+import com.mit.spbau.kirakosian.options.metrics.MetricResult;
+import com.mit.spbau.kirakosian.testing.TestResults;
+import com.mit.spbau.kirakosian.ui.Scene;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import java.util.Map;
+
+public class ResultsScene extends Scene {
+
+ public void acceptResults(final TestResults results) {
+ final Class extends MetricMeta> meta = results.options().metrics().iterator().next();
+ final JFreeChart xyLineChart = ChartFactory.createXYLineChart("",
+ Utils.getOptionInstance(results.options().alteringOption()).name(), "Time",
+ makeDataset(results),
+ PlotOrientation.VERTICAL,
+ true,true,false);
+ add(new ChartPanel(xyLineChart));
+
+ }
+
+ private XYDataset makeDataset(final TestResults results) {
+ final XYSeriesCollection dataset = new XYSeriesCollection();
+ for (final Class extends MetricMeta> meta : results.options().metrics()) {
+ final String name = Utils.getMetricInstance(meta).name();
+ final XYSeries series = new XYSeries(name);
+
+ final MetricResult result = results.getResultsForMetric(meta);
+ for (final Map.Entry entry : result.results().entrySet()) {
+ series.add(entry.getKey(), entry.getValue());
+ }
+
+ dataset.addSeries(series);
+ }
+ return dataset;
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/SettingsScene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/SettingsScene.java
new file mode 100644
index 0000000..1cccd5c
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/SettingsScene.java
@@ -0,0 +1,227 @@
+package com.mit.spbau.kirakosian.ui.scenes;
+
+import com.mit.spbau.kirakosian.testing.ServerTestInitializer;
+import com.mit.spbau.kirakosian.options.metrics.MetricMeta;
+import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta;
+import com.mit.spbau.kirakosian.options.UIOptions;
+import com.mit.spbau.kirakosian.options.TestOptions;
+import com.mit.spbau.kirakosian.servers.Servers;
+import com.mit.spbau.kirakosian.ui.Scene;
+import com.mit.spbau.kirakosian.ui.SceneManager;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.util.ArrayList;
+
+public class SettingsScene extends Scene {
+
+ private final ButtonGroup buttonGroup = new ButtonGroup();
+ private JComboBox comboBox;
+ private final java.util.List optionPanels = new ArrayList<>();
+
+ public SettingsScene() {
+ super();
+ final JButton startButton = new JButton("Run server test");
+ startButton.addActionListener(e ->
+ {
+ final TestOptions testOptions;
+ try {
+ testOptions = collectOptions();
+ } catch (final IllegalOptionsException e1) {
+ JOptionPane.showMessageDialog(null,
+ "Error: Invalid options were set. Only integer values are allowed", "Error Message",
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ final String error = testOptions.validate();
+ if (error != null) {
+ JOptionPane.showMessageDialog(null,
+ "Error: " + error, "Error Message",
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ SceneManager.setScene(SceneManager.TESTING_SCENE);
+ new Thread(() -> ServerTestInitializer.startTest(testOptions)).start();
+ });
+ final JPanel settingsPanel = createSettingsPanel();
+
+ add(settingsPanel);
+ add(startButton, BorderLayout.SOUTH);
+ }
+
+ private TestOptions collectOptions() throws IllegalOptionsException {
+ final TestOptions options = new TestOptions();
+ for (final Class extends MetricMeta> meta : UIOptions.metrics()) {
+ options.addMetric(meta);
+ }
+ options.setServerType((Servers.ServerType) comboBox.getSelectedItem());
+ for (final OptionPanel panel : optionPanels) {
+ panel.collectOptions(options);
+ }
+ return options;
+ }
+
+ private JPanel createSettingsPanel() {
+ final JPanel settingsPanel = new JPanel();
+ settingsPanel.setLayout(new BorderLayout());
+ final JPanel optionsPanel = new JPanel();
+ optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.Y_AXIS));
+
+ optionsPanel.add(Box.createVerticalGlue());
+ for (final ParameterOptionMeta option : UIOptions.options()) {
+ final OptionPanel panel = new OptionPanel(option);
+ optionPanels.add(panel);
+ optionsPanel.add(panel);
+ optionsPanel.add(Box.createVerticalGlue());
+ }
+
+ settingsPanel.add(createServerTypeOption(), BorderLayout.NORTH);
+ settingsPanel.add(optionsPanel);
+ return settingsPanel;
+ }
+
+ private Component createServerTypeOption() {
+ final JComboBox comboBox = new JComboBox<>();
+ for (final Servers.ServerType option : UIOptions.serverOptions()) {
+ comboBox.addItem(option);
+ }
+ ((JLabel)comboBox.getRenderer()).setHorizontalAlignment(JLabel.CENTER);
+ comboBox.setToolTipText("Server type");
+ this.comboBox = comboBox;
+ return comboBox;
+ }
+
+ private class OptionPanel extends JPanel {
+
+ private final ParameterOptionMeta option; // option to set up
+ private final JSlider slider; // slider to choose option value
+ private final JPanel alterPanel; // panel with settings for altering option
+
+ private boolean altering; // shows whether slider or panel is showed
+ private final JTextField lowerBound = new JTextField("0");
+ private final JTextField upperBound = new JTextField("0");
+ private final JTextField delta = new JTextField("0");
+
+ private OptionPanel(final ParameterOptionMeta option) {
+ this.option = option;
+ setLayout(new GridLayout(2, 1));
+
+ slider = new JSlider(option.minValue(), option.maxValue());
+ setUpSlider();
+
+ final JLabel label = new JLabel(option.name());
+ label.setOpaque(true);
+ label.setHorizontalTextPosition(JLabel.CENTER);
+ add(label);
+
+ alterPanel = createAlterPanel();
+
+ add(createInnerPanel());
+ }
+
+ private JPanel createAlterPanel() {
+ final JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
+ panel.add(Box.createHorizontalGlue());
+ panel.add(createAlterOption("Min", lowerBound));
+ panel.add(Box.createHorizontalGlue());
+ panel.add(createAlterOption("Max", upperBound));
+ panel.add(Box.createHorizontalGlue());
+ panel.add(createAlterOption("Delta", delta));
+ panel.add(Box.createHorizontalGlue());
+ return panel;
+ }
+
+ private Component createAlterOption(final String name, final JTextField textField) {
+ final JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
+ final JLabel label = new JLabel(name);
+ panel.add(label);
+ panel.add(Box.createHorizontalStrut(10));
+ panel.add(textField);
+ return panel;
+ }
+
+ /**
+ * Creates new inner panel
+ *
+ * @return created inner panel
+ */
+ private Component createInnerPanel() {
+ final JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+ panel.add(slider);
+
+ if (option.mayAlter()) {
+ final JRadioButton button = new JRadioButton();
+ panel.add(button, BorderLayout.EAST);
+ buttonGroup.add(button);
+ button.addItemListener(e -> {
+ switch (e.getStateChange()) {
+ case ItemEvent.SELECTED: {
+ panel.remove(slider);
+ panel.add(alterPanel);
+ panel.revalidate();
+ altering = true;
+ break;
+ }
+ case ItemEvent.DESELECTED: {
+ panel.remove(alterPanel);
+ panel.add(slider);
+ panel.revalidate();
+ altering = false;
+ break;
+ }
+ }
+ });
+ }
+ return panel;
+ }
+
+ private void setUpSlider() {
+ final int length = option.maxValue() - option.minValue();
+ slider.setMajorTickSpacing(getTickSpace(length, 4));
+ slider.setPaintTicks(true);
+ slider.setPaintLabels(true);
+ slider.setMinorTickSpacing(getTickSpace(length, 50));
+ slider.setSnapToTicks(true);
+ slider.setToolTipText(option.description());
+ }
+
+ /**
+ * The method evaluates optimal tick space for target number of slices.
+ *
+ * @param length length of the gap
+ * @param number number of slices
+ * @return optimal tick space
+ */
+ private int getTickSpace(final int length, final int number) {
+ return length < number ? 1 : length / number;
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public void collectOptions(final TestOptions options) throws IllegalOptionsException {
+ if (altering) {
+ options.setAlteringOptionMeta(option.getClass());
+ try {
+ options.setLowerBound(Integer.parseInt(lowerBound.getText()));
+ options.setUpperBound(Integer.parseInt(upperBound.getText()));
+ options.setDelta(Integer.parseInt(delta.getText()));
+ } catch (final NumberFormatException e) {
+ throw new IllegalOptionsException(e);
+ }
+ options.setOption(option.getClass(), null);
+ } else {
+ options.setOption(option.getClass(), slider.getValue());
+ }
+ }
+ }
+
+
+ private class IllegalOptionsException extends Throwable {
+ IllegalOptionsException(final NumberFormatException e) {
+ super(e);
+ }
+ }
+}
diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/TestingScene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/TestingScene.java
new file mode 100644
index 0000000..b5431d9
--- /dev/null
+++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/TestingScene.java
@@ -0,0 +1,13 @@
+package com.mit.spbau.kirakosian.ui.scenes;
+
+import com.mit.spbau.kirakosian.ui.Scene;
+
+import javax.swing.*;
+
+public class TestingScene extends Scene {
+
+ public TestingScene() {
+ super();
+ add(new JLabel("Waiting for testing..."));
+ }
+}
diff --git a/ServerBenchmark/src/main/resources/array.proto b/ServerBenchmark/src/main/resources/array.proto
new file mode 100644
index 0000000..469c67e
--- /dev/null
+++ b/ServerBenchmark/src/main/resources/array.proto
@@ -0,0 +1,10 @@
+syntax = "proto2";
+
+package tutorial;
+
+option java_package = "com.mit.spbau.kirakosian";
+option java_outer_classname = "ArrayMessage";
+
+message Array {
+ repeated int32 data = 1;
+}
\ No newline at end of file
diff --git a/ServerBenchmark/src/script.py b/ServerBenchmark/src/script.py
new file mode 100644
index 0000000..1528568
--- /dev/null
+++ b/ServerBenchmark/src/script.py
@@ -0,0 +1,55 @@
+import matplotlib.pyplot as plt
+import numpy as np
+from scipy import interpolate
+
+servers = ['Simple', 'Blocking', 'NonBlocking']
+metrics = ['ClientTime', 'ServerTime', 'TaskTime']
+params = ['ClientsNumber', 'ArraySize', 'Delay']
+
+colors = ["red", "green", "blue"]
+
+def get_data(i, metric, param):
+ name = "results/logs/" + servers[i] + '_' + param + ".txt"
+ input = open(name, "r")
+ xs = []
+ ys = []
+ i = 0
+ lines = input.readlines()
+ while i != len(lines) and not(lines[i].startswith(metric)):
+ i = i + 1
+ if (i == len(lines)):
+ return [1, 2, 3], [1, 2, 3]
+ i = i + 1
+ n = int(lines[i])
+ for line in lines[i + 1 : i + n + 1]:
+ xs.append(int(float(line.split(' ')[0])))
+ ys.append(int(float(line.split(' ')[1][0 : -1])))
+ return xs, ys
+
+def build_plot(metric, param):
+ print(metric, param)
+ name = metric + '(' + param + ')'
+ plt.title(name)
+ plt.xlabel(param)
+ plt.ylabel(metric)
+ lines = []
+
+ for i in range(len(servers)):
+ xs, ys = get_data(i, metric, param)
+ x_coord = np.linspace(min(xs), max(xs))
+ y_coord = interpolate.spline(xs, ys, x_coord)
+
+ line, = plt.plot(x_coord, y_coord, color=colors[i])
+ lines.append(line)
+
+ plt.grid(True, linestyle='-', color='0.75')
+ plt.legend(lines, servers, loc='best')
+
+ plt.savefig('results/plots/' + name + '.png')
+ plt.clf()
+
+if __name__ == '__main__':
+ for metric in metrics:
+ for param in params:
+
+ build_plot(metric, param)
\ No newline at end of file