Initial commit
This commit is contained in:
commit
23aee75962
149
.gitignore
vendored
Normal file
149
.gitignore
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Gradle template
|
||||
.gradle
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
### Java template
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### Maven template
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### Vim template
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
!*.svg # comment out if you don't need vector files
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
Sessionx.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
||||
dslab20
|
||||
6
.idea/compiler.xml
generated
Normal file
6
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
||||
5
.idea/misc.xml
generated
Normal file
5
.idea/misc.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="11" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
29
README.md
Normal file
29
README.md
Normal file
@ -0,0 +1,29 @@
|
||||
distributed systems lab
|
||||
=======================
|
||||
|
||||
Using gradle
|
||||
------------
|
||||
|
||||
### Compile & Test
|
||||
|
||||
Gradle is the build tool we are using. Here are some instructions:
|
||||
|
||||
Compile the project using the gradle wrapper:
|
||||
|
||||
./gradlew assemble
|
||||
|
||||
Compile and run the tests:
|
||||
|
||||
./gradlew build
|
||||
|
||||
### Run the applications
|
||||
|
||||
The gradle config config contains several tasks that start application components for you.
|
||||
You can list them with
|
||||
|
||||
./gradlew tasks --all
|
||||
|
||||
And search for 'Other tasks' starting with `run-`. For example, to run the monitoring server, execute:
|
||||
(the `--console=plain` flag disables CLI features, like color output, that may break the console output when running a interactive application)
|
||||
|
||||
./gradlew --console=plain run-monitoring
|
||||
68
build.gradle
Normal file
68
build.gradle
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group = 'at.ac.tuwien.infosys.dslab'
|
||||
version = '2020'
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation files('lib/commons-logging-1.2.jar')
|
||||
implementation files('lib/hamcrest-core-1.3.jar')
|
||||
implementation files('lib/junit-4.12.jar')
|
||||
implementation files('lib/orvell-core-0.2.0.jar')
|
||||
}
|
||||
|
||||
// ======== run specifications
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
def dslabRunTasks = [
|
||||
[name: 'run-monitoring', main: 'dslab.monitoring.MonitoringServer', args: 'monitoring', description: 'Run Monitoring Server'],
|
||||
[name: 'run-transfer-1', main: 'dslab.transfer.TransferServer', args: 'transfer-1', description: 'Run Transfer Server #1'],
|
||||
[name: 'run-transfer-2', main: 'dslab.transfer.TransferServer', args: 'transfer-2', description: 'Run Transfer Server #2'],
|
||||
[name: 'run-mailbox-earth-planet', main: 'dslab.mailbox.MailboxServer', args: 'mailbox-earth-planet', description: 'Run Mailbox Server for earth.planet'],
|
||||
[name: 'run-mailbox-univer-ze', main: 'dslab.mailbox.MailboxServer', args: 'mailbox-univer-ze', description: 'Run Mailbox Server for univer.ze'],
|
||||
// assignment 2
|
||||
[name: 'run-ns-root', main: 'dslab.nameserver.Nameserver', args: 'ns-root', description: 'Run root nameserver'],
|
||||
[name: 'run-ns-ze', main: 'dslab.nameserver.Nameserver', args: 'ns-ze', description: 'Run .ze nameserver'],
|
||||
[name: 'run-ns-planet', main: 'dslab.nameserver.Nameserver', args: 'ns-planet', description: 'Run .planet nameserver'],
|
||||
[name: 'run-ns-earth-planet', main: 'dslab.nameserver.Nameserver', args: 'ns-earth-planet', description: 'Run .earth.planet nameserver'],
|
||||
[name: 'run-client-trillian', main: 'dslab.client.MessageClient', args: 'client-trillian', description: 'Run client for trillian'],
|
||||
[name: 'run-client-arthur', main: 'dslab.client.MessageClient', args: 'client-arthur', description: 'Run client for arthur'],
|
||||
[name: 'run-client-zaphod', main: 'dslab.client.MessageClient', args: 'client-zaphod', description: 'Run client for zaphod'],
|
||||
]
|
||||
|
||||
// dynamically create run tasks of this structure:
|
||||
//
|
||||
// task 'run-transfer-1'(dependsOn: 'classes', type: JavaExec) {
|
||||
// main = 'dslab.transfer.TransferServer'
|
||||
// args 'transfer-2'
|
||||
// classpath = sourceSets.main.runtimeClasspath
|
||||
// standardInput = System.in
|
||||
// }
|
||||
|
||||
for (t in dslabRunTasks) {
|
||||
def taskName = t['name']
|
||||
|
||||
task "$taskName"(dependsOn: 'classes', type: JavaExec) {
|
||||
main = t['main']
|
||||
args t['args']
|
||||
description = t['description']
|
||||
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
// https://discuss.gradle.org/t/why-doesnt-system-in-read-block-when-im-using-gradle/3308
|
||||
standardInput = System.in
|
||||
}
|
||||
}
|
||||
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
185
gradlew
vendored
Executable file
185
gradlew
vendored
Executable file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## 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='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# 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 or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@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 Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@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="-Xmx64m" "-Xms64m"
|
||||
|
||||
@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 execute
|
||||
|
||||
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 execute
|
||||
|
||||
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
|
||||
|
||||
: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 %*
|
||||
|
||||
: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
|
||||
5
settings.gradle
Normal file
5
settings.gradle
Normal file
@ -0,0 +1,5 @@
|
||||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
|
||||
rootProject.name = 'dslab20'
|
||||
80
src/main/java/dslab/ComponentFactory.java
Normal file
80
src/main/java/dslab/ComponentFactory.java
Normal file
@ -0,0 +1,80 @@
|
||||
package dslab;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import dslab.mailbox.IMailboxServer;
|
||||
import dslab.mailbox.MailboxServer;
|
||||
import dslab.monitoring.IMonitoringServer;
|
||||
import dslab.monitoring.MonitoringServer;
|
||||
import dslab.transfer.ITransferServer;
|
||||
import dslab.transfer.TransferServer;
|
||||
import dslab.util.Config;
|
||||
|
||||
/**
|
||||
* The component factory provides methods to create the core components of the application. You can edit the method body
|
||||
* if the component instantiation requires additional logic.
|
||||
*
|
||||
* Do not change the existing method signatures!
|
||||
*/
|
||||
public final class ComponentFactory {
|
||||
|
||||
private ComponentFactory() {
|
||||
// static utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link IMonitoringServer} instance.
|
||||
*
|
||||
* @param componentId the component id
|
||||
* @param in the input stream used for accepting cli commands
|
||||
* @param out the output stream to print to
|
||||
* @return a new MonitoringServer instance
|
||||
*/
|
||||
public static IMonitoringServer createMonitoringServer(String componentId, InputStream in, PrintStream out)
|
||||
throws Exception {
|
||||
/*
|
||||
* TODO: Here you can modify the code (if necessary) to instantiate your components
|
||||
*/
|
||||
|
||||
Config config = new Config(componentId);
|
||||
return new MonitoringServer(componentId, config, in, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link IMailboxServer} instance.
|
||||
*
|
||||
* @param componentId the component id
|
||||
* @param in the input stream used for accepting cli commands
|
||||
* @param out the output stream to print to
|
||||
* @return a new MailboxServer instance
|
||||
*/
|
||||
public static IMailboxServer createMailboxServer(String componentId, InputStream in, PrintStream out)
|
||||
throws Exception {
|
||||
/*
|
||||
* TODO: Here you can modify the code (if necessary) to instantiate your components
|
||||
*/
|
||||
|
||||
Config config = new Config(componentId);
|
||||
return new MailboxServer(componentId, config, in, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ITransferServer} instance.
|
||||
*
|
||||
* @param componentId the component id
|
||||
* @param in the input stream used for accepting cli commands
|
||||
* @param out the output stream to print to
|
||||
* @return a new TransferServer instance
|
||||
*/
|
||||
public static ITransferServer createTransferServer(String componentId, InputStream in, PrintStream out)
|
||||
throws Exception {
|
||||
/*
|
||||
* TODO: Here you can modify the code (if necessary) to instantiate your components
|
||||
*/
|
||||
|
||||
Config config = new Config(componentId);
|
||||
return new TransferServer(componentId, config, in, out);
|
||||
}
|
||||
|
||||
}
|
||||
23
src/main/java/dslab/mailbox/IMailboxServer.java
Normal file
23
src/main/java/dslab/mailbox/IMailboxServer.java
Normal file
@ -0,0 +1,23 @@
|
||||
package dslab.mailbox;
|
||||
|
||||
/**
|
||||
* The mailbox server receives mails via DMTP from transfer servers, and makes them available to users via the DMAP
|
||||
* protocol.
|
||||
*
|
||||
* Do not change the existing method signatures!
|
||||
*/
|
||||
public interface IMailboxServer extends Runnable {
|
||||
|
||||
/**
|
||||
* Starts the server.
|
||||
*/
|
||||
@Override
|
||||
void run();
|
||||
|
||||
/**
|
||||
* CLI command to shut down the server. After this method, all resources should be closed, and the application
|
||||
* should terminate.
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
}
|
||||
37
src/main/java/dslab/mailbox/MailboxServer.java
Normal file
37
src/main/java/dslab/mailbox/MailboxServer.java
Normal file
@ -0,0 +1,37 @@
|
||||
package dslab.mailbox;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.util.Config;
|
||||
|
||||
public class MailboxServer implements IMailboxServer, Runnable {
|
||||
|
||||
/**
|
||||
* Creates a new server instance.
|
||||
*
|
||||
* @param componentId the id of the component that corresponds to the Config resource
|
||||
* @param config the component config
|
||||
* @param in the input stream to read console input from
|
||||
* @param out the output stream to write console output to
|
||||
*/
|
||||
public MailboxServer(String componentId, Config config, InputStream in, PrintStream out) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
IMailboxServer server = ComponentFactory.createMailboxServer(args[0], System.in, System.out);
|
||||
server.run();
|
||||
}
|
||||
}
|
||||
33
src/main/java/dslab/monitoring/IMonitoringServer.java
Normal file
33
src/main/java/dslab/monitoring/IMonitoringServer.java
Normal file
@ -0,0 +1,33 @@
|
||||
package dslab.monitoring;
|
||||
|
||||
/**
|
||||
* The monitoring service accepts incoming monitoring packets via UDP. It provides CLI commands to access the
|
||||
* information.
|
||||
*
|
||||
* Do not change the existing method signatures!
|
||||
*/
|
||||
public interface IMonitoringServer extends Runnable {
|
||||
|
||||
/**
|
||||
* Starts the server.
|
||||
*/
|
||||
@Override
|
||||
void run();
|
||||
|
||||
/**
|
||||
* CLI command to shut down the server. After this method, all resources should be closed, and the application
|
||||
* should terminate.
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* CLI command to report usage statistics for transfer servers.
|
||||
*/
|
||||
void servers();
|
||||
|
||||
/**
|
||||
* CLI command to report usage statistics for individual senders.
|
||||
*/
|
||||
void addresses();
|
||||
|
||||
}
|
||||
48
src/main/java/dslab/monitoring/MonitoringServer.java
Normal file
48
src/main/java/dslab/monitoring/MonitoringServer.java
Normal file
@ -0,0 +1,48 @@
|
||||
package dslab.monitoring;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.util.Config;
|
||||
|
||||
public class MonitoringServer implements IMonitoringServer {
|
||||
|
||||
/**
|
||||
* Creates a new server instance.
|
||||
*
|
||||
* @param componentId the id of the component that corresponds to the Config resource
|
||||
* @param config the component config
|
||||
* @param in the input stream to read console input from
|
||||
* @param out the output stream to write console output to
|
||||
*/
|
||||
public MonitoringServer(String componentId, Config config, InputStream in, PrintStream out) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addresses() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void servers() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
IMonitoringServer server = ComponentFactory.createMonitoringServer(args[0], System.in, System.out);
|
||||
server.run();
|
||||
}
|
||||
|
||||
}
|
||||
22
src/main/java/dslab/transfer/ITransferServer.java
Normal file
22
src/main/java/dslab/transfer/ITransferServer.java
Normal file
@ -0,0 +1,22 @@
|
||||
package dslab.transfer;
|
||||
|
||||
/**
|
||||
* The transfer server is responsible for accepting mails sent by users, and forward them to mailbox servers via DMTP.
|
||||
* It also reports usage statistics to the monitoring server.
|
||||
*
|
||||
* Do not change the existing method signatures!
|
||||
*/
|
||||
public interface ITransferServer extends Runnable {
|
||||
|
||||
/**
|
||||
* Starts the server.
|
||||
*/
|
||||
@Override
|
||||
void run();
|
||||
|
||||
/**
|
||||
* CLI command to shut down the server. After this method, all resources should be closed, and the application
|
||||
* should terminate.
|
||||
*/
|
||||
void shutdown();
|
||||
}
|
||||
38
src/main/java/dslab/transfer/TransferServer.java
Normal file
38
src/main/java/dslab/transfer/TransferServer.java
Normal file
@ -0,0 +1,38 @@
|
||||
package dslab.transfer;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.util.Config;
|
||||
|
||||
public class TransferServer implements ITransferServer, Runnable {
|
||||
|
||||
/**
|
||||
* Creates a new server instance.
|
||||
*
|
||||
* @param componentId the id of the component that corresponds to the Config resource
|
||||
* @param config the component config
|
||||
* @param in the input stream to read console input from
|
||||
* @param out the output stream to write console output to
|
||||
*/
|
||||
public TransferServer(String componentId, Config config, InputStream in, PrintStream out) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ITransferServer server = ComponentFactory.createTransferServer(args[0], System.in, System.out);
|
||||
server.run();
|
||||
}
|
||||
|
||||
}
|
||||
79
src/main/java/dslab/util/Config.java
Normal file
79
src/main/java/dslab/util/Config.java
Normal file
@ -0,0 +1,79 @@
|
||||
package dslab.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Reads the configuration from a {@code .properties} file.
|
||||
*/
|
||||
public final class Config {
|
||||
|
||||
private final ResourceBundle bundle;
|
||||
private Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates an instance of Config which reads configuration data form {@code .properties} file with given name found
|
||||
* in classpath.
|
||||
*
|
||||
* @param name the name of the .properties file
|
||||
*/
|
||||
public Config(String name) {
|
||||
if (name.endsWith(".properties")) {
|
||||
this.bundle = ResourceBundle.getBundle(name.substring(0, name.length() - 11));
|
||||
} else {
|
||||
this.bundle = ResourceBundle.getBundle(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value as String for the given key.
|
||||
*
|
||||
* @param key the property's key
|
||||
* @return String value of the property
|
||||
* @see ResourceBundle#getString(String)
|
||||
*/
|
||||
public String getString(String key) {
|
||||
if (properties.containsKey(key)) {
|
||||
return properties.get(key).toString();
|
||||
}
|
||||
return this.bundle.getString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value as {@code int} for the given key.
|
||||
*
|
||||
* @param key the property's key
|
||||
* @return int value of the property
|
||||
* @throws NumberFormatException if the String cannot be parsed to an Integer
|
||||
*/
|
||||
public int getInt(String key) {
|
||||
return Integer.parseInt(getString(key));
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return properties.containsKey(key) || bundle.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the given key.
|
||||
*
|
||||
* @param key the property's key
|
||||
* @param value the value of the property
|
||||
*/
|
||||
public void setProperty(String key, Object value) {
|
||||
properties.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all keys of this configuration.
|
||||
*
|
||||
* @return the keys
|
||||
*/
|
||||
public Set<String> listKeys() {
|
||||
Set<String> keys = bundle.keySet();
|
||||
keys.addAll(properties.keySet());
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
4
src/main/resources/domains.properties
Normal file
4
src/main/resources/domains.properties
Normal file
@ -0,0 +1,4 @@
|
||||
# <domain>=<socket-address>
|
||||
# TODO: replace ports with the ports that your mailbox servers use
|
||||
earth.planet=127.0.0.1:port_range+2
|
||||
univer.ze=127.0.0.1:port_range+4
|
||||
25
src/main/resources/mailbox-earth-planet.properties
Normal file
25
src/main/resources/mailbox-earth-planet.properties
Normal file
@ -0,0 +1,25 @@
|
||||
# TCP port used for the DMTP server socket
|
||||
# TODO: REPLACE with real value such as 16502 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
dmtp.tcp.port=port_range+2
|
||||
|
||||
# TCP port used for the DMAP server socket
|
||||
# TODO: REPLACE with real value such as 16503 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
dmap.tcp.port=port_range+3
|
||||
|
||||
# mail domain managed by the mailbox server
|
||||
domain=earth.planet
|
||||
|
||||
# location of the users for this mailbox server
|
||||
users.config=users-earth-planet.properties
|
||||
|
||||
# ============================================= Required for Assignment 2
|
||||
|
||||
# name of the root nameserver's remote object
|
||||
root_id=root-nameserver
|
||||
|
||||
# RMI registry host
|
||||
registry.host=localhost
|
||||
|
||||
# RMI registry port
|
||||
# TODO: REPLACE with real value such as 16509 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
registry.port=port_range+9
|
||||
25
src/main/resources/mailbox-univer-ze.properties
Normal file
25
src/main/resources/mailbox-univer-ze.properties
Normal file
@ -0,0 +1,25 @@
|
||||
# TCP port used for the DMTP server socket
|
||||
# TODO: REPLACE with real value such as 16504 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
dmtp.tcp.port=port_range+4
|
||||
|
||||
# TCP port used for the DMAP server socket
|
||||
# TODO: REPLACE with real value such as 16505 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
dmap.tcp.port=port_range+5
|
||||
|
||||
# mail domain managed by the mailbox server
|
||||
domain=univer.ze
|
||||
|
||||
# location of the users for this mailbox server
|
||||
users.config=users-univer-ze.properties
|
||||
|
||||
# ============================================= Required for Assignment 2
|
||||
|
||||
# name of the root nameserver's remote object
|
||||
root_id=root-nameserver
|
||||
|
||||
# RMI registry host
|
||||
registry.host=localhost
|
||||
|
||||
# RMI registry port
|
||||
# TODO: REPLACE with real value such as 16509 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
registry.port=port_range+9
|
||||
3
src/main/resources/monitoring.properties
Normal file
3
src/main/resources/monitoring.properties
Normal file
@ -0,0 +1,3 @@
|
||||
# UDP port used for accepting monitoring packets
|
||||
# TODO: REPLACE with real value such as 16508 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
udp.port=port_range+8
|
||||
20
src/main/resources/transfer-1.properties
Normal file
20
src/main/resources/transfer-1.properties
Normal file
@ -0,0 +1,20 @@
|
||||
# TCP port used for the DMTP server socket
|
||||
# TODO: REPLACE with real value such as 16500 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
tcp.port=port_range
|
||||
|
||||
# UDP socket address of the monitoring server
|
||||
monitoring.host=127.0.0.1
|
||||
# TODO: REPLACE with the real value of the monitoring server port
|
||||
monitoring.port=port_range+8
|
||||
|
||||
# ============================================= Required for Assignment 2
|
||||
|
||||
# name of the root nameserver's remote object
|
||||
root_id=root-nameserver
|
||||
|
||||
# RMI registry host
|
||||
registry.host=localhost
|
||||
|
||||
# RMI registry port
|
||||
# TODO: REPLACE with real value such as 16509 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
registry.port=port_range+9
|
||||
20
src/main/resources/transfer-2.properties
Normal file
20
src/main/resources/transfer-2.properties
Normal file
@ -0,0 +1,20 @@
|
||||
# TCP port used for the DMTP server socket
|
||||
# TODO: REPLACE with real value such as 16501 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
tcp.port=port_range+1
|
||||
|
||||
# UDP socket address of the monitoring server
|
||||
monitoring.host=127.0.0.1
|
||||
# TODO: REPLACE with the real value of the monitoring server port
|
||||
monitoring.port=port_range+8
|
||||
|
||||
# ============================================= Required for Assignment 2
|
||||
|
||||
# name of the root nameserver's remote object
|
||||
root_id=root-nameserver
|
||||
|
||||
# RMI registry host
|
||||
registry.host=localhost
|
||||
|
||||
# RMI registry port
|
||||
# TODO: REPLACE with real value such as 16509 - considering the port range associated with your account - you have received after Lab 0 a port range (beginning_of_the_range, end_of_the_range)
|
||||
registry.port=port_range+9
|
||||
3
src/main/resources/users-earth-planet.properties
Normal file
3
src/main/resources/users-earth-planet.properties
Normal file
@ -0,0 +1,3 @@
|
||||
# <username>=<password>
|
||||
trillian=12345
|
||||
arthur=23456
|
||||
2
src/main/resources/users-univer-ze.properties
Normal file
2
src/main/resources/users-univer-ze.properties
Normal file
@ -0,0 +1,2 @@
|
||||
# <username>=<password>
|
||||
zaphod=12345
|
||||
9
src/test/java/dslab/CheckedConsumer.java
Normal file
9
src/test/java/dslab/CheckedConsumer.java
Normal file
@ -0,0 +1,9 @@
|
||||
package dslab;
|
||||
|
||||
/**
|
||||
* CheckedConsumer.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CheckedConsumer<T, E extends Exception> {
|
||||
void accept(T socket) throws E;
|
||||
}
|
||||
14
src/test/java/dslab/Constants.java
Normal file
14
src/test/java/dslab/Constants.java
Normal file
@ -0,0 +1,14 @@
|
||||
package dslab;
|
||||
|
||||
public interface Constants {
|
||||
|
||||
/**
|
||||
* Default time (in milliseconds) to wait after starting a component to test.
|
||||
*/
|
||||
long COMPONENT_STARTUP_WAIT = 3000;
|
||||
|
||||
/**
|
||||
* Default time (in milliseconds) to wait after shutting down a component to test.
|
||||
*/
|
||||
long COMPONENT_TEARDOWN_WAIT = 3000;
|
||||
}
|
||||
135
src/test/java/dslab/JunitSocketClient.java
Normal file
135
src/test/java/dslab/JunitSocketClient.java
Normal file
@ -0,0 +1,135 @@
|
||||
package dslab;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Assert;
|
||||
import org.junit.rules.ErrorCollector;
|
||||
|
||||
public class JunitSocketClient implements Closeable {
|
||||
|
||||
private ErrorCollector err;
|
||||
|
||||
private Socket socket;
|
||||
private PrintWriter writer;
|
||||
|
||||
private StreamListener listener;
|
||||
|
||||
/**
|
||||
* Creates a new Socket that connects to localhost on the given port and holds the I/O resources.
|
||||
*
|
||||
* @param port the port to connect to
|
||||
* @throws IOException if an I/O error occurred while connecting.
|
||||
*/
|
||||
public JunitSocketClient(int port) throws IOException {
|
||||
this(new Socket("127.0.0.1", port));
|
||||
}
|
||||
|
||||
public JunitSocketClient(Socket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
this.writer = new PrintWriter(socket.getOutputStream());
|
||||
this.listener = new StreamListener(socket.getInputStream());
|
||||
|
||||
new Thread(listener).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Socket that connects to localhost on the given port and holds the I/O resources.
|
||||
*
|
||||
* @param port the port to connect to
|
||||
* @param err the error collector used to verify communication
|
||||
* @throws IOException if an I/O error occurred while connecting.
|
||||
*/
|
||||
|
||||
public JunitSocketClient(int port, ErrorCollector err) throws IOException {
|
||||
this(port);
|
||||
this.err = err;
|
||||
}
|
||||
|
||||
public Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
public PrintWriter getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
public void send(String message) {
|
||||
writer.println(message);
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
public String sendAndListen(String message) {
|
||||
send(message);
|
||||
return listen();
|
||||
}
|
||||
|
||||
public String listen() {
|
||||
return listen(1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public String listen(long time, TimeUnit timeUnit) {
|
||||
return listener.listen(time, timeUnit);
|
||||
}
|
||||
|
||||
public String read() throws IOException {
|
||||
return listener.poll(1, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a line from the input stream and verifies that it contains the given string.
|
||||
*
|
||||
* @param string the partial string to match
|
||||
* @throws IOException on read errors
|
||||
*/
|
||||
public void verify(String string) throws IOException {
|
||||
assertThat(read(), containsString(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given string to the output stream, and then behaves like {@link #verify(String)}.
|
||||
*
|
||||
* @param request the request to send
|
||||
* @param response the expected response (partial string match)
|
||||
* @throws IOException on I/O errors
|
||||
*/
|
||||
public void sendAndVerify(String request, String response) throws IOException {
|
||||
assertThat(sendAndRead(request), containsString(response));
|
||||
}
|
||||
|
||||
public String sendAndRead(String message) throws IOException {
|
||||
send(message);
|
||||
return read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
closeQuietly(listener);
|
||||
closeQuietly(writer);
|
||||
closeQuietly(socket);
|
||||
}
|
||||
|
||||
private <T> void assertThat(T actual, Matcher<? super T> matcher) {
|
||||
if (err != null) {
|
||||
err.checkThat(actual, matcher);
|
||||
} else {
|
||||
Assert.assertThat(actual, matcher);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeQuietly(Closeable closeable) {
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/test/java/dslab/NullOutputStream.java
Normal file
38
src/test/java/dslab/NullOutputStream.java
Normal file
@ -0,0 +1,38 @@
|
||||
package dslab;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* This {@link OutputStream} has no destination (file/socket etc.) and all bytes written to it are ignored and lost.
|
||||
*/
|
||||
public final class NullOutputStream extends OutputStream {
|
||||
public static final OutputStream INSTANCE = new NullOutputStream();
|
||||
|
||||
private NullOutputStream() {
|
||||
}
|
||||
|
||||
public static OutputStream getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the specified byte.
|
||||
*/
|
||||
@Override
|
||||
public void write(int b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the specified byte array.
|
||||
*/
|
||||
@Override
|
||||
public void write(byte[] b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the specified byte array.
|
||||
*/
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
}
|
||||
}
|
||||
84
src/test/java/dslab/Sockets.java
Normal file
84
src/test/java/dslab/Sockets.java
Normal file
@ -0,0 +1,84 @@
|
||||
package dslab;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.BindException;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
/**
|
||||
* Util class for checking sockets.
|
||||
*/
|
||||
public final class Sockets {
|
||||
|
||||
private Sockets() {
|
||||
// util class
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits a given time for a TCP server socket to be opened at the given host and port by repeatedly (every 10 ms)
|
||||
* trying to connect to the address.
|
||||
*
|
||||
* @param host the expected server socket host
|
||||
* @param port the expected server socket port
|
||||
* @param ms the time in milliseconds.
|
||||
* @throws SocketTimeoutException if the timeout period was reached
|
||||
*/
|
||||
public static void waitForSocket(String host, int port, long ms) throws SocketTimeoutException {
|
||||
long interval = 10;
|
||||
|
||||
while (ms > 0) {
|
||||
try (Socket ignored = new Socket(host, port)) {
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
ms -= interval;
|
||||
|
||||
try {
|
||||
Thread.sleep(interval);
|
||||
} catch (InterruptedException e1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new SocketTimeoutException("Gave up waiting for socket " + host + ":" + port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to open a DatagramSocket on the given port. If a BindException is thrown, it indicates that the socket was
|
||||
* open before, and the method will return true. If the socket was opened successfully, the method returns false and
|
||||
* immediately closes the socket.
|
||||
*
|
||||
* @param port the local port
|
||||
* @return true if the socket was already open
|
||||
* @throws SocketException if the socket could not be opened due to some other reason than a bind exception
|
||||
*/
|
||||
public static boolean isDatagramSocketOpen(int port) throws SocketException {
|
||||
try (DatagramSocket socket = new DatagramSocket(port)) {
|
||||
return false;
|
||||
} catch (BindException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to open a ServerSocket on the given port. If a BindException is thrown, it indicates that the socket was
|
||||
* open before, and the method will return true. If the socket was opened successfully, the method returns false and
|
||||
* immediately closes the socket.
|
||||
*
|
||||
* @param port the local port
|
||||
* @return true if the socket was already open
|
||||
* @throws IOException if the socket could not be opened due to some other reason than a bind exception
|
||||
*/
|
||||
public static boolean isServerSocketOpen(int port) throws IOException {
|
||||
try (ServerSocket socket = new ServerSocket(port)) {
|
||||
return false;
|
||||
} catch (BindException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
80
src/test/java/dslab/StreamListener.java
Normal file
80
src/test/java/dslab/StreamListener.java
Normal file
@ -0,0 +1,80 @@
|
||||
package dslab;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.SocketException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Runnable that reads from a Socket's InputStream line-by-line and writes it to a queue, until the socket is closed.
|
||||
*/
|
||||
public class StreamListener implements Runnable, Closeable {
|
||||
|
||||
public static final String NULL_CHAR = new String(new byte[]{0x0});
|
||||
|
||||
private InputStream in;
|
||||
private LinkedBlockingQueue<String> queue;
|
||||
|
||||
public StreamListener(InputStream in) {
|
||||
this.in = in;
|
||||
this.queue = new LinkedBlockingQueue<>();
|
||||
}
|
||||
|
||||
private static String removeNullBytes(String str) {
|
||||
return str.replace(NULL_CHAR, "");
|
||||
}
|
||||
|
||||
public String poll(long timeout, TimeUnit timeUnit) {
|
||||
try {
|
||||
return queue.poll(timeout, timeUnit);
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String listen(long timeout, TimeUnit timeUnit) {
|
||||
StringBuilder str = new StringBuilder(128);
|
||||
|
||||
String line;
|
||||
while ((line = poll(timeout, timeUnit)) != null) {
|
||||
str.append(removeNullBytes(line)).append("\n");
|
||||
}
|
||||
|
||||
if (str.length() > 0) {
|
||||
// remove trailing whitespace
|
||||
int i = str.length() - 1;
|
||||
if ('\n' == str.charAt(i)) {
|
||||
str.deleteCharAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
|
||||
String line;
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
queue.offer(line);
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
// socket closed
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
}
|
||||
30
src/test/java/dslab/StringMatches.java
Normal file
30
src/test/java/dslab/StringMatches.java
Normal file
@ -0,0 +1,30 @@
|
||||
package dslab;
|
||||
|
||||
import org.hamcrest.Factory;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.core.SubstringMatcher;
|
||||
|
||||
/**
|
||||
* String matcher that checks for regex matching.
|
||||
*/
|
||||
public class StringMatches extends SubstringMatcher {
|
||||
|
||||
public StringMatches(String substring) {
|
||||
super(substring);
|
||||
}
|
||||
|
||||
@Factory
|
||||
public static Matcher<String> matchesPattern(String pattern) {
|
||||
return new StringMatches(pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean evalSubstringOf(String string) {
|
||||
return string.matches(substring);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String relationship() {
|
||||
return "matching";
|
||||
}
|
||||
}
|
||||
30
src/test/java/dslab/TestBase.java
Normal file
30
src/test/java/dslab/TestBase.java
Normal file
@ -0,0 +1,30 @@
|
||||
package dslab;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.ErrorCollector;
|
||||
import org.junit.rules.Timeout;
|
||||
|
||||
/**
|
||||
* Contains a generic setup for a unit test.
|
||||
*/
|
||||
public class TestBase {
|
||||
|
||||
@Rule
|
||||
public ErrorCollector err = new ErrorCollector();
|
||||
|
||||
@Rule
|
||||
public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); // fail tests that do not terminate after 30 seconds
|
||||
|
||||
protected TestInputStream in;
|
||||
protected TestOutputStream out;
|
||||
|
||||
@Before
|
||||
public void setUpBase() throws Exception {
|
||||
in = new TestInputStream();
|
||||
out = new TestOutputStream();
|
||||
}
|
||||
|
||||
}
|
||||
99
src/test/java/dslab/TestInputStream.java
Normal file
99
src/test/java/dslab/TestInputStream.java
Normal file
@ -0,0 +1,99 @@
|
||||
package dslab;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Simulates reading lines from an {@link InputStream}.
|
||||
*
|
||||
* Internally, the lines read from the underlying {@link InputStream} are buffered and can be retrieved on demand for
|
||||
* verification purposes.
|
||||
*/
|
||||
public class TestInputStream extends InputStream {
|
||||
private volatile BlockingQueue<String> lines = new LinkedBlockingQueue<>();
|
||||
private InputStream in;
|
||||
|
||||
@Override
|
||||
public synchronized int read() throws IOException {
|
||||
if (in == null) {
|
||||
if ((in = nextLine()) == null) {
|
||||
return -1;
|
||||
}
|
||||
} else if (in.available() <= 0) {
|
||||
try {
|
||||
return -1;
|
||||
} finally {
|
||||
in = null;
|
||||
}
|
||||
}
|
||||
return in != null ? in.read() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the lines available for reading.
|
||||
*
|
||||
* @return the available lines
|
||||
* @throws IOException if the stream is closed
|
||||
*/
|
||||
public List<String> getLines() throws IOException {
|
||||
List<String> copy = new ArrayList<>();
|
||||
do {
|
||||
try {
|
||||
copy.add(lines.take());
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
} while (lines.size() > 0);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given line to the input queue.
|
||||
*
|
||||
* @param line the line to add
|
||||
*/
|
||||
public void addLine(String line) {
|
||||
if (lines != null) {
|
||||
lines.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the next line available for reading from it.
|
||||
* <p/>
|
||||
* This method blocks until a line is available or the stream becomes closed.
|
||||
*
|
||||
* @return the {@link InputStream} holding the line
|
||||
* @throws IOException if the stream is closed
|
||||
*/
|
||||
private InputStream nextLine() throws IOException {
|
||||
try {
|
||||
String line = null;
|
||||
while (lines != null && line == null) {
|
||||
line = lines.poll(500L, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (line != null) {
|
||||
return new ByteArrayInputStream((line.endsWith("\n") ? line : line + '\n').getBytes());
|
||||
} else {
|
||||
return new ByteArrayInputStream("".getBytes());
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (in != System.in) {
|
||||
super.close();
|
||||
}
|
||||
lines = null;
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
150
src/test/java/dslab/TestOutputStream.java
Normal file
150
src/test/java/dslab/TestOutputStream.java
Normal file
@ -0,0 +1,150 @@
|
||||
package dslab;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Simulates writing lines to an {@link PrintStream}.
|
||||
* <p/>
|
||||
* Internally, the lines written to the underlying {@link PrintStream} are buffered and can be retrieved on demand for
|
||||
* verification purposes.
|
||||
*/
|
||||
public class TestOutputStream extends PrintStream {
|
||||
private final LinkedBlockingQueue<String> lines = new LinkedBlockingQueue<>();
|
||||
private volatile StringBuilder line = new StringBuilder();
|
||||
private PrintStream delegate;
|
||||
|
||||
/**
|
||||
* Creates a new {@code TestOutputStream} instance writing to an {@link NullOutputStream}.
|
||||
*/
|
||||
public TestOutputStream() {
|
||||
this(new PrintStream(NullOutputStream.getInstance()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code TestOutputStream} instance writing to the provided {@link PrintStream}.
|
||||
*
|
||||
* @param delegate the stream to write to
|
||||
*/
|
||||
public TestOutputStream(PrintStream delegate) {
|
||||
super(delegate);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != System.out) {
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
delegate.write(b);
|
||||
if (b == '\r') {
|
||||
// Do nothing
|
||||
} else if (b == '\n') {
|
||||
addLine();
|
||||
} else {
|
||||
line.append((char) b);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(byte b[], int off, int len) {
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
} else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
write(b[off + i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the lines written to the {@link PrintStream} so far.
|
||||
*
|
||||
* @return the written lines
|
||||
*/
|
||||
public List<String> getLines() {
|
||||
synchronized (lines) {
|
||||
if (line.length() > 0) {
|
||||
addLine();
|
||||
}
|
||||
return new ArrayList<>(lines);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for stream output until no output has been received for one second.
|
||||
*
|
||||
* @return the aggregated output (joined by a newline)
|
||||
* @throws InterruptedException if the polling was interrupted
|
||||
*/
|
||||
public String listen() throws InterruptedException {
|
||||
return listen(1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public String listen(long timeout, TimeUnit timeUnit) throws InterruptedException {
|
||||
StringBuilder str = new StringBuilder(128);
|
||||
|
||||
String line;
|
||||
while ((line = poll(timeout, timeUnit)) != null) {
|
||||
str.append(line).append("\n");
|
||||
}
|
||||
|
||||
if (str.length() > 0) {
|
||||
// remove trailing whitespace
|
||||
int i = str.length() - 1;
|
||||
if ('\n' == str.charAt(i)) {
|
||||
str.deleteCharAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
public String poll(long time, TimeUnit timeUnit) throws InterruptedException {
|
||||
return lines.poll(time, timeUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the lines written to the {@link PrintStream} so far and clears the buffer.
|
||||
*
|
||||
* @return the written lines
|
||||
* @see #getLines()
|
||||
* @see #clear()
|
||||
*/
|
||||
public List<String> reset() {
|
||||
synchronized (lines) {
|
||||
List<String> lines = getLines();
|
||||
clear();
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the buffer holding the lines written to the {@link PrintStream} so far.
|
||||
*/
|
||||
private void clear() {
|
||||
synchronized (lines) {
|
||||
lines.clear();
|
||||
line = new StringBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the current line to the buffer.
|
||||
*/
|
||||
private void addLine() {
|
||||
synchronized (lines) {
|
||||
lines.add(line.toString());
|
||||
line = new StringBuilder();
|
||||
}
|
||||
}
|
||||
}
|
||||
108
src/test/java/dslab/mailbox/MailboxServerProtocolTest.java
Normal file
108
src/test/java/dslab/mailbox/MailboxServerProtocolTest.java
Normal file
@ -0,0 +1,108 @@
|
||||
package dslab.mailbox;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.Constants;
|
||||
import dslab.JunitSocketClient;
|
||||
import dslab.Sockets;
|
||||
import dslab.TestBase;
|
||||
import dslab.util.Config;
|
||||
|
||||
public class MailboxServerProtocolTest extends TestBase {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(MailboxServerProtocolTest.class);
|
||||
|
||||
private String componentId = "mailbox-earth-planet";
|
||||
|
||||
private IMailboxServer component;
|
||||
private int dmapServerPort;
|
||||
private int dmtpServerPort;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
component = ComponentFactory.createMailboxServer(componentId, in, out);
|
||||
dmapServerPort = new Config(componentId).getInt("dmap.tcp.port");
|
||||
dmtpServerPort = new Config(componentId).getInt("dmtp.tcp.port");
|
||||
|
||||
new Thread(component).start();
|
||||
|
||||
LOG.info("Waiting for server sockets to appear");
|
||||
Sockets.waitForSocket("localhost", dmapServerPort, Constants.COMPONENT_STARTUP_WAIT);
|
||||
Sockets.waitForSocket("localhost", dmtpServerPort, Constants.COMPONENT_STARTUP_WAIT);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
in.addLine("shutdown"); // send "shutdown" command to command line
|
||||
Thread.sleep(Constants.COMPONENT_TEARDOWN_WAIT);
|
||||
}
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void loginAndLogout_withValidLogin() throws Exception {
|
||||
try (JunitSocketClient client = new JunitSocketClient(dmapServerPort, err)) {
|
||||
client.verify("ok DMAP");
|
||||
client.sendAndVerify("login trillian 12345", "ok");
|
||||
client.sendAndVerify("logout", "ok");
|
||||
client.sendAndVerify("quit", "ok bye");
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void login_withInvalidLogin_returnsError() throws Exception {
|
||||
try (JunitSocketClient client = new JunitSocketClient(dmapServerPort, err)) {
|
||||
client.verify("ok DMAP");
|
||||
client.sendAndVerify("login trillian WRONGPW", "error");
|
||||
client.sendAndVerify("quit", "ok bye");
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void acceptDmtpMessage_listDmapMessage() throws Exception {
|
||||
|
||||
// accept a message via DMTP (to trillian)
|
||||
try (JunitSocketClient client = new JunitSocketClient(dmtpServerPort, err)) {
|
||||
client.verify("ok DMTP");
|
||||
client.sendAndVerify("begin", "ok");
|
||||
client.sendAndVerify("from arthur@earth.planet", "ok");
|
||||
client.sendAndVerify("to trillian@earth.planet", "ok 1");
|
||||
client.sendAndVerify("subject hello", "ok");
|
||||
client.sendAndVerify("data hello from junit", "ok");
|
||||
client.sendAndVerify("send", "ok");
|
||||
client.sendAndVerify("quit", "ok bye");
|
||||
}
|
||||
|
||||
// list the message via DMAP list
|
||||
try (JunitSocketClient client = new JunitSocketClient(dmapServerPort, err)) {
|
||||
client.verify("ok DMAP");
|
||||
client.sendAndVerify("login trillian 12345", "ok");
|
||||
|
||||
client.send("list");
|
||||
String listResult = client.listen();
|
||||
err.checkThat(listResult, containsString("arthur@earth.planet hello"));
|
||||
|
||||
client.sendAndVerify("logout", "ok");
|
||||
client.sendAndVerify("quit", "ok bye");
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void dmtpMessage_withUnknownRecipient_returnsError() throws Exception {
|
||||
|
||||
// accept a message via DMTP (to trillian)
|
||||
try (JunitSocketClient client = new JunitSocketClient(dmtpServerPort, err)) {
|
||||
client.verify("ok DMTP");
|
||||
client.sendAndVerify("begin", "ok");
|
||||
client.sendAndVerify("from arthur@earth.planet", "ok");
|
||||
client.sendAndVerify("to unknown@earth.planet", "error unknown");
|
||||
client.sendAndVerify("quit", "ok bye");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
75
src/test/java/dslab/mailbox/MailboxServerTest.java
Normal file
75
src/test/java/dslab/mailbox/MailboxServerTest.java
Normal file
@ -0,0 +1,75 @@
|
||||
package dslab.mailbox;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.Constants;
|
||||
import dslab.Sockets;
|
||||
import dslab.TestBase;
|
||||
import dslab.monitoring.MonitoringServerTest;
|
||||
import dslab.util.Config;
|
||||
|
||||
/**
|
||||
* MailboxServerTest.
|
||||
*/
|
||||
public class MailboxServerTest extends TestBase {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(MonitoringServerTest.class);
|
||||
|
||||
@Test
|
||||
public void runAndShutdownTransferServer_createsAndStopsTcpSocketCorrectly() throws Exception {
|
||||
String componentId = "mailbox-earth-planet";
|
||||
Config config = new Config(componentId);
|
||||
|
||||
IMailboxServer component = ComponentFactory.createMailboxServer(componentId, in, out);
|
||||
int dmtpPort = config.getInt("dmtp.tcp.port");
|
||||
int dmapPort = config.getInt("dmap.tcp.port");
|
||||
|
||||
assertThat(component, is(notNullValue()));
|
||||
|
||||
Thread componentThread = new Thread(component);
|
||||
LOG.info("Starting thread with component " + component);
|
||||
componentThread.start();
|
||||
|
||||
try {
|
||||
LOG.info("Waiting for DMTP socket to open on port " + dmtpPort);
|
||||
Sockets.waitForSocket("localhost", dmtpPort, Constants.COMPONENT_STARTUP_WAIT);
|
||||
} catch (SocketTimeoutException e) {
|
||||
err.addError(new AssertionError("Expected a TCP server socket on port " + dmtpPort, e));
|
||||
}
|
||||
|
||||
try {
|
||||
LOG.info("Waiting for DMAP socket to open on port " + dmapPort);
|
||||
Sockets.waitForSocket("localhost", dmapPort, Constants.COMPONENT_STARTUP_WAIT);
|
||||
} catch (SocketTimeoutException e) {
|
||||
err.addError(new AssertionError("Expected a TCP server socket on port " + dmapPort, e));
|
||||
}
|
||||
|
||||
LOG.info("Shutting down component " + component);
|
||||
in.addLine("shutdown"); // send "shutdown" command to command line
|
||||
Thread.sleep(Constants.COMPONENT_TEARDOWN_WAIT);
|
||||
|
||||
try {
|
||||
LOG.info("Waiting for thread to stop for component " + component);
|
||||
componentThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
err.addError(new AssertionError("Monitoring server was not terminated correctly"));
|
||||
}
|
||||
|
||||
|
||||
err.checkThat("Expected tcp socket on port " + dmtpPort + " to be closed after shutdown",
|
||||
Sockets.isServerSocketOpen(dmtpPort), is(false));
|
||||
|
||||
err.checkThat("Expected tcp socket on port " + dmapPort + " to be closed after shutdown",
|
||||
Sockets.isServerSocketOpen(dmapPort), is(false));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
package dslab.monitoring;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.Constants;
|
||||
import dslab.TestBase;
|
||||
import dslab.util.Config;
|
||||
|
||||
/**
|
||||
* Tests whether the UDP-based monitoring protocol is implemented correctly on the server side.
|
||||
*/
|
||||
public class MonitoringServerProtocolTest extends TestBase {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(MonitoringServerProtocolTest.class);
|
||||
|
||||
private String componentId = "monitoring";
|
||||
|
||||
private IMonitoringServer component;
|
||||
private InetSocketAddress addr;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
component = ComponentFactory.createMonitoringServer(componentId, in, out);
|
||||
addr = new InetSocketAddress("127.0.0.1", new Config(componentId).getInt("udp.port"));
|
||||
|
||||
new Thread(component).start();
|
||||
Thread.sleep(Constants.COMPONENT_STARTUP_WAIT);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
in.addLine("shutdown");
|
||||
Thread.sleep(Constants.COMPONENT_TEARDOWN_WAIT);
|
||||
}
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void addresses_returnsCorrectStatistics() throws Exception {
|
||||
LOG.info("Sending three monitoring packets to monitoring socket");
|
||||
try (DatagramSocket socket = new DatagramSocket()) {
|
||||
String str1 = "127.0.0.1:42 foo@example.com";
|
||||
String str2 = "127.0.0.1:43 foo@example.com";
|
||||
String str3 = "127.0.0.1:42 bar@example.com";
|
||||
|
||||
socket.send(new DatagramPacket(str1.getBytes(), str1.length(), addr));
|
||||
socket.send(new DatagramPacket(str2.getBytes(), str2.length(), addr));
|
||||
socket.send(new DatagramPacket(str3.getBytes(), str3.length(), addr));
|
||||
}
|
||||
|
||||
Thread.sleep(2500);
|
||||
in.addLine("addresses"); // send "addresses" command to command line
|
||||
Thread.sleep(2500);
|
||||
String output = String.join(",", out.getLines());
|
||||
assertThat(output, containsString("foo@example.com 2"));
|
||||
assertThat(output, containsString("bar@example.com 1"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Assumes that run and shutdown works correctly.
|
||||
*/
|
||||
@Test(timeout = 15000)
|
||||
public void servers_returnsCorrectStatistics() throws Exception {
|
||||
LOG.info("Sending three monitoring packets to monitoring socket");
|
||||
try (DatagramSocket socket = new DatagramSocket()) {
|
||||
String str1 = "127.0.0.1:42 foo@example.com";
|
||||
String str2 = "127.0.0.1:43 foo@example.com";
|
||||
String str3 = "127.0.0.1:42 bar@example.com";
|
||||
|
||||
socket.send(new DatagramPacket(str1.getBytes(), str1.length(), addr));
|
||||
socket.send(new DatagramPacket(str2.getBytes(), str2.length(), addr));
|
||||
socket.send(new DatagramPacket(str3.getBytes(), str3.length(), addr));
|
||||
}
|
||||
|
||||
Thread.sleep(2500);
|
||||
in.addLine("servers"); // send "addresses" command to command line
|
||||
Thread.sleep(2500);
|
||||
String output = String.join(",", out.getLines());
|
||||
assertThat(output, containsString("127.0.0.1:42 2"));
|
||||
assertThat(output, containsString("127.0.0.1:43 1"));
|
||||
}
|
||||
}
|
||||
60
src/test/java/dslab/monitoring/MonitoringServerTest.java
Normal file
60
src/test/java/dslab/monitoring/MonitoringServerTest.java
Normal file
@ -0,0 +1,60 @@
|
||||
package dslab.monitoring;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.Constants;
|
||||
import dslab.Sockets;
|
||||
import dslab.TestBase;
|
||||
import dslab.util.Config;
|
||||
|
||||
/**
|
||||
* Tests the creation, running, and shutting down of the monitoring server.
|
||||
*/
|
||||
public class MonitoringServerTest extends TestBase {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(MonitoringServerTest.class);
|
||||
|
||||
@Test
|
||||
public void runAndShutdownMonitoringServer_createsAndStopsUdpSocketCorrectly() throws Exception {
|
||||
IMonitoringServer component = ComponentFactory.createMonitoringServer("monitoring", in, out);
|
||||
int port = new Config("monitoring").getInt("udp.port");
|
||||
|
||||
assertThat(component, is(notNullValue()));
|
||||
|
||||
Thread componentThread = new Thread(component);
|
||||
LOG.info("Starting thread with component " + component);
|
||||
componentThread.start();
|
||||
|
||||
Thread.sleep(Constants.COMPONENT_STARTUP_WAIT); // wait a bit for resources to be initialized
|
||||
|
||||
try {
|
||||
LOG.info("Trying to create socket on port " + port);
|
||||
err.checkThat("Expected an open UDP socket on port " + port, Sockets.isDatagramSocketOpen(port), is(true));
|
||||
} catch (Exception e) {
|
||||
// a different unexpected error occurred (unlikely)
|
||||
err.addError(e);
|
||||
}
|
||||
|
||||
LOG.info("Shutting down component " + component);
|
||||
in.addLine("shutdown"); // send "shutdown" command to command line
|
||||
Thread.sleep(Constants.COMPONENT_TEARDOWN_WAIT);
|
||||
|
||||
try {
|
||||
LOG.info("Waiting for thread to stop for component " + component);
|
||||
componentThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
err.addError(new AssertionError("Monitoring server was not terminated correctly"));
|
||||
}
|
||||
|
||||
err.checkThat("Expected datagram socket on port " + port + " to be closed after shutdown",
|
||||
Sockets.isDatagramSocketOpen(port), is(false));
|
||||
}
|
||||
|
||||
}
|
||||
71
src/test/java/dslab/transfer/TransferServerProtocolTest.java
Normal file
71
src/test/java/dslab/transfer/TransferServerProtocolTest.java
Normal file
@ -0,0 +1,71 @@
|
||||
package dslab.transfer;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.Constants;
|
||||
import dslab.JunitSocketClient;
|
||||
import dslab.Sockets;
|
||||
import dslab.TestBase;
|
||||
import dslab.util.Config;
|
||||
|
||||
/**
|
||||
* TransferServerProtocolTest.
|
||||
*/
|
||||
public class TransferServerProtocolTest extends TestBase {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(TransferServerProtocolTest.class);
|
||||
|
||||
private String componentId = "transfer-1";
|
||||
|
||||
private ITransferServer component;
|
||||
private int serverPort;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
component = ComponentFactory.createTransferServer(componentId, in, out);
|
||||
serverPort = new Config(componentId).getInt("tcp.port");
|
||||
new Thread(component).start();
|
||||
|
||||
LOG.info("Waiting for server socket to appear");
|
||||
Sockets.waitForSocket("localhost", serverPort, Constants.COMPONENT_STARTUP_WAIT);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
in.addLine("shutdown"); // send "shutdown" command to command line
|
||||
Thread.sleep(Constants.COMPONENT_TEARDOWN_WAIT);
|
||||
}
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void defaultDmtpInteractionTest() throws Exception {
|
||||
try (JunitSocketClient client = new JunitSocketClient(serverPort, err)) {
|
||||
client.verify("ok DMTP");
|
||||
client.sendAndVerify("begin", "ok");
|
||||
client.sendAndVerify("from trillian@earth.planet", "ok");
|
||||
client.sendAndVerify("to arthur@earth.planet", "ok 1");
|
||||
client.sendAndVerify("subject hello", "ok");
|
||||
client.sendAndVerify("data hello from junit", "ok");
|
||||
client.sendAndVerify("send", "ok");
|
||||
client.sendAndVerify("quit", "ok bye");
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void sendWithoutRecipient_returnsErrorOnSend() throws Exception {
|
||||
try (JunitSocketClient client = new JunitSocketClient(serverPort, err)) {
|
||||
client.verify("ok DMTP");
|
||||
client.sendAndVerify("begin", "ok");
|
||||
client.sendAndVerify("from trillian@earth.planet", "ok");
|
||||
client.sendAndVerify("subject hello", "ok");
|
||||
client.sendAndVerify("data hello from junit", "ok");
|
||||
client.sendAndVerify("send", "error");
|
||||
client.sendAndVerify("quit", "ok bye");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
60
src/test/java/dslab/transfer/TransferServerTest.java
Normal file
60
src/test/java/dslab/transfer/TransferServerTest.java
Normal file
@ -0,0 +1,60 @@
|
||||
package dslab.transfer;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import dslab.ComponentFactory;
|
||||
import dslab.Constants;
|
||||
import dslab.Sockets;
|
||||
import dslab.TestBase;
|
||||
import dslab.monitoring.MonitoringServerTest;
|
||||
import dslab.util.Config;
|
||||
|
||||
/**
|
||||
* TransferServerTest.
|
||||
*/
|
||||
public class TransferServerTest extends TestBase {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(MonitoringServerTest.class);
|
||||
|
||||
@Test
|
||||
public void runAndShutdownTransferServer_createsAndStopsTcpSocketCorrectly() throws Exception {
|
||||
ITransferServer component = ComponentFactory.createTransferServer("transfer-1", in, out);
|
||||
int port = new Config("transfer-1").getInt("tcp.port");
|
||||
|
||||
assertThat(component, is(notNullValue()));
|
||||
|
||||
Thread componentThread = new Thread(component);
|
||||
LOG.info("Starting thread with component " + component);
|
||||
componentThread.start();
|
||||
|
||||
try {
|
||||
LOG.info("Waiting for socket to open on port " + port);
|
||||
Sockets.waitForSocket("localhost", port, Constants.COMPONENT_STARTUP_WAIT);
|
||||
} catch (SocketTimeoutException e) {
|
||||
err.addError(new AssertionError("Expected a TCP server socket on port " + port, e));
|
||||
}
|
||||
|
||||
LOG.info("Shutting down component " + component);
|
||||
in.addLine("shutdown"); // send "shutdown" command to command line
|
||||
Thread.sleep(Constants.COMPONENT_TEARDOWN_WAIT);
|
||||
|
||||
try {
|
||||
LOG.info("Waiting for thread to stop for component " + component);
|
||||
componentThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
err.addError(new AssertionError("Monitoring server was not terminated correctly"));
|
||||
}
|
||||
|
||||
err.checkThat("Expected tcp socket on port " + port + " to be closed after shutdown",
|
||||
Sockets.isServerSocketOpen(port), is(false));
|
||||
}
|
||||
|
||||
}
|
||||
0
src/test/resources/.gitkeep
Normal file
0
src/test/resources/.gitkeep
Normal file
Loading…
x
Reference in New Issue
Block a user