From 5955368c3aab03d4a58575935e307b5f679bce16 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 12 May 2017 17:25:33 +0200 Subject: [PATCH] Initial commit with Java based grpc server which queries xenon and a Python client --- .gitignore | 42 +++ LICENSE | 202 ++++++++++++++ README.md | 34 +++ build.gradle | 61 +++++ gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++++++++++ gradlew.bat | 84 ++++++ settings.gradle | 18 ++ .../xenon/grpc/XenonJobsImpl.java | 38 +++ .../xenon/grpc/XenonServer.java | 47 ++++ src/main/proto/xenon.proto | 25 ++ src/main/python/client.py | 13 + src/main/python/requirements.txt | 2 + src/main/python/xenon_pb2.py | 255 ++++++++++++++++++ src/main/python/xenon_pb2_grpc.py | 40 +++ 15 files changed, 1039 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/nl/esciencecenter/xenon/grpc/XenonJobsImpl.java create mode 100644 src/main/java/nl/esciencecenter/xenon/grpc/XenonServer.java create mode 100644 src/main/proto/xenon.proto create mode 100644 src/main/python/client.py create mode 100644 src/main/python/requirements.txt create mode 100644 src/main/python/xenon_pb2.py create mode 100644 src/main/python/xenon_pb2_grpc.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..511ef8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# Created by .ignore support plugin (hsz.mobi) +### Gradle template +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties +### Java template +*.class + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# IDE +.idea +*.iml +*.ipr +*.iws + + +__pycache__ +*.pyc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..480526c --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 Netherlands eScience Center + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba0d2a9 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +Experiment to combine Xenon (nlesc.github.io/Xenon/) and gRpc (http://www.grpc.io/). + +# Run server + +```bash +./gradlew installDist +./build/install/xenon-grpc/bin/xenon-grpc +``` + +# Run client + +For use polyglot + +```bash +wget https://github.com/grpc-ecosystem/polyglot/releases/download/v1.2.0/polyglot.jar +java -jar polyglot.jar --command=list_services --proto_discovery_root=src/main/proto +echo {} | java -jar polyglot.jar --endpoint=localhost:50051 --proto_discovery_root=src/main/proto --full_method=xenon.XenonJobs/GetAdaptorStatuses --command=call +``` + +# grpc gateway + +JSON REST api around server with https://github.com/grpc-ecosystem/grpc-gateway + +TODO + +# Python client + +``` +cd src/main/python +pip install -r requirements.txt +# compile proto into python stubs +python -m grpc_tools.protoc -I../proto --python_out=. --grpc_python_out=. ../proto/xenon.proto +python client.py +``` \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..c60e6ba --- /dev/null +++ b/build.gradle @@ -0,0 +1,61 @@ +apply plugin: 'java' +apply plugin: 'com.google.protobuf' +apply plugin: 'application' +apply plugin: 'idea' + +version = '0.0.1' +mainClassName = 'nl.esciencecenter.xenon.grpc.XenonServer' + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0' + } +} + +ext.grpcVersion = '1.3.0' + +repositories { + mavenCentral() + jcenter() +} + +dependencies { + compile "io.grpc:grpc-netty:${grpcVersion}" + compile "io.grpc:grpc-protobuf:${grpcVersion}" + compile "io.grpc:grpc-stub:${grpcVersion}" + compile 'org.slf4j:slf4j-api:1.7.25' + compile 'nl.esciencecenter.xenon:xenon:1.2.2' +} + +protobuf { + protoc { + artifact = 'com.google.protobuf:protoc:3.2.0' + } + plugins { + grpc { + artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" + } + } + generateProtoTasks { + all()*.plugins { + grpc {} + } + } +} + +clean { + delete protobuf.generatedFilesBaseDir +} + +// Inform IntelliJ projects about the generated code. See https://github.com/google/protobuf-gradle-plugin#intellij-idea +idea { + module { + // Not using generatedSourceDirs because of + // https://discuss.gradle.org/t/support-for-intellij-2016/15294/8 + sourceDirs += file("${projectDir}/build/generated/source/proto/main/java") + sourceDirs += file("${projectDir}/build/generated/source/proto/main/grpc") + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9097635 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri May 12 14:04:50 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4453cce --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..987e77d --- /dev/null +++ b/settings.gradle @@ -0,0 +1,18 @@ +/* + * This settings file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * In a single project build this file can be empty or even removed. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user guide at https://docs.gradle.org/3.3/userguide/multi_project_builds.html + */ + +/* +// To declare projects as part of a multi-project build use the 'include' method +include 'shared' +include 'api' +include 'services:webservice' +*/ + +rootProject.name = 'xenon-grpc' diff --git a/src/main/java/nl/esciencecenter/xenon/grpc/XenonJobsImpl.java b/src/main/java/nl/esciencecenter/xenon/grpc/XenonJobsImpl.java new file mode 100644 index 0000000..41a2452 --- /dev/null +++ b/src/main/java/nl/esciencecenter/xenon/grpc/XenonJobsImpl.java @@ -0,0 +1,38 @@ +package nl.esciencecenter.xenon.grpc; + +import io.grpc.stub.StreamObserver; +import nl.esciencecenter.xenon.AdaptorStatus; +import nl.esciencecenter.xenon.Xenon; +import nl.esciencecenter.xenon.XenonException; +import nl.esciencecenter.xenon.XenonFactory; + +import java.util.Arrays; + +public class XenonJobsImpl extends XenonJobsGrpc.XenonJobsImplBase { + @Override + public void getAdaptorStatuses(Empty request, StreamObserver responseObserver) { + AdaptorStatus[] adaptors = getAdaptorStatuses(); + AdaptorStatuses.Builder builder = AdaptorStatuses.newBuilder(); + for (AdaptorStatus adaptor : adaptors) { + builder.addAdaptors( + MAdaptorStatus.newBuilder() + .setName(adaptor.getName()) + .addAllSchemes(Arrays.asList(adaptor.getSupportedSchemes())) + ); + } + responseObserver.onNext(builder.build()); + responseObserver.onCompleted(); + } + + private AdaptorStatus[] getAdaptorStatuses() { + AdaptorStatus[] adaptors = {}; + try { + Xenon xenon = XenonFactory.newXenon(null); + adaptors = xenon.getAdaptorStatuses(); + XenonFactory.endXenon(xenon); + } catch (XenonException e) { + e.printStackTrace(); + } + return adaptors; + } +} diff --git a/src/main/java/nl/esciencecenter/xenon/grpc/XenonServer.java b/src/main/java/nl/esciencecenter/xenon/grpc/XenonServer.java new file mode 100644 index 0000000..a4a466b --- /dev/null +++ b/src/main/java/nl/esciencecenter/xenon/grpc/XenonServer.java @@ -0,0 +1,47 @@ +package nl.esciencecenter.xenon.grpc; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class XenonServer { + private static final Logger LOGGER = LoggerFactory.getLogger(XenonServer.class); + + private Server server; + + public static void main(String[] args) throws InterruptedException, IOException { + final XenonServer server = new XenonServer(); + server.start(); + server.blockUntilShutdown(); + } + + private void blockUntilShutdown() throws InterruptedException { + if (server != null) { + server.awaitTermination(); + } + } + + private void start() throws IOException { + int port = 50051; + server = ServerBuilder.forPort(port) + .addService(new XenonJobsImpl()) + .build() + .start(); + LOGGER.info("Server started, listening on " + port); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + // Use stderr here since the logger may have been reset by its JVM shutdown hook. + System.err.println("*** shutting down gRPC server since JVM is shutting down"); + XenonServer.this.stop(); + System.err.println("*** server shut down"); + })); + } + + private void stop() { + if (server != null) { + server.shutdown(); + } + } +} diff --git a/src/main/proto/xenon.proto b/src/main/proto/xenon.proto new file mode 100644 index 0000000..fbbbfbf --- /dev/null +++ b/src/main/proto/xenon.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "nl.esciencecenter.xenon.grpc"; +option java_outer_classname = "XenonJobsProto"; + +package xenon; + +service XenonJobs { + rpc GetAdaptorStatuses(Empty) returns (AdaptorStatuses) {} +} + +message Empty { + +} + +message MAdaptorStatus { + // The name of the scheduler type eg. slurm + string name = 1; + repeated string schemes = 2; +} + +message AdaptorStatuses { + repeated MAdaptorStatus adaptors = 1; +} \ No newline at end of file diff --git a/src/main/python/client.py b/src/main/python/client.py new file mode 100644 index 0000000..e54e773 --- /dev/null +++ b/src/main/python/client.py @@ -0,0 +1,13 @@ +import grpc +import xenon_pb2 +import xenon_pb2_grpc + + +def run(): + channel = grpc.insecure_channel('localhost:50051') + stub = xenon_pb2_grpc.XenonJobsStub(channel) + response = stub.GetAdaptorStatuses(xenon_pb2.Empty()) + print(response) + +if __name__ == '__main__': + run() \ No newline at end of file diff --git a/src/main/python/requirements.txt b/src/main/python/requirements.txt new file mode 100644 index 0000000..a6646e7 --- /dev/null +++ b/src/main/python/requirements.txt @@ -0,0 +1,2 @@ +grpcio +grpcio-tools diff --git a/src/main/python/xenon_pb2.py b/src/main/python/xenon_pb2.py new file mode 100644 index 0000000..608065b --- /dev/null +++ b/src/main/python/xenon_pb2.py @@ -0,0 +1,255 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: xenon.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='xenon.proto', + package='xenon', + syntax='proto3', + serialized_pb=_b('\n\x0bxenon.proto\x12\x05xenon\"\x07\n\x05\x45mpty\"/\n\x0eMAdaptorStatus\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07schemes\x18\x02 \x03(\t\":\n\x0f\x41\x64\x61ptorStatuses\x12\'\n\x08\x61\x64\x61ptors\x18\x01 \x03(\x0b\x32\x15.xenon.MAdaptorStatus2I\n\tXenonJobs\x12<\n\x12GetAdaptorStatuses\x12\x0c.xenon.Empty\x1a\x16.xenon.AdaptorStatuses\"\x00\x42\x30\n\x1cnl.esciencecenter.xenon.grpcB\x0eXenonJobsProtoP\x01\x62\x06proto3') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_EMPTY = _descriptor.Descriptor( + name='Empty', + full_name='xenon.Empty', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=29, +) + + +_MADAPTORSTATUS = _descriptor.Descriptor( + name='MAdaptorStatus', + full_name='xenon.MAdaptorStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='xenon.MAdaptorStatus.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='schemes', full_name='xenon.MAdaptorStatus.schemes', index=1, + number=2, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=31, + serialized_end=78, +) + + +_ADAPTORSTATUSES = _descriptor.Descriptor( + name='AdaptorStatuses', + full_name='xenon.AdaptorStatuses', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='adaptors', full_name='xenon.AdaptorStatuses.adaptors', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=80, + serialized_end=138, +) + +_ADAPTORSTATUSES.fields_by_name['adaptors'].message_type = _MADAPTORSTATUS +DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY +DESCRIPTOR.message_types_by_name['MAdaptorStatus'] = _MADAPTORSTATUS +DESCRIPTOR.message_types_by_name['AdaptorStatuses'] = _ADAPTORSTATUSES + +Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), dict( + DESCRIPTOR = _EMPTY, + __module__ = 'xenon_pb2' + # @@protoc_insertion_point(class_scope:xenon.Empty) + )) +_sym_db.RegisterMessage(Empty) + +MAdaptorStatus = _reflection.GeneratedProtocolMessageType('MAdaptorStatus', (_message.Message,), dict( + DESCRIPTOR = _MADAPTORSTATUS, + __module__ = 'xenon_pb2' + # @@protoc_insertion_point(class_scope:xenon.MAdaptorStatus) + )) +_sym_db.RegisterMessage(MAdaptorStatus) + +AdaptorStatuses = _reflection.GeneratedProtocolMessageType('AdaptorStatuses', (_message.Message,), dict( + DESCRIPTOR = _ADAPTORSTATUSES, + __module__ = 'xenon_pb2' + # @@protoc_insertion_point(class_scope:xenon.AdaptorStatuses) + )) +_sym_db.RegisterMessage(AdaptorStatuses) + + +DESCRIPTOR.has_options = True +DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\034nl.esciencecenter.xenon.grpcB\016XenonJobsProtoP\001')) +try: + # THESE ELEMENTS WILL BE DEPRECATED. + # Please use the generated *_pb2_grpc.py files instead. + import grpc + from grpc.beta import implementations as beta_implementations + from grpc.beta import interfaces as beta_interfaces + from grpc.framework.common import cardinality + from grpc.framework.interfaces.face import utilities as face_utilities + + + class XenonJobsStub(object): + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GetAdaptorStatuses = channel.unary_unary( + '/xenon.XenonJobs/GetAdaptorStatuses', + request_serializer=Empty.SerializeToString, + response_deserializer=AdaptorStatuses.FromString, + ) + + + class XenonJobsServicer(object): + + def GetAdaptorStatuses(self, request, context): + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + + def add_XenonJobsServicer_to_server(servicer, server): + rpc_method_handlers = { + 'GetAdaptorStatuses': grpc.unary_unary_rpc_method_handler( + servicer.GetAdaptorStatuses, + request_deserializer=Empty.FromString, + response_serializer=AdaptorStatuses.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'xenon.XenonJobs', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + class BetaXenonJobsServicer(object): + """The Beta API is deprecated for 0.15.0 and later. + + It is recommended to use the GA API (classes and functions in this + file not marked beta) for all further purposes. This class was generated + only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0.""" + def GetAdaptorStatuses(self, request, context): + context.code(beta_interfaces.StatusCode.UNIMPLEMENTED) + + + class BetaXenonJobsStub(object): + """The Beta API is deprecated for 0.15.0 and later. + + It is recommended to use the GA API (classes and functions in this + file not marked beta) for all further purposes. This class was generated + only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0.""" + def GetAdaptorStatuses(self, request, timeout, metadata=None, with_call=False, protocol_options=None): + raise NotImplementedError() + GetAdaptorStatuses.future = None + + + def beta_create_XenonJobs_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None): + """The Beta API is deprecated for 0.15.0 and later. + + It is recommended to use the GA API (classes and functions in this + file not marked beta) for all further purposes. This function was + generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0""" + request_deserializers = { + ('xenon.XenonJobs', 'GetAdaptorStatuses'): Empty.FromString, + } + response_serializers = { + ('xenon.XenonJobs', 'GetAdaptorStatuses'): AdaptorStatuses.SerializeToString, + } + method_implementations = { + ('xenon.XenonJobs', 'GetAdaptorStatuses'): face_utilities.unary_unary_inline(servicer.GetAdaptorStatuses), + } + server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout) + return beta_implementations.server(method_implementations, options=server_options) + + + def beta_create_XenonJobs_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None): + """The Beta API is deprecated for 0.15.0 and later. + + It is recommended to use the GA API (classes and functions in this + file not marked beta) for all further purposes. This function was + generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0""" + request_serializers = { + ('xenon.XenonJobs', 'GetAdaptorStatuses'): Empty.SerializeToString, + } + response_deserializers = { + ('xenon.XenonJobs', 'GetAdaptorStatuses'): AdaptorStatuses.FromString, + } + cardinalities = { + 'GetAdaptorStatuses': cardinality.Cardinality.UNARY_UNARY, + } + stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size) + return beta_implementations.dynamic_stub(channel, 'xenon.XenonJobs', cardinalities, options=stub_options) +except ImportError: + pass +# @@protoc_insertion_point(module_scope) diff --git a/src/main/python/xenon_pb2_grpc.py b/src/main/python/xenon_pb2_grpc.py new file mode 100644 index 0000000..6739f72 --- /dev/null +++ b/src/main/python/xenon_pb2_grpc.py @@ -0,0 +1,40 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import xenon_pb2 as xenon__pb2 + + +class XenonJobsStub(object): + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GetAdaptorStatuses = channel.unary_unary( + '/xenon.XenonJobs/GetAdaptorStatuses', + request_serializer=xenon__pb2.Empty.SerializeToString, + response_deserializer=xenon__pb2.AdaptorStatuses.FromString, + ) + + +class XenonJobsServicer(object): + + def GetAdaptorStatuses(self, request, context): + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_XenonJobsServicer_to_server(servicer, server): + rpc_method_handlers = { + 'GetAdaptorStatuses': grpc.unary_unary_rpc_method_handler( + servicer.GetAdaptorStatuses, + request_deserializer=xenon__pb2.Empty.FromString, + response_serializer=xenon__pb2.AdaptorStatuses.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'xenon.XenonJobs', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,))