diff --git a/api-data-store-server-java/.gitignore b/api-data-store-server-java/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fe63103f29daac95f5989e4cfc75a0fb0cff693b --- /dev/null +++ b/api-data-store-server-java/.gitignore @@ -0,0 +1,7 @@ +/build/ +/bin/ +/out/ +/.idea/ +/target/ +*.iml +*.eml \ No newline at end of file diff --git a/api-data-store-server-java/build.gradle b/api-data-store-server-java/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..c770d54e3704c128ffeaa7f353a939fcde79c4fc --- /dev/null +++ b/api-data-store-server-java/build.gradle @@ -0,0 +1,28 @@ +evaluationDependsOn(':lib-json') + +apply plugin: 'java' +apply plugin: 'application' + +compileJava { + options.compilerArgs << '-parameters' +} + +compileTestJava { + options.compilerArgs << '-parameters' +} + +repositories { + ivy { + ivyPattern "https://sissource.ethz.ch/openbis/openbis-public/openbis-ivy/-/raw/main/[organisation]/[module]/[revision]/ivy.xml" + artifactPattern "https://sissource.ethz.ch/openbis/openbis-public/openbis-ivy/-/raw/main/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]" + } +} + +dependencies { + compileOnly 'lombok:lombok:1.18.22' + annotationProcessor 'lombok:lombok:1.18.22' + implementation project(':lib-json'), + 'lombok:lombok:1.18.22' + testImplementation 'junit:junit:4.10', + 'hamcrest:hamcrest-core:1.3' +} diff --git a/api-data-store-server-java/gradle/wrapper/gradle-wrapper.jar b/api-data-store-server-java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5c2d1cf016b3885f6930543d57b744ea8c220a1a Binary files /dev/null and b/api-data-store-server-java/gradle/wrapper/gradle-wrapper.jar differ diff --git a/api-data-store-server-java/gradle/wrapper/gradle-wrapper.properties b/api-data-store-server-java/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..7745c4ec463c7bb1c4ff6a5e58db239890345152 --- /dev/null +++ b/api-data-store-server-java/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://sissource.ethz.ch/openbis/openbis-public/openbis-ivy/-/raw/main/gradle/distribution/7.4/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/api-data-store-server-java/gradlew b/api-data-store-server-java/gradlew new file mode 100755 index 0000000000000000000000000000000000000000..a69d9cb6c20655813e44515156e7253a2a239138 --- /dev/null +++ b/api-data-store-server-java/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original 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 POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${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 "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# 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 ;; #( + MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/api-data-store-server-java/gradlew.bat b/api-data-store-server-java/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..f127cfd49d4024c3e1e0d08ba56399221b4fb25d --- /dev/null +++ b/api-data-store-server-java/gradlew.bat @@ -0,0 +1,91 @@ +@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% equ 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% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/api-data-store-server-java/settings.gradle b/api-data-store-server-java/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..7cbef054c8bed632b3fc62c2d34bab78ff53a039 --- /dev/null +++ b/api-data-store-server-java/settings.gradle @@ -0,0 +1 @@ +includeFlat 'lib-json' diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/AuthenticationAPI.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/AuthenticationAPI.java new file mode 100644 index 0000000000000000000000000000000000000000..898547b8a9c2eaed627cec94092cb543eeef8a14 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/AuthenticationAPI.java @@ -0,0 +1,29 @@ +/* + * Copyright ETH 2022 - 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsapi.api; + +import lombok.NonNull; + +public interface AuthenticationAPI { + public String login(@NonNull String userId, @NonNull String password) throws Exception; + + public Boolean isSessionValid() throws Exception; + + public Boolean logout() throws Exception; +} + diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/api/OperationsAPI.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/OperationsAPI.java similarity index 59% rename from server-data-store/src/main/java/ch/ethz/sis/afsserver/api/OperationsAPI.java rename to api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/OperationsAPI.java index 36d28eb33b3fb59e6cdf99d3b7164dd03c0815aa..2afacb9b37e89a9906359c19572c2136bf55d45c 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/api/OperationsAPI.java +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/OperationsAPI.java @@ -1,23 +1,25 @@ /* - * Copyright ETH 2022 - 2023 Zürich, Scientific IT Services + * Copyright ETH 2022 - 2023 Zürich, Scientific IT Services * - * 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 + * 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 * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * - * 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. */ -package ch.ethz.sis.afsserver.api; -import ch.ethz.sis.afs.api.dto.File; +package ch.ethz.sis.afsapi.api; + import java.util.List; +import ch.ethz.sis.afsapi.dto.File; import lombok.NonNull; public interface OperationsAPI @@ -46,4 +48,4 @@ public interface OperationsAPI Boolean move(@NonNull String sourceOwner, @NonNull String source, @NonNull String targetOwner, @NonNull String target) throws Exception; -} +} \ No newline at end of file diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/PublicAPI.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/PublicAPI.java new file mode 100644 index 0000000000000000000000000000000000000000..67ae9575bb884349960bc516267b60510d6b358b --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/PublicAPI.java @@ -0,0 +1,21 @@ +/* + * Copyright ETH 2022-2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsapi.api; + +public interface PublicAPI extends OperationsAPI, AuthenticationAPI, TwoPhaseTransactionAPI { +} diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/TwoPhaseTransactionAPI.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/TwoPhaseTransactionAPI.java new file mode 100644 index 0000000000000000000000000000000000000000..58ebc6bf85e92146af8e735fadc589d4b1afc8d9 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/api/TwoPhaseTransactionAPI.java @@ -0,0 +1,37 @@ +/* + * Copyright ETH 2022 - 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ch.ethz.sis.afsapi.api; + +import java.util.List; +import java.util.UUID; + +public interface TwoPhaseTransactionAPI { + + // + // 2PT API + // + + void begin(UUID transactionId) throws Exception; // Starts or recovers an existing transaction + + Boolean prepare() throws Exception; // Prepares the transaction serializing it to disk, if is already prepared ignores the command + + void commit() throws Exception; // Commits the transaction, under any circumstances + + void rollback() throws Exception; // Rollback a begin or prepared transaction + + List<UUID> recover() throws Exception; // Returns the list of transactions on prepare state, used internally on crash recovery scenarios. + +} \ No newline at end of file diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ApiResponse.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ApiResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..d133276f08ae5dded388be059e5404168eba5c15 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ApiResponse.java @@ -0,0 +1,36 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsapi.dto; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Value; + +@Value +@Builder(toBuilder = true) +@AllArgsConstructor(access = AccessLevel.PUBLIC) +public class ApiResponse { + + String id; + + Object result; + + Object error; + +} diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ExceptionReason.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ExceptionReason.java new file mode 100644 index 0000000000000000000000000000000000000000..a724e9523f4671b2cd651a5e93f551906f876286 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ExceptionReason.java @@ -0,0 +1,32 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsapi.dto; + +import lombok.Value; + +import java.io.Serializable; +import java.util.List; + +@Value +public class ExceptionReason implements Serializable { + private Integer componentCode; + private Integer exceptionCode; + private String javaClassName; + private List<ExceptionType> types; + private String message; +} diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ExceptionType.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ExceptionType.java new file mode 100644 index 0000000000000000000000000000000000000000..a7585ac030c4655e1acce050addae78894210440 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/ExceptionType.java @@ -0,0 +1,28 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsapi.dto; + +public enum ExceptionType { + UnknownError, + UserUsageError, + AdminConfigError, + ClientDeveloperCodingError, + CoreDeveloperCodingError, + RecoverableSystemStateError, + IrrecoverableSystemStateError +} diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/File.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/File.java new file mode 100644 index 0000000000000000000000000000000000000000..05aa125caabe467ac02383b6223c7f42a3894611 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/dto/File.java @@ -0,0 +1,37 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsapi.dto; + +import lombok.*; + +import java.time.OffsetDateTime; + +@Value +@Builder(toBuilder = true) +@AllArgsConstructor(access = AccessLevel.PUBLIC) +public class File { + private String path; + private String name; + private Boolean directory; + private Long size; // Size in bytes + private OffsetDateTime lastModifiedTime; + private OffsetDateTime creationTime; + + @EqualsAndHashCode.Exclude + private OffsetDateTime lastAccessTime; +} \ No newline at end of file diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/BaseRuntimeExceptionTemplate.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/BaseRuntimeExceptionTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..709b2848a87d14228256b2afbd2ce3f3ab039244 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/BaseRuntimeExceptionTemplate.java @@ -0,0 +1,67 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsapi.exception; + +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.util.List; + +public abstract class BaseRuntimeExceptionTemplate<REASON extends Serializable, TYPE extends Enum> { + protected final Class clazz; + protected final int componentCode; + protected final int exceptionCode; + protected final String messageTemplate; + protected final List<TYPE> types; + + public BaseRuntimeExceptionTemplate(int componentCode, Class clazz, List<TYPE> types, int exceptionCode, String messageTemplate) { + this.componentCode = componentCode; + this.clazz = clazz; + this.types = types; + this.exceptionCode = exceptionCode; + this.messageTemplate = messageTemplate; + } + + public abstract REASON getReason(Object... args); + + public ThrowableReason getThrowableReason(Object... args) { + return new ThrowableReason(getReason(args)); + } + + public RuntimeException getInstance(Object... args) { + RuntimeException exception; + try { + Constructor constructor = clazz.getConstructor(Throwable.class); + exception = (RuntimeException) constructor.newInstance(getThrowableReason(args)); + } catch (Exception e) { + throw new RuntimeException(e); + } + return exception; + } + + public Exception getCheckedInstance(Object... args) { + Exception exception; + try { + Constructor constructor = clazz.getConstructor(Throwable.class); + exception = (Exception) constructor.newInstance(getThrowableReason(args)); + } catch (Exception e) { + throw new RuntimeException(e); + } + return exception; + } + +} diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/api/PublicAPI.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/ExceptionTemplateHolder.java similarity index 77% rename from server-data-store/src/main/java/ch/ethz/sis/afsserver/api/PublicAPI.java rename to api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/ExceptionTemplateHolder.java index c52ae8929c12d8ff95fc1dab4aa78454fbe749b3..02c5f2edcdb85df3a5d49ba7871a8a929d8e0959 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/api/PublicAPI.java +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/ExceptionTemplateHolder.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ch.ethz.sis.afsserver.api; +package ch.ethz.sis.afsapi.exception; -import ch.ethz.sis.afs.api.TwoPhaseTransactionAPI; - -public interface PublicAPI extends OperationsAPI, AuthenticationAPI, TwoPhaseTransactionAPI { +public interface ExceptionTemplateHolder +{ + public RuntimeException getInstance(Object... args) throws Exception; } diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/RuntimeExceptionTemplate.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/RuntimeExceptionTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..398886ea0dd399e79554d8d01b8e8f396d49f2c0 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/RuntimeExceptionTemplate.java @@ -0,0 +1,37 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsapi.exception; + + +import ch.ethz.sis.afsapi.dto.ExceptionReason; +import ch.ethz.sis.afsapi.dto.ExceptionType; + +import java.util.List; + +public class RuntimeExceptionTemplate extends BaseRuntimeExceptionTemplate<ExceptionReason, ExceptionType> { + + public RuntimeExceptionTemplate(int componentCode, Class clazz, List<ExceptionType> serverExceptionTypes, int exceptionCode, String messageTemplate) { + super(componentCode, clazz, serverExceptionTypes, exceptionCode, messageTemplate); + } + + @Override + public ExceptionReason getReason(Object... args) { + return new ExceptionReason(componentCode, exceptionCode, clazz.getName(), types, String.format(messageTemplate, args)); + } +} + diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/api/AuthenticationAPI.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/ThrowableReason.java similarity index 67% rename from server-data-store/src/main/java/ch/ethz/sis/afsserver/api/AuthenticationAPI.java rename to api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/ThrowableReason.java index 16fd1a86692bcdc1a15ae466fb0a700742764174..87a9690baa0fa44a430517c8c57f8472d6dba5d3 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/api/AuthenticationAPI.java +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsapi/exception/ThrowableReason.java @@ -13,14 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ch.ethz.sis.afsserver.api; +package ch.ethz.sis.afsapi.exception; -import lombok.NonNull; +import java.io.Serializable; -public interface AuthenticationAPI { - public String login(@NonNull String userId, @NonNull String password) throws Exception; +public class ThrowableReason extends Throwable { + private Serializable reason; - public Boolean isSessionValid() throws Exception; + public ThrowableReason(Serializable reason) { + this.reason = reason; + } - public Boolean logout() throws Exception; + public Serializable getReason() { + return reason; + } } diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java new file mode 100644 index 0000000000000000000000000000000000000000..63dc36dbcbea41f234b0348c8ba7348ff5bc47ea --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java @@ -0,0 +1,255 @@ +package ch.ethz.sis.afsclient.client; + +import java.io.ByteArrayInputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.*; +import java.util.stream.Stream; + +import ch.ethz.sis.afsapi.api.PublicAPI; +import ch.ethz.sis.afsapi.dto.ApiResponse; +import ch.ethz.sis.afsapi.dto.File; +import ch.ethz.sis.afsclient.client.exception.ClientExceptions; +import ch.ethz.sis.afsjson.JsonObjectMapper; +import ch.ethz.sis.afsjson.jackson.JacksonObjectMapper; +import lombok.NonNull; + +public final class AfsClient implements PublicAPI +{ + + private static final int DEFAULT_PACKAGE_SIZE_IN_BYTES = 1024; + + private static final int DEFAULT_TIMEOUT_IN_MILLIS = 30000; + + private final int maxReadSizeInBytes; + + private final int timeout; + + private String sessionToken; + + private final URI serverUri; + + private final JsonObjectMapper jsonObjectMapper; + + public AfsClient(final URI serverUri) + { + this(serverUri, DEFAULT_PACKAGE_SIZE_IN_BYTES, DEFAULT_TIMEOUT_IN_MILLIS); + } + + public AfsClient(final URI serverUri, final int maxReadSizeInBytes, final int timeout) + { + this.maxReadSizeInBytes = maxReadSizeInBytes; + this.timeout = timeout; + this.serverUri = serverUri; + this.jsonObjectMapper = new JacksonObjectMapper(); + } + + public URI getServerUri() + { + return serverUri; + } + + public int getMaxReadSizeInBytes() + { + return maxReadSizeInBytes; + } + + public String getSessionToken() + { + return sessionToken; + } + + public void setSessionToken(final String sessionToken) + { + this.sessionToken = sessionToken; + } + + private static String urlEncode(final String s) + { + return URLEncoder.encode(s, StandardCharsets.UTF_8); + } + + @Override + public @NonNull String login(@NonNull final String userId, @NonNull final String password) + throws Exception + { + String result = request("POST", "login", Map.of(), + (userId + ":" + password).getBytes()); + setSessionToken(result); + return result; + } + + @Override + public @NonNull Boolean isSessionValid() throws Exception + { + if (getSessionToken() == null) + { + throw new IllegalStateException("No session information detected!"); + } + return request("GET", "isSessionValid", Map.of("sessionToken", getSessionToken())); + } + + @Override + public @NonNull Boolean logout() throws Exception + { + if (getSessionToken() == null) + { + throw new IllegalStateException("No session information detected!"); + } +// Boolean result = request("POST", "logout", Map.of(), getSessionToken().getBytes()); + Boolean result = request("POST", "logout", Map.of("sessionToken", getSessionToken())); + setSessionToken(null); + return result; + } + + @Override + public @NonNull List<File> list(@NonNull final String owner, @NonNull final String source, + @NonNull final Boolean recursively) throws Exception + { + return null; + } + + @Override + public @NonNull byte[] read(@NonNull final String owner, @NonNull final String source, + @NonNull final Long offset, @NonNull final Integer limit) throws Exception + { + return new byte[0]; + } + + @Override + public @NonNull Boolean write(@NonNull final String owner, @NonNull final String source, + @NonNull final Long offset, final byte @NonNull [] data, + final byte @NonNull [] md5Hash) throws Exception + { + return null; + } + + @Override + public @NonNull Boolean delete(@NonNull final String owner, @NonNull final String source) + throws Exception + { + return null; + } + + @Override + public @NonNull Boolean copy(@NonNull final String sourceOwner, @NonNull final String source, + @NonNull final String targetOwner, + @NonNull final String target) + throws Exception + { + return null; + } + + @Override + public @NonNull Boolean move(@NonNull final String sourceOwner, @NonNull final String source, + @NonNull final String targetOwner, + @NonNull final String target) + throws Exception + { + return null; + } + + @Override + public void begin(final UUID transactionId) throws Exception + { + + } + + @Override + public Boolean prepare() throws Exception + { + return null; + } + + @Override + public void commit() throws Exception + { + + } + + @Override + public void rollback() throws Exception + { + + } + + @Override + public List<UUID> recover() throws Exception + { + return null; + } + + private <T> T request(@NonNull final String httpMethod, @NonNull final String apiMethod, + @NonNull final Map<String, String> parameters) throws Exception + { + return request(httpMethod, apiMethod, parameters, new byte[0]); + } + + @SuppressWarnings({ "OptionalGetWithoutIsPresent", "unchecked" }) + private <T> T request(@NonNull final String httpMethod, @NonNull final String apiMethod, + @NonNull final Map<String, String> parameters, final byte @NonNull [] body) + throws Exception + { + HttpClient.Builder clientBuilder = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_1_1) + .followRedirects(HttpClient.Redirect.NORMAL) + .connectTimeout(Duration.ofMillis(timeout)); + Map<String, String> params = parameters; + + HttpClient client = clientBuilder.build(); + + final String query = Stream.concat( + Stream.of(new AbstractMap.SimpleImmutableEntry<>("method", apiMethod)), + params.entrySet().stream()) + .map(entry -> urlEncode(entry.getKey()) + "=" + urlEncode(entry.getValue())) + .reduce((s1, s2) -> s1 + "&" + s2).get(); + + final URI uri = + new URI(serverUri.getScheme(), null, serverUri.getHost(), serverUri.getPort(), + serverUri.getPath(), query, null); + + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(uri) + .version(HttpClient.Version.HTTP_1_1) + .timeout(Duration.ofMillis(timeout)) + .method(httpMethod, HttpRequest.BodyPublishers.ofByteArray(body)); + + final HttpRequest request = builder.build(); + + final HttpResponse<byte[]> httpResponse = + client.send(request, HttpResponse.BodyHandlers.ofByteArray()); + + final int statusCode = httpResponse.statusCode(); + if (statusCode >= 200 && statusCode < 300) + { + final ApiResponse response = + jsonObjectMapper.readValue(new ByteArrayInputStream(httpResponse.body()), + ApiResponse.class); + + if (response.getError() != null) + { + throw ClientExceptions.API_ERROR.getInstance(response.getError()); + } else + { + return (T) response.getResult(); + } + } else if (statusCode >= 400 && statusCode < 500) + { + throw ClientExceptions.CLIENT_ERROR.getInstance(statusCode); + } else if (statusCode >= 500 && statusCode < 600) + { + throw ClientExceptions.SERVER_ERROR.getInstance(statusCode); + } else + { + throw ClientExceptions.OTHER_ERROR.getInstance(statusCode); + } + } + +} diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/jackson/JSONObjectMapper.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AtomicFileSystemClientParameter.java similarity index 50% rename from lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/jackson/JSONObjectMapper.java rename to api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AtomicFileSystemClientParameter.java index 3f2bfcf067304029df9ba71ada88a7328345aed2..7c1ac8e11899d963039aeae94d47e179f7db0113 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/jackson/JSONObjectMapper.java +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AtomicFileSystemClientParameter.java @@ -1,5 +1,5 @@ /* - * Copyright ETH 2022 - 2023 Zürich, Scientific IT Services + * Copyright 2022 ETH Zürich, SIS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,19 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ch.ethz.sis.shared.json.jackson; -import java.io.InputStream; -import java.nio.charset.Charset; +package ch.ethz.sis.afsclient.client; -public interface JSONObjectMapper { +public enum AtomicFileSystemClientParameter { + // + // Parameters from the AFS Library + // + jsonObjectMapperClass, - Charset getCharset(); + // + // Parameters for the HTTP server + // + httpServerPath, - void setCharset(Charset charset); + httpServerPort, - <T> T readValue(InputStream src, Class<T> valueType) throws Exception; - - byte[] writeValue(Object value) throws Exception; - -} + // + // Parameters for the API server + // + maxReadSizeInBytes, // This is the chunk size used by the API, sizes between 1 and 6 megabytes are typical, anything bigger is unlikely to provide performance benefits because we are limited by the http package sizes +} \ No newline at end of file diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/exception/ClientExceptions.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/exception/ClientExceptions.java new file mode 100644 index 0000000000000000000000000000000000000000..62ad2d74db0ffbbc43bcc3cd602b6ca512cf5cb0 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/exception/ClientExceptions.java @@ -0,0 +1,54 @@ +package ch.ethz.sis.afsclient.client.exception; + +import static ch.ethz.sis.afsapi.dto.ExceptionType.ClientDeveloperCodingError; +import static ch.ethz.sis.afsapi.dto.ExceptionType.CoreDeveloperCodingError; +import static ch.ethz.sis.afsapi.dto.ExceptionType.UnknownError; +import static ch.ethz.sis.afsapi.dto.ExceptionType.UserUsageError; + +import java.util.List; + +import ch.ethz.sis.afsapi.dto.ExceptionReason; +import ch.ethz.sis.afsapi.dto.ExceptionType; +import ch.ethz.sis.afsapi.exception.RuntimeExceptionTemplate; +import ch.ethz.sis.afsapi.exception.ExceptionTemplateHolder; + +public enum ClientExceptions implements ExceptionTemplateHolder { + + // APIServer + UNKNOWN(RuntimeException.class, List.of(UnknownError), 50001, + "Unknown error of type %s, please contact support, this error comes with message: %s"), + + CLIENT_ERROR(RuntimeException.class, List.of(ClientDeveloperCodingError), 50002, + "Client error HTTP response. Response code: %d"), + + SERVER_ERROR(RuntimeException.class, List.of(CoreDeveloperCodingError), 50003, + "Server error HTTP response. Response code: %d"), + + OTHER_ERROR(RuntimeException.class, List.of(UnknownError), 50004, + "Unexpected HTTP response. Response code: %d"), + + API_ERROR(IllegalArgumentException.class, List.of(UserUsageError), 50005, + "API error. Error message: '%s'"); + + private final RuntimeExceptionTemplate template; + + ClientExceptions(Class<?> clazz, List<ExceptionType> types, int code, String messageTemplate) { + this.template = new RuntimeExceptionTemplate(3, clazz, types, code, messageTemplate); + } + + public RuntimeException getInstance(Object... args) { + return template.getInstance(args); + } + + public Exception getCheckedInstance(Object... args) { + return template.getCheckedInstance(args); + } + + public static void throwInstance(ClientExceptions exception, Object... args) { + throw exception.getInstance(args); + } + + public ExceptionReason getCause(Object... args) { + return template.getReason(args); + } +} diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/lombok.config b/api-data-store-server-java/src/main/java/ch/ethz/sis/lombok.config new file mode 100644 index 0000000000000000000000000000000000000000..c8df289e28ed77a2f80207e256f1fde2bc72e7a5 --- /dev/null +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/lombok.config @@ -0,0 +1,4 @@ +lombok.nonNull.exceptionType = IllegalArgumentException +lombok.addGeneratedAnnotation = false +lombok.addJavaxGeneratedAnnotation = false +lombok.anyConstructor.addConstructorProperties=true \ No newline at end of file diff --git a/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/AfsClientTest.java b/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/AfsClientTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a9ec352489faa11d83b5bfd371c1fbc6972a4cf7 --- /dev/null +++ b/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/AfsClientTest.java @@ -0,0 +1,148 @@ +package ch.ethz.sis.afsclient.client; + +import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.containsString; + +import java.net.URI; + +import org.junit.*; + +public class AfsClientTest +{ + + private static DummyHttpServer httpServer; + + private AfsClient afsClient; + private static final int HTTP_SERVER_PORT = 8085; + private static final String HTTP_SERVER_PATH = "/fileserver"; + + @Before + public void setUp() throws Exception { + httpServer = new DummyHttpServer(HTTP_SERVER_PORT, HTTP_SERVER_PATH); + httpServer.start(); + afsClient = new AfsClient( + new URI("http", null, "localhost", HTTP_SERVER_PORT, + HTTP_SERVER_PATH, null, null)); + } + + @After + public void tearDown() { + httpServer.stop(); + } + + @Test + public void login_methodIsPost() throws Exception + { + final String token = afsClient.login("test", "test"); + assertNotNull(token); + assertEquals(token, afsClient.getSessionToken()); + assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); + } + + @Test + public void isSessionValid_withoutLogin_throwsException() throws Exception + { + try + { + afsClient.isSessionValid(); + fail(); + } catch (IllegalStateException e) { + assertThat(e.getMessage(), containsString("No session information detected!")); + } + } + + @Test + public void isSessionValid_afterLogin_methodIsGet() throws Exception + { + afsClient.login("test", "test"); + httpServer.setNextResponse("{\"result\": true}"); + + Boolean result = afsClient.isSessionValid(); + + assertTrue(result); + assertEquals("GET", httpServer.getHttpExchange().getRequestMethod()); + } + + @Test + public void logout_withoutLogin_throwsException() throws Exception + { + try + { + afsClient.logout(); + fail(); + } catch (IllegalStateException e) { + assertThat(e.getMessage(), containsString("No session information detected!")); + } + } + + @Test + public void logout_sessionTokenIsCleared() throws Exception + { + afsClient.login("test", "test"); + assertNotNull(afsClient.getSessionToken()); + + httpServer.setNextResponse("{\"result\": true}"); + + Boolean result = afsClient.logout(); + assertTrue(result); + assertNull(afsClient.getSessionToken()); + assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); + } + + @Test + public void testList() throws Exception + { + } + + @Test + public void testRead()throws Exception + { + } + + @Test + public void testWrite()throws Exception + { + } + + @Test + public void testDelete()throws Exception + { + } + + @Test + public void testCopy()throws Exception + { + } + + @Test + public void testMove() + { + } + + @Test + public void testBegin() + { + } + + @Test + public void testPrepare() + { + } + + @Test + public void testCommit() + { + } + + @Test + public void testRollback() + { + } + + @Test + public void testRecover() + { + } + +} \ No newline at end of file diff --git a/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/DummyHttpServer.java b/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/DummyHttpServer.java new file mode 100644 index 0000000000000000000000000000000000000000..f4d9337fce7294bc4a63ceaa945978785a6914bf --- /dev/null +++ b/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/DummyHttpServer.java @@ -0,0 +1,78 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsclient.client; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +public final class DummyHttpServer +{ + private HttpServer httpServer; + + private final int httpServerPort; // 8085 + + private final String httpServerPath; // "/fileserver" + + private static final String DEFAULT_RESPONSE = "{\"result\": \"success\"}"; + + private String nextResponse = DEFAULT_RESPONSE; + private HttpExchange httpExchange; + + public DummyHttpServer(int httpServerPort, String httpServerPath) throws IOException + { + this.httpServerPort = httpServerPort; + this.httpServerPath = httpServerPath; + httpServer = HttpServer.create(new InetSocketAddress(httpServerPort), 0); + httpServer.createContext(httpServerPath, new HttpHandler() + { + public void handle(HttpExchange exchange) throws IOException + { + byte[] response = nextResponse.getBytes(); + exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length); + exchange.getResponseBody().write(response); + exchange.close(); + httpExchange = exchange; + } + }); + } + + public void start() + { + httpServer.start(); + } + + public void stop() + { + httpServer.stop(0); + } + + public void setNextResponse(String response) + { + this.nextResponse = response; + } + + public HttpExchange getHttpExchange() { + return httpExchange; + } + +} diff --git a/build/settings.gradle b/build/settings.gradle index d790bf35e109db3f87a4755aed2bffbcc3734e9f..c0216a4822aedef3214cbab94c77c84d7583be67 100644 --- a/build/settings.gradle +++ b/build/settings.gradle @@ -1,4 +1,6 @@ -includeFlat 'lib-commonbase', 'lib-common', 'api-openbis-java', 'lib-openbis-common', 'lib-authentication', 'lib-dbmigration', 'server-application-server', - 'server-original-data-store', 'server-screening', 'core-plugin-openbis', 'app-openbis-installer', - 'lib-image-readers', 'test-ui-core', 'test-api-openbis-javascript', 'server-external-data-store', 'ui-admin', - 'lib-microservice-server', 'lib-transactional-file-system', 'server-data-store', 'ui-eln-lims', 'api-openbis-javascript' \ No newline at end of file +includeFlat 'lib-commonbase', 'lib-common', 'api-openbis-java', 'lib-openbis-common', 'lib-authentication', + 'lib-dbmigration', 'server-application-server', 'server-original-data-store', 'server-screening', + 'core-plugin-openbis', 'app-openbis-installer', 'lib-image-readers', 'test-ui-core', + 'test-api-openbis-javascript', 'server-external-data-store', 'ui-admin', 'lib-microservice-server', + 'lib-transactional-file-system', 'server-data-store', 'ui-eln-lims', 'api-openbis-javascript', + 'lib-json', 'api-data-store-server-java' \ No newline at end of file diff --git a/lib-json/.gitignore b/lib-json/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fe63103f29daac95f5989e4cfc75a0fb0cff693b --- /dev/null +++ b/lib-json/.gitignore @@ -0,0 +1,7 @@ +/build/ +/bin/ +/out/ +/.idea/ +/target/ +*.iml +*.eml \ No newline at end of file diff --git a/lib-json/build.gradle b/lib-json/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..1c7c2813097ed41b51b0b485f5e37b6901b159c1 --- /dev/null +++ b/lib-json/build.gradle @@ -0,0 +1,12 @@ +apply from: '../build/javaproject.gradle' + +dependencies { + implementation 'fasterxml:jackson-annotations:2.9.10', + 'fasterxml:jackson-core:2.9.10', + 'fasterxml:jackson-databind:2.9.10.8', + 'fasterxml:jackson-datatype-jsr310:2.9.10' + + testImplementation 'testng:testng:6.8-CISD' + + testRuntimeOnly 'hamcrest:hamcrest-core:1.3' +} diff --git a/lib-json/gradle/wrapper/gradle-wrapper.jar b/lib-json/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5c2d1cf016b3885f6930543d57b744ea8c220a1a Binary files /dev/null and b/lib-json/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lib-json/gradle/wrapper/gradle-wrapper.properties b/lib-json/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..7745c4ec463c7bb1c4ff6a5e58db239890345152 --- /dev/null +++ b/lib-json/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://sissource.ethz.ch/openbis/openbis-public/openbis-ivy/-/raw/main/gradle/distribution/7.4/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lib-json/gradlew b/lib-json/gradlew new file mode 100755 index 0000000000000000000000000000000000000000..a69d9cb6c20655813e44515156e7253a2a239138 --- /dev/null +++ b/lib-json/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original 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 POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${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 "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# 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 ;; #( + MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/lib-json/gradlew.bat b/lib-json/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..f127cfd49d4024c3e1e0d08ba56399221b4fb25d --- /dev/null +++ b/lib-json/gradlew.bat @@ -0,0 +1,91 @@ +@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% equ 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% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib-json/source/java/ch/ethz/sis/afsjson/JsonObjectMapper.java b/lib-json/source/java/ch/ethz/sis/afsjson/JsonObjectMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..a826a6f627a7efbafbc198d967223110f423bb7b --- /dev/null +++ b/lib-json/source/java/ch/ethz/sis/afsjson/JsonObjectMapper.java @@ -0,0 +1,28 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsjson; + +import java.io.InputStream; + +public interface JsonObjectMapper +{ + <T> T readValue(InputStream src, Class<T> valueType) throws Exception; + + byte[] writeValue(Object value) throws Exception; + +} diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/jackson/JacksonObjectMapper.java b/lib-json/source/java/ch/ethz/sis/afsjson/jackson/JacksonObjectMapper.java similarity index 58% rename from lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/jackson/JacksonObjectMapper.java rename to lib-json/source/java/ch/ethz/sis/afsjson/jackson/JacksonObjectMapper.java index 4ec1f9e1f75bb46437670c2e073bb6b0caf73eb7..45fff2e94374fbf8d58f0e2191b08f702f2e1ac1 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/jackson/JacksonObjectMapper.java +++ b/lib-json/source/java/ch/ethz/sis/afsjson/jackson/JacksonObjectMapper.java @@ -1,29 +1,30 @@ /* - * Copyright ETH 2018 - 2023 Zürich, Scientific IT Services + * Copyright ETH 2023 Zürich, Scientific IT Services * - * 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 + * 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 * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * - * 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. */ -package ch.ethz.sis.shared.json.jackson; -import ch.ethz.sis.shared.json.JSONObjectMapper; -import com.fasterxml.jackson.core.type.TypeReference; +package ch.ethz.sis.afsjson.jackson; + +import ch.ethz.sis.afsjson.JsonObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.io.InputStream; -public class JacksonObjectMapper implements JSONObjectMapper +public class JacksonObjectMapper implements JsonObjectMapper { // // Singleton @@ -35,7 +36,7 @@ public class JacksonObjectMapper implements JSONObjectMapper jacksonObjectMapper = new JacksonObjectMapper(); } - public static JSONObjectMapper getInstance() + public static JsonObjectMapper getInstance() { return jacksonObjectMapper; } diff --git a/lib-json/source/java/ch/ethz/sis/afsjson/jackson/JsonObjectMapper.java b/lib-json/source/java/ch/ethz/sis/afsjson/jackson/JsonObjectMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..c0e74656097b7ec34f8181579f79c1cbc94b2336 --- /dev/null +++ b/lib-json/source/java/ch/ethz/sis/afsjson/jackson/JsonObjectMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsjson.jackson; + +import java.io.InputStream; +import java.nio.charset.Charset; + +public interface JsonObjectMapper { + + Charset getCharset(); + + void setCharset(Charset charset); + + <T> T readValue(InputStream src, Class<T> valueType) throws Exception; + + byte[] writeValue(Object value) throws Exception; + +} diff --git a/lib-transactional-file-system/build.gradle b/lib-transactional-file-system/build.gradle index e9fc6a1dc7286f834adbd59fc556e30c349d190f..d64c65cbecff7d747737f62088d3990bea341472 100644 --- a/lib-transactional-file-system/build.gradle +++ b/lib-transactional-file-system/build.gradle @@ -14,7 +14,8 @@ repositories { dependencies { annotationProcessor 'lombok:lombok:1.18.22' - implementation 'lombok:lombok:1.18.22', + implementation project(':lib-json'), + 'lombok:lombok:1.18.22', 'log4j:log4j-api:2.10.0', 'log4j:log4j-core:2.10.0', 'fasterxml:jackson-annotations:2.9.10', diff --git a/lib-transactional-file-system/settings.gradle b/lib-transactional-file-system/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..048a4c86d15e4deac0dbf45119c319fcf29fc615 --- /dev/null +++ b/lib-transactional-file-system/settings.gradle @@ -0,0 +1 @@ +includeFlat 'lib-json' \ No newline at end of file diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java index fbb4bea9fe957ab126e07658505617b27bf67a55..6b7780928c1f3499ba70e06ef74a982b3ef54859 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java @@ -22,7 +22,7 @@ import ch.ethz.sis.afs.exception.AFSExceptions; import ch.ethz.sis.afs.api.dto.File; import ch.ethz.sis.afs.manager.operation.*; import ch.ethz.sis.shared.io.IOUtils; -import ch.ethz.sis.shared.json.JSONObjectMapper; +import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.afs.dto.Lock; import ch.ethz.sis.afs.dto.LockType; @@ -47,7 +47,7 @@ public class TransactionConnection implements TransactionalFileSystem { } private LockManager<UUID, String> lockManager; - private JSONObjectMapper jsonObjectMapper; + private JsonObjectMapper jsonObjectMapper; private Transaction transaction; private State state; private String writeAheadLogRoot; @@ -58,7 +58,7 @@ public class TransactionConnection implements TransactionalFileSystem { * Used only to create new transactions */ TransactionConnection(LockManager<UUID, String> lockManager, - JSONObjectMapper jsonObjectMapper, + JsonObjectMapper jsonObjectMapper, String writeAheadLogRoot, String storageRoot, RecoveredTransactions recoveredTransactions) { @@ -72,7 +72,7 @@ public class TransactionConnection implements TransactionalFileSystem { * Can be used to recover a committed transactions after a crash */ TransactionConnection(LockManager<UUID, String> lockManager, - JSONObjectMapper jsonObjectMapper, + JsonObjectMapper jsonObjectMapper, Transaction transaction) { this.lockManager = lockManager; this.jsonObjectMapper = jsonObjectMapper; diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionManager.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionManager.java index 47820c04c4987d0fb619df720d2b136e2703f287..a45509625ed372b12c3f74aea4d955241aece9f5 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionManager.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionManager.java @@ -17,7 +17,7 @@ package ch.ethz.sis.afs.manager; import ch.ethz.sis.afs.api.dto.File; import ch.ethz.sis.shared.io.IOUtils; -import ch.ethz.sis.shared.json.JSONObjectMapper; +import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.shared.log.LogManager; import ch.ethz.sis.shared.log.Logger; import ch.ethz.sis.afs.dto.Transaction; @@ -37,12 +37,12 @@ public class TransactionManager { private static final Logger logger = LogManager.getLogger(TransactionManager.class); private LockManager<UUID, String> lockManager; - private JSONObjectMapper jsonObjectMapper; + private JsonObjectMapper jsonObjectMapper; private String writeAheadLogRoot; private String storageRoot; private RecoveredTransactions recoveredTransactions; - public TransactionManager(JSONObjectMapper jsonObjectMapper, + public TransactionManager(JsonObjectMapper jsonObjectMapper, String writeAheadLogRoot, String storageRoot) throws IOException { this.lockManager = new LockManager<>(new PathLockFinder()); diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/startup/Main.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/startup/Main.java index 3249bb23dba457bb9b9fed85fcd626b0d5cbe24b..d0f442d14e91bbd36c7e6d83a975d44b4b57da95 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/startup/Main.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/startup/Main.java @@ -16,7 +16,7 @@ package ch.ethz.sis.afs.startup; import ch.ethz.sis.afs.manager.TransactionManager; -import ch.ethz.sis.shared.json.JSONObjectMapper; +import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.shared.log.LogFactory; import ch.ethz.sis.shared.log.LogFactoryFactory; import ch.ethz.sis.shared.log.LogManager; @@ -48,7 +48,7 @@ public class Main LogManager.setLogFactory(logFactory); // - JSONObjectMapper jsonObjectMapper = + JsonObjectMapper jsonObjectMapper = configuration.getSharableInstance(AtomicFileSystemParameter.jsonObjectMapperClass); String writeAheadLogRoot = configuration.getStringProperty(AtomicFileSystemParameter.writeAheadLogRoot); diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/JSONObjectMapper.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/JSONObjectMapper.java deleted file mode 100644 index ab3befa85b9dce77ae5bf607c6c50b8cab88ec2c..0000000000000000000000000000000000000000 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/json/JSONObjectMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright ETH 2018 - 2023 Zürich, Scientific IT Services - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ch.ethz.sis.shared.json; - -import java.io.InputStream; - -public interface JSONObjectMapper { - <T> T readValue(InputStream src, Class<T> valueType) throws Exception; - - byte[] writeValue(Object value) throws Exception; - -} diff --git a/lib-transactional-file-system/src/main/resources/afs-config.properties b/lib-transactional-file-system/src/main/resources/afs-config.properties index 794e68d16b2d5bdbfd5c2b386c14b47262b87d73..29f2cb57ea70e543136d27009381023e34c26a66 100755 --- a/lib-transactional-file-system/src/main/resources/afs-config.properties +++ b/lib-transactional-file-system/src/main/resources/afs-config.properties @@ -1,5 +1,5 @@ logFactoryClass=ch.ethz.sis.shared.log.log4j2.Log4J2LogFactory -jsonObjectMapperClass=ch.ethz.sis.shared.json.jackson.JacksonObjectMapper +jsonObjectMapperClass=ch.ethz.sis.afsjson.jackson.JacksonObjectMapper logConfigFile=afs-config-log4j2.xml # Where all the transactions information is written until the prepare step # For performance reasons should be on the save volume as the configured storage diff --git a/lib-transactional-file-system/src/test/java/ch/ethz/sis/afs/AFSEnvironment.java b/lib-transactional-file-system/src/test/java/ch/ethz/sis/afs/AFSEnvironment.java index 1d6a3fd97bb81a99ee13c4ec83b66b9f9740a2c5..1bded2ba24265f37ce5f0d8ea87aea373ec15cc5 100644 --- a/lib-transactional-file-system/src/test/java/ch/ethz/sis/afs/AFSEnvironment.java +++ b/lib-transactional-file-system/src/test/java/ch/ethz/sis/afs/AFSEnvironment.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afs; -import ch.ethz.sis.shared.json.jackson.JacksonObjectMapper; +import ch.ethz.sis.afsjson.jackson.JacksonObjectMapper; import ch.ethz.sis.shared.startup.Configuration; import ch.ethz.sis.shared.log.log4j2.Log4J2LogFactory; import ch.ethz.sis.afs.startup.AtomicFileSystemParameter; diff --git a/lib-transactional-file-system/src/test/java/ch/ethz/sis/afs/manager/AbstractTransactionConnectionTest.java b/lib-transactional-file-system/src/test/java/ch/ethz/sis/afs/manager/AbstractTransactionConnectionTest.java index c82068ceee204683e5ae31f7ba212a6b460ca8aa..c0bd1714f14b1c3d1501892a63c07c1f4677e041 100644 --- a/lib-transactional-file-system/src/test/java/ch/ethz/sis/afs/manager/AbstractTransactionConnectionTest.java +++ b/lib-transactional-file-system/src/test/java/ch/ethz/sis/afs/manager/AbstractTransactionConnectionTest.java @@ -17,7 +17,7 @@ package ch.ethz.sis.afs.manager; import ch.ethz.sis.afs.api.dto.File; import ch.ethz.sis.shared.io.IOUtils; -import ch.ethz.sis.shared.json.JSONObjectMapper; +import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.afs.AFSEnvironment; import ch.ethz.sis.afs.AbstractTest; import ch.ethz.sis.afs.dto.Transaction; @@ -38,7 +38,7 @@ import static ch.ethz.sis.shared.io.IOUtils.setFilePermissions; public abstract class AbstractTransactionConnectionTest extends AbstractTest { - private JSONObjectMapper jsonObjectMapper; + private JsonObjectMapper jsonObjectMapper; private LockManager<UUID, String> lockManager; private TransactionConnection transaction; diff --git a/server-data-store/build.gradle b/server-data-store/build.gradle index 6d5b75d246b93b5d04a423a596fb3e579be8afb9..b468482a87654872625e93d67492554f679e1fef 100644 --- a/server-data-store/build.gradle +++ b/server-data-store/build.gradle @@ -1,3 +1,7 @@ +evaluationDependsOn(':lib-json') +evaluationDependsOn(':api-data-store-server-java') +evaluationDependsOn(':lib-transactional-file-system') + apply plugin: 'java' apply plugin: 'application' @@ -15,13 +19,15 @@ repositories { dependencies { annotationProcessor 'lombok:lombok:1.18.22' implementation project(':lib-transactional-file-system'), + project(':api-data-store-server-java'), + project(':lib-json'), 'lombok:lombok:1.18.22', 'io.netty:netty-all:4.1.68.Final', 'log4j:log4j-api:2.10.0', 'log4j:log4j-core:2.10.0', 'openbis:openbis-v3-api-batteries-included:20.10.5'; - testImplementation 'junit:junit:4.10' - testRuntimeOnly 'hamcrest:hamcrest-core:1.3' + testImplementation 'junit:junit:4.10', + 'hamcrest:hamcrest-core:1.3' } task AFSServerDevelopmentEnvironmentStart(type: JavaExec) { diff --git a/server-data-store/settings.gradle b/server-data-store/settings.gradle index b1fcbd28024378b0ab6157e63697c10cfdf01989..47d57e7460445ba72b7781ac397d5068a34fff2c 100644 --- a/server-data-store/settings.gradle +++ b/server-data-store/settings.gradle @@ -1 +1 @@ -includeFlat 'lib-transactional-file-system' +includeFlat 'lib-transactional-file-system', 'api-data-store-server-java', 'lib-json' diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/http/impl/NettyHttpHandler.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/http/impl/NettyHttpHandler.java index b9efb2ba185fd2e3dedbfe9d0b639cb0cb9b3241..50099d69e6814d9989fbe65405320b8403ef096c 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/http/impl/NettyHttpHandler.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/http/impl/NettyHttpHandler.java @@ -26,75 +26,96 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.*; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.Set; import static io.netty.handler.codec.http.HttpMethod.*; -public class NettyHttpHandler extends ChannelInboundHandlerAdapter { +public class NettyHttpHandler extends ChannelInboundHandlerAdapter +{ private static final Logger logger = LogManager.getLogger(NettyHttpServer.class); + private static final byte[] NOT_FOUND = "404 NOT FOUND".getBytes(); + private static final ByteBuf NOT_FOUND_BUFFER = Unpooled.wrappedBuffer(NOT_FOUND); + private static final Set<HttpMethod> allowedMethods = Set.of(GET, POST, PUT, DELETE); private final String uri; + private final HttpServerHandler httpServerHandler; - public NettyHttpHandler(String uri, HttpServerHandler httpServerHandler) { + public NettyHttpHandler(String uri, HttpServerHandler httpServerHandler) + { this.uri = uri; this.httpServerHandler = httpServerHandler; } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof FullHttpRequest) { + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception + { + if (msg instanceof FullHttpRequest) + { final FullHttpRequest request = (FullHttpRequest) msg; QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri(), true); if (queryStringDecoder.path().equals(uri) && - allowedMethods.contains(request.method())) { + allowedMethods.contains(request.method())) + { FullHttpResponse response = null; ByteBuf content = request.content(); - try { - byte[] contentAsArray = (content.hasArray())?content.array():null; - HttpResponse apiResponse = httpServerHandler.process(request.method(), queryStringDecoder.parameters(), contentAsArray); - HttpResponseStatus status = (!apiResponse.isError())?HttpResponseStatus.OK:HttpResponseStatus.BAD_REQUEST; + try + { + byte[] array = new byte[content.readableBytes()]; + content.readBytes(array); + HttpResponse apiResponse = httpServerHandler.process(request.method(), + queryStringDecoder.parameters(), array); + HttpResponseStatus status = (!apiResponse.isError()) ? + HttpResponseStatus.OK : + HttpResponseStatus.BAD_REQUEST; response = getHttpResponse( status, apiResponse.getContentType(), Unpooled.wrappedBuffer(apiResponse.getBody()), apiResponse.getBody().length); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); - } finally { + } finally + { content.release(); } - } else { + } else + { FullHttpResponse response = getHttpResponse( - HttpResponseStatus.NOT_FOUND, - "text/plain", - NOT_FOUND_BUFFER, - NOT_FOUND.length); + HttpResponseStatus.NOT_FOUND, + "text/plain", + NOT_FOUND_BUFFER, + NOT_FOUND.length); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } - } else { + } else + { super.channelRead(ctx, msg); } } @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception + { ctx.flush(); } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception + { logger.catching(cause); byte[] causeBytes = cause.getMessage().getBytes(); FullHttpResponse response = getHttpResponse( - HttpResponseStatus.INTERNAL_SERVER_ERROR, - "text/plain", - Unpooled.wrappedBuffer(causeBytes), - causeBytes.length - ); + HttpResponseStatus.INTERNAL_SERVER_ERROR, + "text/plain", + Unpooled.wrappedBuffer(causeBytes), + causeBytes.length + ); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } @@ -102,7 +123,8 @@ public class NettyHttpHandler extends ChannelInboundHandlerAdapter { HttpResponseStatus status, String contentType, ByteBuf content, - int contentLength) { + int contentLength) + { FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, status, diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/http/impl/NettyHttpServer.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/http/impl/NettyHttpServer.java index b96132285bfa51c82b2207b5bd0dc01713960c8e..dbfa4dc78b0c0e82dc6094df920116f6afc0c58b 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/http/impl/NettyHttpServer.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/http/impl/NettyHttpServer.java @@ -31,77 +31,101 @@ import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.util.concurrent.Future; -public class NettyHttpServer implements HttpServer { +public class NettyHttpServer implements HttpServer +{ private static final Logger logger = LogManager.getLogger(NettyHttpServer.class); private final EventLoopGroup masterGroup; + private final EventLoopGroup slaveGroup; private ChannelFuture channel; - public NettyHttpServer() { + public NettyHttpServer() + { masterGroup = new NioEventLoopGroup(); slaveGroup = new NioEventLoopGroup(); } - public void start(int port, int maxContentLength, String uri, HttpServerHandler httpServerHandler) { + public void start(int port, int maxContentLength, String uri, + HttpServerHandler httpServerHandler) + { Integer maxQueueLengthForIncomingConnections = 128; - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new Thread() + { @Override - public void run() { + public void run() + { shutdown(true); } }); - try { + try + { final ServerBootstrap bootstrap = new ServerBootstrap() .group(masterGroup, slaveGroup) .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer<SocketChannel>() { + .childHandler(new ChannelInitializer<SocketChannel>() + { @Override - public void initChannel(final SocketChannel ch) throws Exception { + public void initChannel(final SocketChannel ch) throws Exception + { ch.pipeline().addLast("codec", new HttpServerCodec()); - ch.pipeline().addLast("aggregator", new HttpObjectAggregator(maxContentLength)); - ch.pipeline().addLast("request", new NettyHttpHandler(uri, httpServerHandler)); + ch.pipeline().addLast("aggregator", + new HttpObjectAggregator(maxContentLength)); + ch.pipeline().addLast("request", + new NettyHttpHandler(uri, httpServerHandler)); } }) .option(ChannelOption.SO_BACKLOG, maxQueueLengthForIncomingConnections) .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE) .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE); channel = bootstrap.bind(port).sync(); - } catch (final Exception ex) { + } catch (final Exception ex) + { logger.catching(ex); } } - public void shutdown(boolean gracefully) { - try { + public void shutdown(boolean gracefully) + { + try + { channel.channel().close(); - } catch (Exception ex) { + } catch (Exception ex) + { logger.catching(ex); } - try { - if (gracefully) { + try + { + if (gracefully) + { Future slaveShutdown = slaveGroup.shutdownGracefully(); slaveShutdown.await(); - } else { + } else + { slaveGroup.shutdown(); } - } catch (Exception ex) { + } catch (Exception ex) + { logger.catching(ex); } - try { - if (gracefully) { + try + { + if (gracefully) + { Future masterShutdown = masterGroup.shutdownGracefully(); masterShutdown.await(); - } else { + } else + { masterGroup.shutdown(); } - } catch (Exception ex) { + } catch (Exception ex) + { logger.catching(ex); } } diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/Server.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/Server.java index 8582537ad240b3a1cdfbd8cbde60f5c9cf8ecedf..21f9c7eac09c6284bf8f445475732cafa5eee614 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/Server.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/Server.java @@ -20,7 +20,7 @@ import ch.ethz.sis.afsserver.server.impl.ApiServerAdapter; import ch.ethz.sis.afsserver.server.observer.APIServerObserver; import ch.ethz.sis.afsserver.server.observer.ServerObserver; import ch.ethz.sis.afsserver.startup.AtomicFileSystemServerParameter; -import ch.ethz.sis.shared.json.jackson.JacksonObjectMapper; +import ch.ethz.sis.afsjson.jackson.JacksonObjectMapper; import ch.ethz.sis.shared.log.LogFactory; import ch.ethz.sis.shared.log.LogFactoryFactory; import ch.ethz.sis.shared.log.LogManager; diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/Worker.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/Worker.java index cb416565e05369020205a93ef236972c780c3b06..82ea2a874a0e25fb41b76640b08f8e730f20853c 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/Worker.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/Worker.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afsserver.server; -import ch.ethz.sis.afsserver.api.PublicAPI; +import ch.ethz.sis.afsapi.api.PublicAPI; import ch.ethz.sis.afsserver.server.performance.PerformanceAuditor; public interface Worker<CONNECTION> extends PublicAPI { diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/impl/ApiServerAdapter.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/impl/ApiServerAdapter.java index c9ee0aebd8e934c62ad0bade6f3c6d5ba0727276..3a8f9991c53b9ca39bcf5eaad8b20dc006e8e594 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/impl/ApiServerAdapter.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/impl/ApiServerAdapter.java @@ -15,13 +15,15 @@ */ package ch.ethz.sis.afsserver.server.impl; +import static java.nio.charset.StandardCharsets.UTF_8; + import ch.ethz.sis.afsserver.exception.HTTPExceptions; import ch.ethz.sis.afsserver.http.*; import ch.ethz.sis.afsserver.server.*; import ch.ethz.sis.afsserver.server.performance.Event; import ch.ethz.sis.afsserver.server.performance.PerformanceAuditor; import ch.ethz.sis.shared.io.IOUtils; -import ch.ethz.sis.shared.json.JSONObjectMapper; +import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.shared.log.LogManager; import ch.ethz.sis.shared.log.Logger; import io.netty.handler.codec.http.HttpMethod; @@ -31,50 +33,56 @@ import java.util.*; /* * This class is supposed to be called by a TCP or HTTP transport class */ -public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler { +public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler +{ private static final Logger logger = LogManager.getLogger(ApiServerAdapter.class); private final APIServer<CONNECTION, Request, Response, API> server; - private final JSONObjectMapper jsonObjectMapper; - private final ApiResponseBuilder apiResponseBuilder; + private final JsonObjectMapper jsonObjectMapper; + + private final ApiResponseBuilder apiResponseBuilder; public ApiServerAdapter( APIServer<CONNECTION, Request, Response, API> server, - JSONObjectMapper jsonObjectMapper) { + JsonObjectMapper jsonObjectMapper) + { this.server = server; this.jsonObjectMapper = jsonObjectMapper; this.apiResponseBuilder = new ApiResponseBuilder(); } - public static HttpMethod getHttpMethod(String apiMethod) { - HttpMethod httpMethod = null; - switch (apiMethod){ - case "delete": - httpMethod = HttpMethod.DELETE; - break; - case "write": - httpMethod = HttpMethod.PUT; - break; + public static HttpMethod getHttpMethod(String apiMethod) + { + switch (apiMethod) + { case "list": case "read": case "isSessionValid": - httpMethod = HttpMethod.GET; - break; - default: - httpMethod = HttpMethod.POST; + return HttpMethod.GET; // all parameters from GET methods come on the query string + case "write": + case "move": + case "login": + case "logout": + return HttpMethod.POST; // all parameters from POST methods come on the body + case "delete": + return HttpMethod.DELETE; // all parameters from DELETE methods come on the body } - return httpMethod; + throw new UnsupportedOperationException("This line SHOULD NOT be unreachable!"); } - public boolean isValidMethod(HttpMethod givenMethod, String apiMethod) { + public boolean isValidMethod(HttpMethod givenMethod, String apiMethod) + { HttpMethod correctMethod = getHttpMethod(apiMethod); return correctMethod == givenMethod; } - public HttpResponse process(HttpMethod httpMethod, Map<String, List<String>> uriParameters, byte[] requestBody) { - try { + public HttpResponse process(HttpMethod httpMethod, Map<String, List<String>> uriParameters, + byte[] requestBody) + { + try + { logger.traceAccess(null); PerformanceAuditor performanceAuditor = new PerformanceAuditor(); @@ -83,22 +91,31 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler { String interactiveSessionKey = null; String transactionManagerKey = null; Map<String, Object> methodParameters = new HashMap<>(); - for (Map.Entry<String, List<String>> entry:uriParameters.entrySet()) { + for (Map.Entry<String, List<String>> entry : uriParameters.entrySet()) + { String value = null; - if (entry.getValue() != null) { - if (entry.getValue().size() == 1) { + if (entry.getValue() != null) + { + if (entry.getValue().size() == 1) + { value = entry.getValue().get(0); - } else if (entry.getValue().size() > 1) { - return getHTTPResponse(new ApiResponse("1", null, HTTPExceptions.INVALID_PARAMETERS.getCause())); + } else if (entry.getValue().size() > 1) + { + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.INVALID_PARAMETERS.getCause())); } } - try { - switch (entry.getKey()) { + try + { + switch (entry.getKey()) + { case "method": method = value; - if (!isValidMethod(httpMethod, method)) { - return getHTTPResponse(new ApiResponse("1", null, HTTPExceptions.INVALID_HTTP_METHOD.getCause())); + if (!isValidMethod(httpMethod, method)) + { + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.INVALID_HTTP_METHOD.getCause())); } break; case "sessionToken": @@ -129,54 +146,80 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler { methodParameters.put(entry.getKey(), value); break; } - } catch (Exception e) { + } catch (Exception e) + { logger.catching(e); - return getHTTPResponse(new ApiResponse("1", null, HTTPExceptions.INVALID_PARAMETERS.getCause(e.getClass().getSimpleName(), e.getMessage()))); + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.INVALID_PARAMETERS.getCause(e.getClass().getSimpleName(), + e.getMessage()))); } } - if (method.equals("write")) { - methodParameters.put("data", requestBody); + switch (method) { + case "write": + methodParameters.put("data", requestBody); + break; + case "login": + // userId : password + String[] credentials = new String(requestBody, UTF_8).split(":"); + methodParameters.put("userId", credentials[0]); + methodParameters.put("password", credentials[1]); + break; } - ApiRequest apiRequest = new ApiRequest("1", method, methodParameters, sessionToken, interactiveSessionKey, transactionManagerKey); - Response response = server.processOperation(apiRequest, apiResponseBuilder, performanceAuditor); + + ApiRequest apiRequest = new ApiRequest("1", method, methodParameters, sessionToken, + interactiveSessionKey, transactionManagerKey); + Response response = + server.processOperation(apiRequest, apiResponseBuilder, performanceAuditor); HttpResponse httpResponse = getHTTPResponse(response); performanceAuditor.audit(Event.WriteResponse); logger.traceExit(performanceAuditor); logger.traceExit(httpResponse); return httpResponse; - } catch (APIServerException e) { + } catch (APIServerException e) + { logger.catching(e); - switch (e.getType()) { + switch (e.getType()) + { case MethodNotFound: case IncorrectParameters: case InternalError: - try { + try + { return getHTTPResponse(new ApiResponse("1", null, e.getData())); - } catch (Exception ex) { + } catch (Exception ex) + { logger.catching(ex); } } - } catch (Exception e) { + } catch (Exception e) + { logger.catching(e); - try { - return getHTTPResponse(new ApiResponse("1", null, HTTPExceptions.UNKNOWN.getCause(e.getClass().getSimpleName(), e.getMessage()))); - } catch (Exception ex) { + try + { + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.UNKNOWN.getCause(e.getClass().getSimpleName(), + e.getMessage()))); + } catch (Exception ex) + { logger.catching(ex); } } return null; // This should never happen, it would mean an error writing the Unknown error happened. } - private HttpResponse getHTTPResponse(Response response) throws Exception { + private HttpResponse getHTTPResponse(Response response) throws Exception + { boolean error = response.getError() != null; String contentType = null; byte[] body = null; - if (response.getResult() instanceof byte[]) { + if (response.getResult() instanceof byte[]) + { contentType = HttpResponse.CONTENT_TYPE_BINARY_DATA; body = (byte[]) response.getResult(); - } else { + } else + { contentType = HttpResponse.CONTENT_TYPE_JSON; body = jsonObjectMapper.writeValue(response); } diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/startup/Main.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/startup/Main.java index cde81d249589c1475cf809a2b21772b3cd5a8b4a..aac4b1ffffa4c9e27b6b5a866dbb4a65c8e66ebf 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/startup/Main.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/startup/Main.java @@ -34,7 +34,7 @@ public class Main { System.out.println("Current Working Directory: " + (new File("")).getCanonicalPath()); Configuration configuration = new Configuration(getParameterClasses(), - "../server-data-store/src/main/resources/afs-server-config.properties"); + "../server-data-store/src/main/resources/server-data-store-config.properties"); DummyServerObserver dummyServerObserver = new DummyServerObserver(); Server server = new Server(configuration, dummyServerObserver, dummyServerObserver); Thread.currentThread().join(); diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java index 4ded4e936b36f5ae3809a2bdd50b91f28e74652c..6e075316ec5f3fcb499e851866674cdcf3832ed0 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java @@ -16,7 +16,7 @@ package ch.ethz.sis.afsserver.worker; import ch.ethz.sis.afs.api.TransactionalFileSystem; -import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsserver.server.Worker; import ch.ethz.sis.afsserver.server.performance.PerformanceAuditor; import lombok.NonNull; diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java index 28bf7dcb21c48fce3afa3bfa4d3cd1b155dd6d24..bdcf0eb0e9d15b8729b398443df6cb8ca57a2272 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afsserver.worker.proxy; -import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsserver.worker.AbstractProxy; import ch.ethz.sis.afsserver.server.performance.Event; import lombok.NonNull; diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java index b530233e2a787857a0ebec82b9a7f8551c26f8fe..5a3b033a5cfafec540c133245e4360e1be6cd96e 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afsserver.worker.proxy; -import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsserver.exception.FSExceptions; import ch.ethz.sis.afsserver.worker.AbstractProxy; import ch.ethz.sis.afsserver.worker.providers.AuthenticationInfoProvider; diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java index 9670fc0ca85140751f1bb85525907826a169cd0d..88ba76d7e8960386671813b15952506741f15c2e 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afsserver.worker.proxy; -import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afs.dto.operation.OperationName; import ch.ethz.sis.afsserver.exception.FSExceptions; import ch.ethz.sis.afsserver.worker.AbstractProxy; diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ExecutorProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ExecutorProxy.java index 38a7eed7b82202b5c14ad14b4c87d3ff0ba5e186..ab041bb13c81c85b315347e96461eae5d6235203 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ExecutorProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ExecutorProxy.java @@ -15,12 +15,13 @@ */ package ch.ethz.sis.afsserver.worker.proxy; -import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsserver.worker.AbstractProxy; import ch.ethz.sis.shared.io.IOUtils; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; public class ExecutorProxy extends AbstractProxy { @@ -68,7 +69,15 @@ public class ExecutorProxy extends AbstractProxy { @Override public List<File> list(String owner, String source, Boolean recursively) throws Exception { - return workerContext.getConnection().list(getPath(owner, source), recursively); + return workerContext.getConnection().list(getPath(owner, source), recursively) + .stream() + .map(this::convertToFile) + .collect(Collectors.toList()); + } + + private File convertToFile(ch.ethz.sis.afs.api.dto.File file) { + return new File(file.getPath(), file.getName(), file.getDirectory(), file.getSize(), + file.getLastModifiedTime(), file.getCreationTime(), file.getLastAccessTime()); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java index 38ccfc8374e262ead411d2e084eb956cd674cfc3..0268a104ac162c49d609ba30b901fbc675c3f684 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afsserver.worker.proxy; -import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsserver.worker.AbstractProxy; import ch.ethz.sis.shared.log.LogManager; import ch.ethz.sis.shared.log.Logger; diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java index 21a6b8bc7d339636e630b3eb1970ceedeaaf133b..10fa6b7ca3da3388515d50b2f932d1ec1d011551 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afsserver.worker.proxy; -import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsserver.exception.FSExceptions; import ch.ethz.sis.afsserver.worker.AbstractProxy; diff --git a/server-data-store/src/main/resources/afs-server-config-log4j2.xml b/server-data-store/src/main/resources/log4j2.xml similarity index 100% rename from server-data-store/src/main/resources/afs-server-config-log4j2.xml rename to server-data-store/src/main/resources/log4j2.xml diff --git a/server-data-store/src/main/resources/afs-server-config.properties b/server-data-store/src/main/resources/server-data-store-config.properties similarity index 88% rename from server-data-store/src/main/resources/afs-server-config.properties rename to server-data-store/src/main/resources/server-data-store-config.properties index 138f6513d66cd2999e4b88b4220119acbd332bc4..461e66d29a04fbb952632e9d80c72fa9c8adc0a3 100755 --- a/server-data-store/src/main/resources/afs-server-config.properties +++ b/server-data-store/src/main/resources/server-data-store-config.properties @@ -1,7 +1,7 @@ logFactoryClass=ch.ethz.sis.shared.log.log4j2.Log4J2LogFactory logConfigFile=afs-config-log4j2.xml -jsonObjectMapperClass=ch.ethz.sis.shared.json.jackson.JacksonObjectMapper +jsonObjectMapperClass=ch.ethz.sis.afsjson.jackson.JacksonObjectMapper # Where all the transactions information is written until the prepare step # For performance reasons should be on the save volume as the configured storage writeAheadLogRoot=./target/tests/transactions @@ -18,7 +18,7 @@ authorizationInfoProviderClass=ch.ethz.sis.afsserver.worker.providers.impl.Dummy poolSize=50 connectionFactoryClass=ch.ethz.sis.afsserver.worker.ConnectionFactory workerFactoryClass=ch.ethz.sis.afsserver.worker.WorkerFactory -publicApiInterface=ch.ethz.sis.afsserver.api.PublicAPI +publicApiInterface=ch.ethz.sis.afsapi.api.PublicAPI apiServerInteractiveSessionKey=1234 apiServerTransactionManagerKey=5678 apiServerWorkerTimeout=30000 diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/ApiClientTest.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/ApiClientTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e9065b602411801a27fb50c38a61efa807e413d2 --- /dev/null +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/ApiClientTest.java @@ -0,0 +1,131 @@ +/* + * Copyright ETH 2023 Zürich, Scientific IT Services + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ch.ethz.sis.afsserver; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.*; + +import java.net.URI; +import java.util.List; + +import org.junit.*; + +import ch.ethz.sis.afs.manager.TransactionConnection; +import ch.ethz.sis.afsclient.client.AfsClient; +import ch.ethz.sis.afsserver.server.Server; +import ch.ethz.sis.afsserver.server.observer.impl.DummyServerObserver; +import ch.ethz.sis.afsserver.startup.AtomicFileSystemServerParameter; +import ch.ethz.sis.shared.startup.Configuration; + +public final class ApiClientTest +{ + private static Server<TransactionConnection, ?> afsServer; + + private static AfsClient afsClient; + + private static int httpServerPort; + + private static String httpServerPath; + + @BeforeClass + public static void classSetUp() throws Exception + { + final Configuration configuration = + new Configuration(List.of(AtomicFileSystemServerParameter.class), + "src/test/resources/test-server-config.properties"); + final DummyServerObserver dummyServerObserver = new DummyServerObserver(); + afsServer = new Server<>(configuration, dummyServerObserver, dummyServerObserver); + httpServerPort = + configuration.getIntegerProperty(AtomicFileSystemServerParameter.httpServerPort); + httpServerPath = + configuration.getStringProperty(AtomicFileSystemServerParameter.httpServerUri); + } + + @Before + public void setUp() throws Exception + { + afsClient = new AfsClient( + new URI("http", null, "localhost", httpServerPort, + httpServerPath, null, null)); + } + + private String login() throws Exception + { + return afsClient.login("test", "test"); + } + + @AfterClass + public static void classTearDown() throws Exception + { + afsServer.shutdown(true); + } + + @Test + public void login_sessionTokenIsNotNull() throws Exception + { + final String token = login(); + assertNotNull(token); + } + + @Test + public void isSessionValid_throwsException() throws Exception + { + try + { + afsClient.isSessionValid(); + fail(); + } catch (IllegalStateException e) + { + assertThat(e.getMessage(), containsString("No session information detected!")); + } + } + + @Test + public void isSessionValid_returnsTrue() throws Exception + { + login(); + + final Boolean isValid = afsClient.isSessionValid(); + assertTrue(isValid); + } + + @Test + public void logout_withoutLogin_throwsException() throws Exception + { + try + { + afsClient.logout(); + fail(); + } catch (IllegalStateException e) + { + assertThat(e.getMessage(), containsString("No session information detected!")); + } + } + + @Test + public void logout_withLogin_returnsTrue() throws Exception + { + login(); + + final Boolean result = afsClient.logout(); + + assertTrue(result); + } + +} diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/ServerClientEnvironmentFS.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/ServerClientEnvironmentFS.java index 9807d346b1ff32632c0928e7662c5fb13a88d5d9..441183e65a9f1a1d4093b6b8e8ea53bf5c6265f0 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/ServerClientEnvironmentFS.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/ServerClientEnvironmentFS.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afsserver; -import ch.ethz.sis.afsserver.api.PublicAPI; +import ch.ethz.sis.afsapi.api.PublicAPI; import ch.ethz.sis.afsserver.http.impl.NettyHttpServer; import ch.ethz.sis.afsserver.server.Server; import ch.ethz.sis.afsserver.server.observer.APIServerObserver; @@ -26,7 +26,7 @@ import ch.ethz.sis.afsserver.worker.ConnectionFactory; import ch.ethz.sis.afsserver.worker.WorkerFactory; import ch.ethz.sis.afsserver.worker.providers.impl.DummyAuthenticationInfoProvider; import ch.ethz.sis.afsserver.worker.providers.impl.DummyAuthorizationInfoProvider; -import ch.ethz.sis.shared.json.jackson.JacksonObjectMapper; +import ch.ethz.sis.afsjson.jackson.JacksonObjectMapper; import ch.ethz.sis.shared.log.log4j2.Log4J2LogFactory; import ch.ethz.sis.shared.startup.Configuration; diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/AbstractPublicAPIWrapper.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/AbstractPublicAPIWrapper.java index abdb9540d5ce683571e9053c3a796f6975efc784..68d8338a2d4925e3128f39e9d7516ed47c70d4f4 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/AbstractPublicAPIWrapper.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/AbstractPublicAPIWrapper.java @@ -15,8 +15,8 @@ */ package ch.ethz.sis.afsserver.core; -import ch.ethz.sis.afs.api.dto.File; -import ch.ethz.sis.afsserver.api.PublicAPI; +import ch.ethz.sis.afsapi.dto.File; +import ch.ethz.sis.afsapi.api.PublicAPI; import lombok.NonNull; import java.util.List; diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/PublicApiTest.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/PublicApiTest.java index 3b9b1f7271dedc856b3a94bc96801c5aac763be4..cb7acab3d1335309a2fce86c32e7595aea087280 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/PublicApiTest.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/PublicApiTest.java @@ -15,12 +15,11 @@ */ package ch.ethz.sis.afsserver.core; -import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsserver.AbstractTest; import ch.ethz.sis.afsserver.ServerClientEnvironmentFS; -import ch.ethz.sis.afsserver.api.PublicAPI; +import ch.ethz.sis.afsapi.api.PublicAPI; import ch.ethz.sis.afsserver.startup.AtomicFileSystemServerParameter; -import ch.ethz.sis.shared.exception.ThrowableReason; import ch.ethz.sis.shared.io.IOUtils; import org.junit.After; import org.junit.Before; diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerAdapterWrapper.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerAdapterWrapper.java index 47a3ed56e50c645aa27a7aea6ef441f53b423047..6c6ff9644b4184c00f021f0218a658e015903ef1 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerAdapterWrapper.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerAdapterWrapper.java @@ -23,7 +23,7 @@ import ch.ethz.sis.afsserver.server.impl.ApiResponse; import ch.ethz.sis.afsserver.server.impl.ApiServerAdapter; import ch.ethz.sis.afsserver.server.performance.PerformanceAuditor; import ch.ethz.sis.shared.io.IOUtils; -import ch.ethz.sis.shared.json.JSONObjectMapper; +import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.shared.log.LogManager; import ch.ethz.sis.shared.log.Logger; import io.netty.handler.codec.http.HttpMethod; @@ -40,9 +40,9 @@ public class APIServerAdapterWrapper extends AbstractPublicAPIWrapper { private static final Logger logger = LogManager.getLogger(APIServerAdapterWrapper.class); private ApiServerAdapter apiServerAdapter; - private JSONObjectMapper jsonObjectMapper; + private JsonObjectMapper jsonObjectMapper; - public APIServerAdapterWrapper(ApiServerAdapter apiServerAdapter, JSONObjectMapper jsonObjectMapper) { + public APIServerAdapterWrapper(ApiServerAdapter apiServerAdapter, JsonObjectMapper jsonObjectMapper) { this.apiServerAdapter = apiServerAdapter; this.jsonObjectMapper = jsonObjectMapper; } diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/ApiServerAdapterTest.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/ApiServerAdapterTest.java index feafbd48dd6462343d2ffa919dd1fa43df36e42b..e61913bf65df3447b73cb0669d68c9a09234f642 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/ApiServerAdapterTest.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/ApiServerAdapterTest.java @@ -16,11 +16,11 @@ package ch.ethz.sis.afsserver.impl; import ch.ethz.sis.afsserver.ServerClientEnvironmentFS; -import ch.ethz.sis.afsserver.api.PublicAPI; +import ch.ethz.sis.afsapi.api.PublicAPI; import ch.ethz.sis.afsserver.server.APIServer; import ch.ethz.sis.afsserver.server.impl.ApiServerAdapter; import ch.ethz.sis.afsserver.startup.AtomicFileSystemServerParameter; -import ch.ethz.sis.shared.json.JSONObjectMapper; +import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.shared.startup.Configuration; public class ApiServerAdapterTest extends ApiServerTest { @@ -29,7 +29,7 @@ public class ApiServerAdapterTest extends ApiServerTest { public PublicAPI getPublicAPI() throws Exception { APIServer apiServer = getAPIServer(); Configuration configuration = ServerClientEnvironmentFS.getInstance().getDefaultServerConfiguration(); - JSONObjectMapper jsonObjectMapper = configuration.getSharableInstance(AtomicFileSystemServerParameter.jsonObjectMapperClass); + JsonObjectMapper jsonObjectMapper = configuration.getSharableInstance(AtomicFileSystemServerParameter.jsonObjectMapperClass); ApiServerAdapter apiServerAdapter = new ApiServerAdapter(apiServer, jsonObjectMapper); return new APIServerAdapterWrapper(apiServerAdapter, jsonObjectMapper); } diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/ApiServerTest.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/ApiServerTest.java index 3ea5350afe0ca5c006008601ed1e87280f0522ec..cfe121fae004fa5af61d8db73a47933422c25b64 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/ApiServerTest.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/ApiServerTest.java @@ -17,7 +17,7 @@ package ch.ethz.sis.afsserver.impl; import ch.ethz.sis.afs.manager.TransactionConnection; import ch.ethz.sis.afsserver.ServerClientEnvironmentFS; -import ch.ethz.sis.afsserver.api.PublicAPI; +import ch.ethz.sis.afsapi.api.PublicAPI; import ch.ethz.sis.afsserver.core.PublicApiTest; import ch.ethz.sis.afsserver.server.APIServer; import ch.ethz.sis.afsserver.server.Worker; diff --git a/server-data-store/src/test/resources/test-server-config.properties b/server-data-store/src/test/resources/test-server-config.properties new file mode 100644 index 0000000000000000000000000000000000000000..0b988b4160d4a558b42d4d219c337a9e64b0cb6f --- /dev/null +++ b/server-data-store/src/test/resources/test-server-config.properties @@ -0,0 +1,26 @@ +logFactoryClass=ch.ethz.sis.shared.log.log4j2.Log4J2LogFactory +logConfigFile= + +jsonObjectMapperClass=ch.ethz.sis.afsjson.jackson.JacksonObjectMapper +# Where all the transactions information is written until the prepare step +# For performance reasons should be on the save volume as the configured storage +writeAheadLogRoot=./target/tests/transactions +storageRoot=./target/tests/storage + +httpServerClass=ch.ethz.sis.afsserver.http.impl.NettyHttpServer +httpServerPort=8085 +httpServerUri=/fileserver +httpMaxContentLength=1024 + +maxReadSizeInBytes=1024 +authenticationInfoProviderClass=ch.ethz.sis.afsserver.worker.providers.impl.DummyAuthenticationInfoProvider +authorizationInfoProviderClass=ch.ethz.sis.afsserver.worker.providers.impl.DummyAuthorizationInfoProvider +poolSize=50 +connectionFactoryClass=ch.ethz.sis.afsserver.worker.ConnectionFactory +workerFactoryClass=ch.ethz.sis.afsserver.worker.WorkerFactory +publicApiInterface=ch.ethz.sis.afsapi.api.PublicAPI +apiServerInteractiveSessionKey=1234 +apiServerTransactionManagerKey=5678 +apiServerWorkerTimeout=30000 +openBISUrl= +openBISTimeout=30000 \ No newline at end of file