├── src
├── docker
│ ├── backports.list
│ ├── Dockerfile
│ ├── README.MD
│ └── build.sh
├── distribution
│ ├── kernel.json
│ ├── create.sh
│ └── install-template.sh
├── test
│ └── resources
│ │ ├── kernel.json
│ │ └── logback-test.xml
├── site
│ └── site.xml
└── main
│ ├── resources
│ └── logback.xml
│ └── groovy
│ └── org
│ └── lappsgrid
│ └── jupyter
│ └── groovy
│ ├── handler
│ ├── IHandler.groovy
│ ├── HistoryHandler.groovy
│ ├── AbstractHandler.groovy
│ ├── KernelInfoHandler.groovy
│ ├── CompleteHandler.groovy
│ └── ExecuteHandler.groovy
│ ├── Config.groovy
│ ├── context
│ ├── DefaultGroovyContext.groovy
│ └── GroovyContext.groovy
│ ├── threads
│ ├── StdinThread.groovy
│ ├── AbstractThread.groovy
│ ├── HeartbeatThread.groovy
│ ├── ControlThread.groovy
│ └── ShellThread.groovy
│ ├── msg
│ ├── Header.groovy
│ └── Message.groovy
│ ├── security
│ └── HmacSigner.groovy
│ ├── json
│ └── Serializer.groovy
│ └── GroovyKernel.groovy
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── .gitignore
├── release.sh
├── COPYRIGHT
├── install.sh
├── RELEASE.md
├── README.md
├── mvnw.cmd
├── mvnw
├── pom.xml
└── LICENSE
/src/docker/backports.list:
--------------------------------------------------------------------------------
1 | # jessie backports
2 | deb http://http.debian.net/debian jessie-backports main
3 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lappsgrid-incubator/jupyter-groovy-kernel/HEAD/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip
--------------------------------------------------------------------------------
/src/distribution/kernel.json:
--------------------------------------------------------------------------------
1 | {
2 | "argv": [ "java", "-jar", "__PATH__", "{connection_file}" ],
3 | "display_name": "Groovy",
4 | "language": "groovy",
5 | "env": { "PS1":"groovy>"}
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | *.log
4 | *.tgz
5 | target
6 | VERSION
7 | src/main/java/org/lappsgrid/jupyter/groovy/Version.java
8 | groovy-kernel.properties
9 | jupyter-groovy-kernel
10 | src/docker/*.jar
11 | src/docker/kernel.json
12 |
--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ -z "$PGP_PASSPHRASE" ] ; then
4 | source ~/.passphrases
5 | fi
6 |
7 | mvn clean
8 | mvn -Dgpg.passphrase="$PGP_PASSPHRASE" package source:jar deploy
9 |
10 | if [ "$1" = "-s" -o "$1" = "--site" ] ; then
11 | mvn site
12 | fi
13 |
--------------------------------------------------------------------------------
/src/test/resources/kernel.json:
--------------------------------------------------------------------------------
1 | {
2 | "stdin_port": 48691,
3 | "ip": "127.0.0.1",
4 | "control_port": 44808,
5 | "hb_port": 49691,
6 | "signature_scheme": "hmac-sha256",
7 | "key": "",
8 | "shell_port": 40544,
9 | "transport": "tcp",
10 | "iopub_port": 43462
11 | }
--------------------------------------------------------------------------------
/src/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM jupyter/base-notebook
2 |
3 | ARG VERSION
4 | ENV KERNEL_DIR /opt/groovy
5 | ENV JAR jupyter-groovy-kernel-${VERSION}.jar
6 |
7 | USER root
8 | ADD backports.list /etc/apt/sources.list.d/backports.list
9 | RUN apt-get update && apt-get install -y openjdk-8-jdk
10 |
11 | RUN mkdir -p $KERNEL_DIR
12 | COPY $JAR $KERNEL_DIR/$JAR
13 | COPY kernel.json $KERNEL_DIR/kernel.json
14 | RUN jupyter kernelspec install --name groovy $KERNEL_DIR
15 |
16 | VOLUME ["/home/jovyan/work"]
17 | WORKDIR /home/jovyan/work
18 | CMD ["jupyter", "notebook"]
19 |
--------------------------------------------------------------------------------
/COPYRIGHT:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
--------------------------------------------------------------------------------
/src/distribution/create.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | TARGET=../../target
4 | NAME=jupyter-groovy-kernel
5 | VERSION=`cat ../../VERSION`
6 | JAR=$NAME-$VERSION.jar
7 |
8 | DIST=$TARGET/$NAME-$VERSION
9 |
10 | if [ ! -e $DIST ] ; then
11 | mkdir $DIST
12 | fi
13 |
14 | cp $TARGET/$JAR $DIST
15 | cp kernel.json $DIST
16 | cat install-template.sh | sed "s/__VERSION__/$VERSION/" > $DIST/install.sh
17 | chmod ug+x $DIST/install.sh
18 |
19 | cd $TARGET
20 | echo "Creating tgz file."
21 | tar czf $NAME-$VERSION.tgz $NAME-$VERSION
22 | echo "Uploading $NAME-$VERSION.tgz"
23 | scp -P 22022 $NAME-$VERSION.tgz suderman@anc.org:/usr/share/wp-lapplanders/downloads
24 | echo "Uploading $NAME-latest.tgz"
25 | scp -P 22022 $NAME-$VERSION.tgz suderman@anc.org:/usr/share/wp-lapplanders/downloads/$NAME-latest.tgz
26 |
27 | echo "Done"
28 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 | %date %-5level [%class{0}.%M:%line] %msg%n
11 |
12 |
13 |
14 |
15 | %date %-5level [%class{0}.%M:%line] %msg%n
16 |
17 | true
18 | change-me.log
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/site/site.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 | ${project.name}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 | %date %-5level %-10logger{0} [%class{0}.%M:%line] %msg%n
11 |
12 |
13 |
14 |
15 | %date %-5level [%class{0}.%M:%line] %msg%n
16 |
17 | true
18 | jupyter-groovy-kernel.log
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/handler/IHandler.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.handler
19 |
20 | import org.lappsgrid.jupyter.groovy.msg.Message
21 |
22 | /**
23 | * Defines objects that can respond to Jupyter messages. There will be one
24 | * handler for each ZMQ.Socket.
25 | *
26 | * @author Keith Suderman
27 | */
28 | interface IHandler {
29 | void handle(Message message)
30 | }
--------------------------------------------------------------------------------
/src/distribution/install-template.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | VERSION=__VERSION__
5 |
6 | user=
7 |
8 | while [ -n $1 ] ; do
9 | case $1 in
10 | -u|--user)
11 | user=--user
12 | ;;
13 | *)
14 | KERNEL_DIR=$1
15 | ;;
16 | esac
17 | shift
18 | done
19 |
20 | if [ -z $KERNEL_DIR ] ; then
21 | echo "Please specifiy an installation directory."
22 | exit 1
23 | fi
24 |
25 | set -u
26 | KERNEL_DIR=$1
27 |
28 | if [ ! -e $KERNEL_DIR ] ; then
29 | read -p "The directory $KERNEL_DIR does not exist. Woud you like to create it? [Y/n] " response
30 | if [ "$response" = "n" ] || [ "$response" = "N" ] ; then
31 | echo "Aborting."
32 | exit 1
33 | fi
34 | mkdir -p $KERNEL_DIR
35 | fi
36 |
37 | JAR=jupyter-groovy-kernel-$VERSION.jar
38 |
39 | echo "Installing the Groovy kernel to $KERNEL_DIR"
40 | cp $JAR $KERNEL_DIR
41 | cat kernel.json | sed "s|__PATH__|$KERNEL_DIR/$JAR|" > $KERNEL_DIR/kernel.json
42 |
43 | jupyter kernelspec install --replace $user --name groovy $KERNEL_DIR
44 |
45 | echo "Done."
46 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | if [ -z $1 ] ; then
5 | echo "Please specifiy an installation directory."
6 | exit 1
7 | fi
8 |
9 | set -u
10 | KERNEL_DIR=$1
11 |
12 | if [ ! -e $KERNEL_DIR ] ; then
13 | read -p "The directory $KERNEL_DIR does not exist. Woud you like to create it? [Y/n] " response
14 | if [ "$response" = "n" ] || [ "$response" = "N" ] ; then
15 | echo "Aborting."
16 | exit 1
17 | fi
18 | mkdir -p $KERNEL_DIR
19 | fi
20 |
21 | # The name of the distribution directory to create.
22 | NAME=jupyter-groovy-kernel
23 |
24 | VERSION=`cat VERSION`
25 | JAR=$NAME-$VERSION.jar
26 | DIST=target/groovy
27 |
28 | if [ ! -e $DIST ] ; then
29 | mkdir -p $DIST
30 | fi
31 |
32 | cp target/$JAR $KERNEL_DIR
33 | cat src/distribution/kernel.json | sed "s|__PATH__|$KERNEL_DIR/$JAR|" > $DIST/kernel.json
34 |
35 | echo "Installing the Groovy kernel to $KERNEL_DIR"
36 | if [ $(whoami) = root ]; then
37 | jupyter kernelspec install --replace --name groovy $DIST
38 | else
39 | jupyter kernelspec install --replace --user --name groovy $DIST
40 | fi
41 |
42 | echo "Done."
43 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/handler/HistoryHandler.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.handler
19 |
20 | import org.lappsgrid.jupyter.groovy.GroovyKernel
21 | import org.lappsgrid.jupyter.groovy.msg.Message
22 |
23 | /**
24 | * Does nothing right now.
25 | *
26 | * @author Keith Suderman
27 | */
28 | class HistoryHandler extends AbstractHandler {
29 | public HistoryHandler(GroovyKernel kernel) {
30 | super(kernel)
31 | }
32 |
33 | @Override
34 | void handle(Message message) {
35 | //TODO Handle history messages.
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/docker/README.MD:
--------------------------------------------------------------------------------
1 | # Building A Docker Image
2 |
3 | ## TL;DR
4 |
5 | ```bash
6 | ./build.sh
7 | ```
8 | -or-
9 |
10 | ```bash
11 | docker build --build-arg VERSION=x.y.z -t lappsgrid/jupyter-groovy-kernel .
12 | ```
13 |
14 | Where *x.y.z* is the version of the jar file that will be included in the Docker image. For example, if the jar is named `jupypter-groovy-kernel-1.1.1.jar` run
15 |
16 | ```bash
17 | docker build --build-arg VERSION=1.1.1 -t lappsgrid/jupyter-groovy-kernel .
18 | ```
19 |
20 |
21 | ## The Long Version
22 |
23 | Since Docker copies the entire contents of the current directory to the Docker daemon when performing a build the files requied by Docker are kept in a separate src directory. The *build.sh* script copies the most recent jar and kernel.json files to the current directory and sets the *VERSION* variable before running the Docker build.
24 |
25 | ## Running The Image
26 |
27 | ```bash
28 | docker run -p 8888:8888 lappsgrid/jupyter-groovy-kernel
29 | ```
30 |
31 | If you would like to save your notebooks outside of the Docker container you will need to mount a directory as `/home/jovyan/work`.
32 |
33 | ```bash
34 | LOCAL_DIR=/path/to/your/notebooks
35 | docker run -p 8888:8888 -v $LOCAL_DIR:/home/jovyan/work lappsgrid/jupyter-groovy-kernel
36 | ```
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/Config.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy
19 |
20 | import com.fasterxml.jackson.annotation.JsonProperty
21 |
22 | /**
23 | * The Config class is used to parse the connection information that is passed to the
24 | * kernel by Jupyter.
25 | *
26 | * @author Keith Suderman
27 | */
28 | class Config {
29 | String transport
30 | String key
31 | @JsonProperty('ip')
32 | String host
33 | @JsonProperty('signature_scheme')
34 | String scheme
35 | @JsonProperty('kernel_name')
36 | String name
37 | @JsonProperty('control_port')
38 | int control
39 | @JsonProperty('shell_port')
40 | int shell
41 | @JsonProperty('stdin_port')
42 | int stdin
43 | @JsonProperty('hb_port')
44 | int heartbeat
45 | @JsonProperty('iopub_port')
46 | int iopub
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/context/DefaultGroovyContext.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.context
19 |
20 | import org.codehaus.groovy.control.CompilerConfiguration
21 |
22 | /**
23 | * The DefaultGroovyContext implements the GroovyContext interface and returns default
24 | * CompilerConfiguration and ExpandoMetaClass objects.
25 | *
26 | * Users implementing a Jupyter kernel for a custom Groovy DSL can extend the DefaultGroovyContext
27 | * if they only need/want to implement one of the methods.
28 | *
29 | * @author Keith Suderman
30 | */
31 | class DefaultGroovyContext implements GroovyContext {
32 | @Override
33 | CompilerConfiguration getCompilerConfiguration() {
34 | return new CompilerConfiguration()
35 | }
36 |
37 | @Override
38 | MetaClass getMetaClass(Class aClass, boolean initialize) {
39 | MetaClass mc = new ExpandoMetaClass(aClass, false)
40 | if (initialize) {
41 | mc.initialize()
42 | }
43 | return mc
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/threads/StdinThread.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.threads
19 |
20 | import org.slf4j.Logger
21 | import org.lappsgrid.jupyter.groovy.GroovyKernel
22 | import org.slf4j.LoggerFactory
23 | import org.zeromq.ZMQ
24 |
25 | /**
26 | * This class is not used and will be removed.
27 | *
28 | * @author Keith Suderman
29 | */
30 | class StdinThread extends AbstractThread {
31 | // public static final Logger logger = LoggerFactory.getLogger(StdinThread)
32 |
33 | boolean enabled = false
34 |
35 | public StdinThread(ZMQ.Socket socket, GroovyKernel kernel) {
36 | super(socket, kernel, org.lappsgrid.jupyter.groovy.threads.StdinThread.class)
37 | }
38 |
39 | // TODO: Not sure what to do with this yet.
40 | void run() {
41 | while (running) {
42 | byte[] buffer = socket.recv()
43 | logger.info("Stdin: {}", new String(buffer))
44 | }
45 | logger.info("StdinThread shutdown.")
46 | }
47 |
48 | void read(String prompt) {
49 |
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/handler/AbstractHandler.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.handler
19 |
20 | import org.lappsgrid.jupyter.groovy.GroovyKernel
21 | import org.lappsgrid.jupyter.groovy.msg.Message
22 | import org.slf4j.Logger
23 | import org.zeromq.ZMQ
24 |
25 | /**
26 | * The AbstractHandler class is the base class for all the other socket handlers
27 | * and provides some default helper methods.
28 | *
29 | * @author Keith Suderman
30 | */
31 | abstract class AbstractHandler implements IHandler {
32 | protected Logger logger
33 | protected GroovyKernel kernel
34 |
35 | public AbstractHandler(GroovyKernel kernel) {
36 | this.kernel = kernel
37 | }
38 |
39 | /** Sends to the shell socket by default. */
40 | void send(Message message) {
41 | kernel.send(message)
42 | }
43 |
44 | /** Sends a message to the specified socket. */
45 | void send(ZMQ.Socket socket, Message message) {
46 | kernel.send(socket, message)
47 | }
48 |
49 | /** Sends the message to the IOPub socket. */
50 | void publish(Message message) {
51 | kernel.publish(message)
52 | }
53 | }
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/threads/AbstractThread.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.threads
19 |
20 | import org.lappsgrid.jupyter.groovy.GroovyKernel
21 | import org.lappsgrid.jupyter.groovy.msg.Message
22 | import org.slf4j.Logger
23 | import org.slf4j.LoggerFactory
24 | import org.zeromq.ZMQ
25 |
26 | /**
27 | * @author Keith Suderman
28 | */
29 | abstract class AbstractThread extends Thread {
30 | boolean running = false
31 | ZMQ.Socket socket
32 | GroovyKernel kernel
33 | final Logger logger
34 |
35 | public AbstractThread(ZMQ.Socket socket, GroovyKernel kernel, Class theClass) {
36 | this.socket = socket
37 | this.kernel = kernel
38 | this.logger = LoggerFactory.getLogger(theClass)
39 | }
40 |
41 | void start() {
42 | logger.info('Thread starting.')
43 | running = true
44 | super.start()
45 | }
46 |
47 | void halt() {
48 | logger.info("Thread halting.")
49 | running = false
50 | }
51 |
52 | Message readMessage() {
53 | return kernel.readMessage(socket)
54 | }
55 |
56 | void send(Message message) {
57 | kernel.send(socket, message)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/handler/KernelInfoHandler.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.handler
19 |
20 | import org.lappsgrid.jupyter.groovy.GroovyKernel
21 | import org.lappsgrid.jupyter.groovy.msg.Header
22 | import org.lappsgrid.jupyter.groovy.msg.Message
23 | import org.slf4j.LoggerFactory
24 |
25 | import static org.lappsgrid.jupyter.groovy.msg.Message.Type.KERNEL_INFO_REPLY
26 |
27 | /**
28 | * Provides Jupyter with information about this kernel.
29 | *
30 | * TODO: For some reason Jupyter always complains about timeouts
31 | * while waiting for the kernel_info_reply message...
32 | *
33 | * @author Keith Suderman
34 | */
35 | class KernelInfoHandler extends AbstractHandler {
36 |
37 | public KernelInfoHandler(GroovyKernel kernel) {
38 | super(kernel)
39 | logger = LoggerFactory.getLogger(KernelInfoHandler)
40 | }
41 |
42 | void handle(Message message) {
43 | logger.info("Processing kernel info request")
44 | Message reply = new Message()
45 | reply.content = kernel.info()
46 | reply.header = new Header(KERNEL_INFO_REPLY, message.header.session)
47 | reply.parentHeader = message.header
48 | reply.identities = message.identities
49 | logger.debug("Sending kernel info reply.")
50 | logger.debug(reply.asJson())
51 | send(reply)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/context/GroovyContext.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.context
19 |
20 | import org.codehaus.groovy.control.CompilerConfiguration
21 | import org.lappsgrid.jupyter.groovy.GroovyKernel
22 |
23 | /**
24 | * Objects that implement the GroovyContext interface are used to obtain CompilerConfiguration
25 | * and MetaClass object used by the Groovy compiler when compiler user scripts.
26 | *
27 | * @author Keith Suderman
28 | */
29 | interface GroovyContext {
30 | /**
31 | * Obtain a CompilerConfiguration object the Groovy compiler will use when compiling
32 | * user code.
33 | * @return an initialized CompilerConfiguration object.
34 | */
35 | CompilerConfiguration getCompilerConfiguration();
36 |
37 | /**
38 | * Obtain the MetaClass for compiled user code.
39 | *
40 | * This MetaClass will be used by the Script object obtained by compiling user code.
41 | * User code will be compiled into a Script object by the Groovy compiler, the
42 | * script will have its metaClass field set, and thenrun is called.
43 | *
44 | * @param theClass the class the MetaClass applies to.
45 | * @param initialize if the MetaClass should be initialized.
46 | * @return
47 | */
48 | MetaClass getMetaClass(Class theClass, boolean initialize);
49 | }
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/threads/HeartbeatThread.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.threads
19 |
20 | import org.lappsgrid.jupyter.groovy.GroovyKernel
21 | import org.slf4j.Logger
22 | import org.slf4j.LoggerFactory
23 | import org.zeromq.ZMQ
24 |
25 | /**
26 | * @author Keith Suderman
27 | */
28 | class HeartbeatThread extends AbstractThread {
29 |
30 | HeartbeatThread(ZMQ.Socket socket, GroovyKernel kernel) {
31 | super(socket, kernel, org.lappsgrid.jupyter.groovy.threads.HeartbeatThread.class)
32 | }
33 |
34 | void run() {
35 | logger.info("Heartbeat thread starting.")
36 | ZMQ.Poller poller = new ZMQ.Poller(1)
37 | poller.register(socket, ZMQ.Poller.POLLIN)
38 | while (running) {
39 | if (poller.poll(0)) {
40 | try {
41 | byte[] buffer = socket.recv(0)
42 | socket.send(buffer)
43 | }
44 | catch (Throwable t) {
45 | // This likley means socket.close() has been called due to the kernel
46 | // receiving a SHUTDOWN message from the notebook.
47 | logger.warn("Error handling heartbeat socket. {}", t)
48 | }
49 | }
50 | else {
51 | sleep(1000) { true }
52 | }
53 | }
54 | logger.info("HearbeatThread shutdown.")
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/threads/ControlThread.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.threads
19 |
20 | import org.lappsgrid.jupyter.groovy.GroovyKernel
21 | import org.lappsgrid.jupyter.groovy.msg.Header
22 | import org.lappsgrid.jupyter.groovy.msg.Message
23 | import org.slf4j.Logger
24 | import org.slf4j.LoggerFactory
25 | import org.zeromq.ZMQ
26 |
27 | import static org.lappsgrid.jupyter.groovy.msg.Message.Type.SHUTDOWN_REPLY
28 | import static org.lappsgrid.jupyter.groovy.msg.Message.Type.SHUTDOWN_REQUEST
29 |
30 | /**
31 | * @author Keith Suderman
32 | */
33 | class ControlThread extends AbstractThread {
34 |
35 | public ControlThread(ZMQ.Socket socket, GroovyKernel kernel) {
36 | super(socket, kernel, ControlThread.class)
37 | }
38 |
39 | void run() {
40 | logger.info("ControlThread starting.")
41 | while (running) {
42 | Message message = readMessage()
43 | String type = message.header.type
44 | if (type == SHUTDOWN_REQUEST) {
45 | logger.info("Control handler received a shutdown request")
46 | running = false
47 | Message reply = new Message()
48 | reply.header = new Header(SHUTDOWN_REPLY, message)
49 | reply.parentHeader = message.header
50 | reply.content = message.content
51 | kernel.shutdown()
52 | send(reply)
53 | }
54 | else {
55 | logger.warn("Unhandled control message: {}", type)
56 | }
57 | }
58 | logger.info("ControlThread shutdown.")
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/threads/ShellThread.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.threads
19 |
20 | import org.lappsgrid.jupyter.groovy.GroovyKernel
21 | import org.lappsgrid.jupyter.groovy.handler.IHandler
22 | import org.lappsgrid.jupyter.groovy.msg.Message
23 | import org.slf4j.Logger
24 | import org.slf4j.LoggerFactory
25 | import org.zeromq.ZMQ
26 |
27 | /**
28 | * @author Keith Suderman
29 | */
30 | class ShellThread extends AbstractThread {
31 | ShellThread(ZMQ.Socket socket, GroovyKernel kernel) {
32 | super(socket, kernel, ShellThread)
33 | }
34 |
35 | public void run() {
36 | logger.info('ShellThread starting.')
37 | ZMQ.Poller poller = new ZMQ.Poller(1)
38 | poller.register(socket, ZMQ.Poller.POLLIN)
39 | while (running) {
40 | try {
41 | if (poller.poll(0)) {
42 | Message message = readMessage()
43 | IHandler handler = kernel.getHandler(message.type())
44 | if (handler) {
45 | logger.debug("Handling message type {}", message.type())
46 | handler.handle(message)
47 | }
48 | else {
49 | logger.warn("Unhandled message type: {}", message.type())
50 | }
51 | }
52 | else {
53 | logger.trace("zzzz")
54 | sleep(1000) {
55 | true
56 | }
57 | }
58 | }
59 | catch (Throwable t) {
60 | running = false
61 | logger.warn("Exception in ShellThread.", t)
62 | }
63 | }
64 | logger.info("ShellThread shutdown.")
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/msg/Header.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.msg
19 |
20 | import com.fasterxml.jackson.annotation.JsonProperty
21 | import com.fasterxml.jackson.annotation.JsonPropertyOrder
22 | import org.lappsgrid.jupyter.groovy.GroovyKernel
23 | import org.lappsgrid.jupyter.groovy.json.Serializer
24 |
25 | /**
26 | * The ZMQ header included with each message.
27 | *
28 | * @author Keith Suderman
29 | */
30 | @JsonPropertyOrder(['id', 'username', 'session', 'date', 'type', 'version'])
31 | class Header {
32 | String date
33 | @JsonProperty('msg_id')
34 | String id
35 | String username
36 | String session
37 | @JsonProperty('msg_type')
38 | String type
39 | String version
40 |
41 | public Header() { }
42 | public Header(Header header) {
43 | this.id = header.id
44 | this.date = header.date
45 | this.username = header.username
46 | this.session = header.session
47 | this.type = header.type
48 | this.version = header.version
49 | }
50 |
51 | public Header(Map header) {
52 | this.id = header.id
53 | this.date = header.date
54 | this.username = header.username
55 | this.session = header.session
56 | this.type = header.type
57 | this.version = header.verion ?: '5.0'
58 | }
59 | @Deprecated
60 | public Header(String type, Message message) {
61 | this(type, message.header.session)
62 | }
63 |
64 | public Header(String type, String session) {
65 | date = GroovyKernel.timestamp()
66 | id = GroovyKernel.uuid()
67 | username = 'kernel'
68 | this.type = type
69 | this.session = session
70 | this.version = '5.0'
71 | }
72 |
73 | String asJson() { return Serializer.toJson(this) }
74 | String prettyPrint() { return Serializer.toPrettyJson(this) }
75 | }
76 |
--------------------------------------------------------------------------------
/src/docker/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -e
3 |
4 | # This script is used to ensure the most recent jar file and kernel.json file
5 | # are copied into the working directory before running the docker build.
6 |
7 | KERNEL_NAME=jupyter-groovy-kernel
8 | KERNEL_GROUP=lappsgrid
9 |
10 | # Parse the latest VERSION. The VERSION file is generated by Maven during the build.
11 | VERSION=`cat ../../VERSION`
12 |
13 | # The name of the jar file generated by Maven. The jar file name just happens to be
14 | # the same as the default kernel name, but this can not be relied upon so we can not
15 | # use $KERNEL_NAME here.
16 | jar=jupyter-groovy-kernel-$VERSION.jar
17 |
18 | # Where the latest jar file can be located
19 | targetjar=../../target/$jar
20 |
21 | function bold {
22 | echo -e "\033[1m\033[97m$@\033[0m"
23 | }
24 |
25 | function usage {
26 | echo
27 | echo "$(bold ./build.sh) - A Bash script for generating the Docker image with the"
28 | echo "Jupyter Groovy kernel pre-installed."
29 | echo
30 | bold "USAGE"
31 | echo " ./build [-n|--name ] [-g|--group ]"
32 | echo
33 | bold "OPTIONS"
34 | echo " -n | --name"
35 | echo " Specifies the kernel name. Default is jupyter-groovy-kernel."
36 | echo " -g | --group"
37 | echo " Specifies the kernel group. Default is lappsgrid."
38 | echo
39 | bold "EXAMPLE"
40 | echo " $> ./build.sh --name igroovy --group example"
41 | echo " $> docker run -p 8888:8888 example/igroovy"
42 | echo
43 | }
44 |
45 | args=
46 | while [ -n "$1" ] ; do
47 | case $1 in
48 | -n|--name)
49 | KERNEL_NAME=$2
50 | shift 2
51 | ;;
52 | -g|--group)
53 | KERNEL_GROUP=$2
54 | shift 2
55 | ;;
56 | -nc|--no-cache)
57 | args=--no-cache
58 | shift
59 | ;;
60 | -h|--help)
61 | usage
62 | exit 1
63 | ;;
64 | *)
65 | echo "Unknown option $1"
66 | usage
67 | exit 1
68 | ;;
69 | esac
70 | done
71 |
72 | set -u
73 |
74 | if [ ! -e $targetjar ] ; then
75 | echo "JAR file not found. Building it now."
76 | cd ../../
77 | mvn clean package
78 | # We need to parse the VERSION file again.
79 | VERSION=`cat VERSION`
80 | cd -
81 | fi
82 |
83 | # The location of the kernel.json template.
84 | kernel=../../src/distribution/kernel.json
85 |
86 | if [ ! -e $jar ] || [ $targetjar -nt $jar ] ; then
87 | echo "Copying $targetjar"
88 | cp $targetjar .
89 | fi
90 |
91 | if [ ! -e kernel.json ] || [ $kernel -nt kernel.json ] ; then
92 | echo "Patching the kernel.json file"
93 | cat $kernel | sed "s|__PATH__|/opt/groovy/$jar|" > kernel.json
94 | fi
95 |
96 | echo "Building the Docker container $KERNEL_GROUP/$KERNEL_NAME with JAR file $jar"
97 | docker build $args --build-arg VERSION=$VERSION -t $KERNEL_GROUP/$KERNEL_NAME .
98 |
99 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/security/HmacSigner.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.security
19 |
20 | import org.apache.commons.codec.binary.Hex
21 | import org.slf4j.Logger
22 | import org.slf4j.LoggerFactory
23 |
24 | import javax.crypto.Mac
25 | import javax.crypto.spec.SecretKeySpec
26 | import java.security.InvalidKeyException
27 |
28 | /**
29 | * Generates HMACs (Hashed Message Authentication Codes) for
30 | * messages exchanged with Jupyter. Jupyter supplies us with
31 | * the signing key in the connection information file that is
32 | * passed in as a parameter when a kernel is started.
33 | *
34 | * @author Keith Suderman
35 | */
36 | class HmacSigner {
37 | private static Logger logger = LoggerFactory.getLogger(HmacSigner)
38 |
39 | private static final String TYPE = "HmacSHA256"
40 | private SecretKeySpec spec
41 |
42 | public HmacSigner(String key) {
43 | if (key == null) {
44 | throw new NullPointerException("No hmac specified.")
45 | }
46 |
47 | logger.info("Using signing hmac: {}", key)
48 | spec = new SecretKeySpec(key.bytes, TYPE)
49 | }
50 |
51 | String sign(List msg) {
52 | try {
53 | Mac mac = Mac.getInstance(TYPE)
54 | mac.init(spec)
55 | msg.each {
56 | mac.update(it.bytes)
57 | }
58 | byte[] digest = mac.doFinal()
59 | return asHex(digest)
60 | } catch (InvalidKeyException e) {
61 | logger.error("Unable to sign message", e)
62 | throw new RuntimeException("Invalid hmac exception while converting to HmacSHA256")
63 | }
64 | }
65 |
66 | String signBytes(List msg) {
67 | try {
68 | Mac mac = Mac.getInstance("HmacSHA256")
69 | mac.init(spec)
70 | msg.each {
71 | mac.update(it)
72 | }
73 | byte[] digest = mac.doFinal()
74 | return asHex(digest)
75 | } catch (InvalidKeyException e) {
76 | logger.error("Unable to sign message", e)
77 | throw new RuntimeException("Invalid hmac exception while converting to HmacSHA256")
78 | }
79 | }
80 |
81 | String asHex(byte[] buffer) {
82 | return Hex.encodeHexString(buffer)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/msg/Message.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.msg
19 |
20 | import com.fasterxml.jackson.annotation.JsonProperty
21 | import com.fasterxml.jackson.annotation.JsonPropertyOrder
22 | import org.lappsgrid.jupyter.groovy.GroovyKernel
23 | import org.lappsgrid.jupyter.groovy.json.Serializer
24 |
25 | /**
26 | * Defines the wire protocol used for messages exchanged with Jupyter.
27 | *
28 | * @author Keith Suderman
29 | */
30 | @JsonPropertyOrder(['identities', 'header', 'parentHeader', 'metadata', 'content'])
31 | class Message {
32 | /**
33 | * Definitions of the strings used as message type values.
34 | */
35 | public static class Type {
36 | // Shell messages.
37 | public static final String KERNEL_INFO_REQUEST = 'kernel_info_request'
38 | public static final String KERNEL_INFO_REPLY = 'kernel_info_reply'
39 | public static final String EXECUTE_REQUEST = 'execute_request'
40 | public static final String EXECUTE_INPUT = 'execute_input'
41 | public static final String EXECUTE_RESULT = 'execute_result'
42 | public static final String EXECUTE_REPLY = 'execute_reply'
43 | public static final String COMPLETE_REQUEST = 'is_complete_request'
44 | public static final String COMPLETE_REPLY = 'is_complete_reply'
45 | public static final String HISTORY_REQUEST = 'history_request'
46 | public static final String HISTORY_REPLY = 'history_reply'
47 | public static final String STATUS = 'status'
48 | public static final String STREAM = 'stream'
49 |
50 | // Stdin messages
51 | public static final String STDIN_REQUEST = "input_request"
52 | public static final String STDIN_REPLY = "input_reply"
53 |
54 | // Control messages
55 | public static final String SHUTDOWN_REQUEST = 'shutdown_request'
56 | public static final String SHUTDOWN_REPLY = 'shutdown_reply'
57 |
58 | // Misc
59 | public static final String UNDEFINED = 'undefined'
60 | }
61 |
62 | List identities = []
63 | Header header
64 | @JsonProperty('parent_header')
65 | Header parentHeader
66 | Map metadata
67 | Map content
68 |
69 | public Message(String type) {
70 | header = new Header()
71 | header.type = type
72 | header.date = GroovyKernel.timestamp()
73 | }
74 | String type() { return header.type }
75 | String asJson() { return Serializer.toJson(this) }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/json/Serializer.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.json
19 |
20 | import com.fasterxml.jackson.annotation.JsonInclude
21 | import com.fasterxml.jackson.databind.ObjectMapper
22 | import com.fasterxml.jackson.databind.SerializationFeature
23 |
24 | /**
25 | * The Serializer class is a very thin wrapper around the Jackson ObjectMapper class.
26 | *
27 | * The Jackson ObjectMapper class is thread-safe as long as it is initialized in a
28 | * single thread and never modified. Once initialized static instances can be used
29 | * by multiple threads without further synchronization.
30 | *
31 | * The Serializer provides two static instances, one instance that pretty prints JSON and one
32 | * instance that removes whitespace.
33 | *
34 | * @author Keith Suderman
35 | */
36 | class Serializer {
37 | private static ObjectMapper mapper;
38 | private static ObjectMapper prettyPrinter;
39 |
40 | static {
41 | mapper = new ObjectMapper()
42 | mapper.disable(SerializationFeature.INDENT_OUTPUT)
43 | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
44 | prettyPrinter = new ObjectMapper()
45 | prettyPrinter.enable(SerializationFeature.INDENT_OUTPUT)
46 | prettyPrinter.setSerializationInclusion(JsonInclude.Include.NON_NULL)
47 | }
48 | private Serializer() {}
49 |
50 | /**
51 | * Parses a JSON string and creates an instance of the specified class.
52 | */
53 | public static T parse(String json, Class theClass) {
54 | T result = null
55 | try {
56 | result = (T) mapper.readValue(json, theClass)
57 | }
58 | catch(Exception e)
59 | {
60 | // Ignored. We return null to indicate an error.
61 | }
62 | return result;
63 | }
64 |
65 | /**
66 | * Returns a JSON representation of the object.
67 | */
68 | public static String toJson(Object object)
69 | {
70 | try {
71 | return mapper.writeValueAsString(object)
72 | }
73 | catch (Exception e)
74 | {
75 | return null;
76 | }
77 | }
78 |
79 | /** Returns a pretty-printed JSON representation of the object. */
80 | public static String toPrettyJson(Object object)
81 | {
82 | try {
83 | return prettyPrinter.writeValueAsString(object)
84 | }
85 | catch (Exception e)
86 | {
87 | return null;
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/handler/CompleteHandler.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.handler
19 |
20 | import org.codehaus.groovy.control.CompilationFailedException
21 | import org.lappsgrid.jupyter.groovy.GroovyKernel
22 | import org.lappsgrid.jupyter.groovy.msg.Header
23 | import org.lappsgrid.jupyter.groovy.msg.Message
24 | import org.slf4j.LoggerFactory
25 |
26 | import static Message.Type.*
27 |
28 | /**
29 | * The code completion handler. The CompleteHandler is called by Jupyter to determine
30 | * if the line of code just entered should be executed immediately or if more input is required.
31 | * It also compiles the code to ensure that it is valid.
32 | *
33 | * @author Keith Suderman
34 | */
35 | class CompleteHandler extends AbstractHandler {
36 |
37 | GroovyClassLoader compiler
38 |
39 | static final String COMPLETE_CHARS = '"\'};])'
40 | static final String INCOMPLETE_CHARS = '([:='
41 |
42 | String waitingFor = null
43 |
44 | public CompleteHandler(GroovyKernel kernel) {
45 | super(kernel)
46 | logger = LoggerFactory.getLogger(CompleteHandler)
47 | compiler = new GroovyClassLoader()
48 | }
49 |
50 | void handle(Message message) {
51 | String code = message.content.code.trim()
52 | logger.debug("Checking code: {}", code)
53 |
54 | // One of 'complete', 'incomplete', 'invalid', 'unknown'
55 | String status = 'unknown'
56 | String ch = code[-1]
57 | if (ch == '{') {
58 | waitingFor = '}'
59 | status = 'incomplete'
60 | }
61 | else if (waitingFor) {
62 | if (ch == waitingFor) {
63 | status = 'complete'
64 | waitingFor = null
65 | }
66 | else {
67 | status = 'incomplete'
68 | }
69 | }
70 | else if (INCOMPLETE_CHARS.contains(ch)) {
71 | logger.trace("Incomplete due to char {}", ch)
72 | status = 'incomplete'
73 | }
74 | else if (COMPLETE_CHARS.contains(ch)) {
75 | logger.trace("Complete due to char {}", ch)
76 | status = 'complete'
77 | }
78 | else {
79 | try {
80 | logger.trace("Attempting to compile code.")
81 | compiler.parseClass(code)
82 | logger.trace("Complete")
83 | status = 'complete'
84 | }
85 | catch (Exception e)
86 | {
87 | logger.debug("Invalid: {}", e.message)
88 | }
89 | }
90 | Message reply = new Message()
91 | reply.header = new Header(COMPLETE_REPLY, message)
92 | reply.identities = message.identities
93 | reply.parentHeader = message.header
94 | reply.content = [
95 | status: status,
96 | ]
97 | send(reply)
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | # Rolling Out A Release
2 |
3 | This page is intended for *Release Managers*, that is, people that have the proper credentials to:
4 |
5 | 1. Commit changes to the *master* branch on GitHub.
6 | 1. Have a public certificate installed on lappsgrid.org so they can upload tarballs.
7 | 1. Have credentials to push Docker images to the Docker Hub, and finally
8 | 1. Have a user account and permissions to push org.lappsgrid artifacts to Maven Central
9 |
10 | ## TL;DR
11 |
12 | Assume we are releasing version 1.2.0
13 |
14 | ```bash
15 | git flow release start 1.2.0
16 | mvn versions:set -DnewVersion=1.2.0 -DgeneratebackupPoms=false
17 | cd src/docker
18 | ./build.sh
19 | docker tag lappsgrid/jupyter-groovy-kernel lappsgrid/jupyter-groovy-kernel:1.20
20 | docker push lappsgrid/jupyter-groovy-kernel
21 | docker push lappsgrid/jupyter-groovy-kernel:1.20
22 | cd ../../src/distribution
23 | ./create.sh
24 | cd ../
25 | ./release.sh --site
26 | git flow release finish 1.20
27 | git push --tags
28 | git push origin master
29 | ```
30 |
31 | It is up to the person performing the release to ensure the project does not include any SNAPSHOT dependencies.
32 |
33 | ## The Long Story
34 |
35 | There are a number of steps required to complete a new release:
36 |
37 | 1. Create and deploy the Docker images.
38 | 1. Create and upload a tarball of the kernel to lappsgrid.org
39 | 1. Generate the Maven site and deploy it to the gh-pages branch of the repository.
40 | 1. Deploy the Maven artifacts to Maven Central.
41 | 1. Upate the master branch on GitHub and create a Git tag of the current state of master.
42 |
43 |
44 | ### Git
45 |
46 | While not strictly required, these instructions assume you are using [Git Flow](https://github.com/nvie/gitflow) to handle the Git branching, merging and tagging. If you don't or can't use Git Flow simply create a "release branch" for performing the release tasks and then merge the release branch into *master* when finished. Don't forget to *tag* the version on GitHub as part of the release if not using Git Flow.
47 |
48 | ```bash
49 | git flow release start v1.2.0
50 | ...work...work...work...
51 | git commit -a -m "Release ready."
52 | git flow release finish v1.2.0
53 | ```
54 | -or-
55 | ```bash
56 | git checkout -b v1.2.0
57 | ...work...work...work...
58 | git checkout master
59 | git merge v1.2.0
60 | git branch -d v1.2.0
61 | git tag -a -m "Release v1.2.0"
62 | git checkout develop
63 | git merge master
64 | ```
65 |
66 | After the release branch has been created either use the Maven Versions plugin to change the version number in the pom.xml file or edit the file manually.
67 |
68 | ```bash
69 | mvn versions:set -DnewVersion=1.2.0 -DgenerateBackupPoms=false
70 | ```
71 |
72 | ### Docker
73 |
74 | The `build.sh` script used to generate the Docker images does N things:
75 |
76 | 1. Gets a copy of the most current JAR file.
77 | 1. Creates a kernel.json file that references the current JAR.
78 | 1. Sets the VERSION arg for the Dockerfile.
79 | 1. Runs `docker build`
80 |
81 | Once the image has been generated it needs to be tagged and pushed to the Docker Hub.
82 |
83 | ### Downloadable Tarball
84 |
85 | To be written. See the `src/distribution/create.sh` script for details.
86 |
87 |
88 | ### Maven Central
89 |
90 | The Lappsgrid parent pom is set up so PGP signatures are generated for all artifacts if the Maven variable gpg.passphrase has been defined. The deploy plugin has also been configured to deploy SNAPSHOT versions to the Sonatype Snapshot Repository and release builds to Maven Central.
91 |
92 | ```bash
93 | mvn -Dgpg.passphrase=top_secret package source:jar deploy
94 | ```
95 |
96 | The GroovyDoc Maven Plugin is run during the *package* phase and does not need to be run separately.
97 |
98 | ### Deploying The Maven Site
99 |
100 | The [GitHub Site Plugin](https://github.github.com/maven-plugins/site-plugin/) is used to deploy the Maven generated site to the gh-pages branch on GitHub.
101 |
102 | ```bash
103 | mvn site
104 | ```
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Groovy Jupyter Kernel
2 |
3 | A native [Jupyter](http://jupyter.org) kernel for the [Apache Groovy](http://www.groovy-lang.org) language. By "native" we mean the kernel is written in Groovy and handles the ZeroMQ message queues directly.
4 |
5 | [](https://maven-badges.herokuapp.com/maven-central/org.lappsgrid.jupyter/jupyter-groovy-kernel)
6 |
7 | ## Contents
8 |
9 | 1. [Installation](#installation)
10 | a. [From Source](#from-source)
11 | a. [Manually](#manually)
12 | 1. [Docker](#docker)
13 | 1. [Creating a Kernel for a Groovy DSL](#creating-a-kernel-for-a-groovy-dsl)
14 | 1. [Contributing](#contributing)
15 |
16 | ## Documentation
17 |
18 | The Maven generated site (API docs etc.) is available [here](https://lappsgrid-incubator.github.io/jupyter-groovy-kernel).
19 |
20 | ## Installation
21 |
22 | ### From Source
23 |
24 | Building the Groovy Jupyter Kernel project requires Maven 3.x or higher.
25 |
26 | ```bash
27 | $> git clone https://github.com/lappsgrid-incubator/jupyter-groovy-kernel.git
28 | $> cd jupyter-groovy-kernel
29 | $> mvn clean package
30 | $> ./install.sh
31 | ```
32 |
33 | Where *<kernel directory>* is a directory where the kernel jar file will be copied and can be any directory on your system.
34 |
35 | If you do not have Maven installed you can use the `mvnw` (Linus/OS X) or `mvnw.cmd` (Windows) [Maven Wrapper](https://github.com/takari/maven-wrapper) scripts to build the project.
36 |
37 | ```bash
38 | $> ./mvnw clean package
39 | ```
40 |
41 | ### Manually
42 |
43 | Download and expand the [Groovy Kernel archive](http://www.lappsgrid.org/downloads/jupyter-groovy-kernel-latest.tgz) and then run the *install.sh* script.
44 |
45 | ```bash
46 | $> wget http://www.lappsgrid.org/downloads/jupyter-groovy-kernel-latest.tgz
47 | $> tar xzf jupyter-groovy-kernel-latest.tgz
48 | $> cd jupyter-groovy-kernel
49 | $> ./install.sh
50 | ```
51 |
52 | Where *<kernel directory>* is a directory where the kernel jar file will be copied and can be any directory on your system.
53 |
54 | ### Notes
55 |
56 | By default the *install.sh* script will install the Jupyter kernel to the system kernel directory. This is typically */usr/local/share/juptyer* on Linux/MacOS systems and %PROGRAMDATA%\jupyter\kernels on Windows systems. To install the Jupyter kernel to the User's directory you must either:
57 |
58 | * Edit the *install.script* and add the *--user* option to the `kernelspec` command, or
59 | * Edit the kernel.json file to set the *argv* paramater to the location of the Jupyter Groovy kernel and then run the `jupyter kernelspec install` command.
60 |
61 | ## Docker
62 |
63 | A Docker image containing the Groovy Kernel is available from the [Docker Hub](https://hub.docker.com/r/lappsgrid/jupyter-groovy-kernel). To save notebooks outside of the Docker container you will need to mount a local directory as */home/jovyan/work* inside the container.
64 |
65 | ```bash
66 | docker run -p 8888:8888 -v /path/to/local/directory:/home/jovyan/work lappsgrid/jupyter-groovy-kernel
67 | ```
68 |
69 | Please refer to the Docker Hub website for a list of the current Docker images available.
70 |
71 | ## Creating a Kernel for a Groovy DSL
72 |
73 | ### The short version
74 |
75 | To create a Jupyter kernel for a Groovy DSL you will need to:
76 |
77 | 1. Add the Groovy Jupyter kernel project as a dependency.
78 | 1. Implement the `GroovyContext` interface to provide a CompilerConfiguration,
79 | base script, and/or MetaClass object for compiling user code.
80 | 1. Pass your `GroovyContext` to the `GroovyKernel` constructor
81 |
82 | ### The long version
83 |
84 | The Groovy kernel uses objects that implement the `org.lappsgrid.jupyter.groovy.context.GroovyContext` interface to configure the Groovy compiler and to obtain `MetaClass` instances that are attached to the compiled script objects.
85 |
86 | ```java
87 | interface GroovyContext {
88 | CompilerConfiguration getCompilerConfiguration();
89 | MetaClass getMetaClass(Class aClass);
90 | }
91 | ```
92 |
93 | There is also a `DefaultGroovyContext` class that implements both methods and returns a default `CompilerConfiguration` object and default `ExapandoMetaClass`. You can use the `DefaultGroovyContext` class if you only want/need to implement one of the `GroovyContext` methods.
94 |
95 | To create a Jupyter kernel for a Groovy DSL implement the `GroovyContext` interface and pass that object to the *GroovyKernel* constructor.
96 |
97 | ```java
98 | class CustomContext extends DefaultGroovyContext {
99 | ...
100 | }
101 |
102 | class CustomJupyterKernel {
103 | static void main(String[] args) {
104 | GroovyContext context = new CustomContext()
105 | GroovyKernel kernel = new GroovyKernel(context)
106 | kernel.connectionFile = new File(args[0])
107 | kernel.run()
108 | }
109 | ```
110 |
111 | See the [Lappsgrid Services DSL Jupyter kernel](https://github.com/lappsgrid-incubator/jupyter-lsd-kernel) for an example of implementing a Jupyter kernel for a Groovy DSL using the Groovy Jupyter kernel.
112 |
113 | ## Contributing
114 |
115 | If you would like to contribute to the Jupyter Groovy Kernel please Fork this repository, make your changes, and then submit a pull request targeting the `develop` branch.
116 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM http://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven2 Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
40 |
41 | @REM set %HOME% to equivalent of $HOME
42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
43 |
44 | @REM Execute a user defined script before this one
45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
49 | :skipRcPre
50 |
51 | @setlocal
52 |
53 | set ERROR_CODE=0
54 |
55 | @REM To isolate internal variables from possible post scripts, we use another setlocal
56 | @setlocal
57 |
58 | @REM ==== START VALIDATION ====
59 | if not "%JAVA_HOME%" == "" goto OkJHome
60 |
61 | echo.
62 | echo Error: JAVA_HOME not found in your environment. >&2
63 | echo Please set the JAVA_HOME variable in your environment to match the >&2
64 | echo location of your Java installation. >&2
65 | echo.
66 | goto error
67 |
68 | :OkJHome
69 | if exist "%JAVA_HOME%\bin\java.exe" goto init
70 |
71 | echo.
72 | echo Error: JAVA_HOME is set to an invalid directory. >&2
73 | echo JAVA_HOME = "%JAVA_HOME%" >&2
74 | echo Please set the JAVA_HOME variable in your environment to match the >&2
75 | echo location of your Java installation. >&2
76 | echo.
77 | goto error
78 |
79 | @REM ==== END VALIDATION ====
80 |
81 | :init
82 |
83 | set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %*
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 |
121 | set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
123 |
124 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %*
125 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
126 | if ERRORLEVEL 1 goto error
127 | goto end
128 |
129 | :error
130 | set ERROR_CODE=1
131 |
132 | :end
133 | @endlocal & set ERROR_CODE=%ERROR_CODE%
134 |
135 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
136 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
137 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
138 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
139 | :skipRcPost
140 |
141 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
142 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
143 |
144 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
145 |
146 | exit /B %ERROR_CODE%
147 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/handler/ExecuteHandler.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy.handler
19 |
20 | import org.codehaus.groovy.control.CompilerConfiguration
21 | import org.lappsgrid.jupyter.groovy.GroovyKernel
22 | import org.lappsgrid.jupyter.groovy.Version
23 | import org.lappsgrid.jupyter.groovy.msg.Header
24 | import org.lappsgrid.jupyter.groovy.msg.Message
25 | import org.slf4j.LoggerFactory
26 | import org.zeromq.ZMQ
27 |
28 | import static Message.Type.*
29 |
30 | /**
31 | * Does the actual work of executing user code.
32 | *
33 | * @author Keith Suderman
34 | */
35 | class ExecuteHandler extends AbstractHandler {
36 |
37 | int executionCount
38 | Binding binding
39 | GroovyShell compiler
40 | Set included
41 |
42 | public ExecuteHandler(GroovyKernel kernel) {
43 | super(kernel)
44 | logger = LoggerFactory.getLogger(ExecuteHandler)
45 | executionCount = 0
46 | binding = new Binding()
47 | binding.setVariable('version', version())
48 | CompilerConfiguration configuration = kernel.context.getCompilerConfiguration()
49 | compiler = new GroovyShell(this.class.classLoader, binding, configuration)
50 | }
51 |
52 | //TODO some security would be nice!
53 | void handle(Message message) {
54 | logger.info("Processing execute request")
55 | Message reply = new Message()
56 | reply.content = [ execution_state: 'busy' ]
57 | reply.header = new Header(STATUS, message)
58 | reply.parentHeader = message.header
59 | reply.identities = message.identities
60 | publish(reply)
61 |
62 | // Get the code to be executed from the message.
63 | String code = message.content.code.trim()
64 |
65 | if (message.content.allow_stdin) {
66 | kernel.allowStdin(true)
67 | }
68 | else {
69 | kernel.allowStdin(false)
70 | }
71 |
72 | // TODO Should check message.content.store_history here as well.
73 |
74 | // Announce that we have the code.
75 | reply.header = new Header(EXECUTE_INPUT, message)
76 | reply.content = [
77 | execution_count: executionCount,
78 | code: message.content.code
79 | ]
80 | publish(reply)
81 |
82 | // Now try compiling and running the code.
83 | Exception error = null
84 | try {
85 | logger.debug("Running: {}", code)
86 | Script script = compiler.parse(code)
87 | ExpandoMetaClass meta = kernel.context.getMetaClass(script.class, false)
88 | meta.readline = { String prompt ->
89 | if (!kernel.stdinEnabled) {
90 | return "STDIN is not enabled for this request."
91 | }
92 | Message stdinMsg = new Message()
93 | stdinMsg.content = [
94 | prompt : prompt,
95 | password: false
96 | ]
97 | stdinMsg.header = new Header(STDIN_REQUEST, message.header.session)
98 | stdinMsg.identities = message.identities
99 | stdinMsg.parentHeader = message.header
100 | ZMQ.Socket socket = kernel.stdinSocket
101 | kernel.send(socket, stdinMsg)
102 | logger.trace("Send message on stdin socket.")
103 | stdinMsg = kernel.readMessage(socket)
104 | logger.trace ("Received response on stdin socket.")
105 | //println stdinMsg.asJson()
106 | return stdinMsg.content.value
107 | }
108 | meta.version = {
109 | version()
110 | }
111 |
112 | meta.initialize()
113 | script.metaClass = meta
114 | logger.trace("code compiled")
115 | Object result = script.run()
116 | logger.trace("Ran script")
117 | if (result == null) {
118 | result = 'Cell returned null.'
119 | }
120 | ++executionCount
121 | logger.debug("Result is {}", result)
122 |
123 | // Publish the result of the execution.
124 | reply.header = new Header(EXECUTE_RESULT, message)
125 | reply.content = [
126 | execution_count: executionCount,
127 | data: [ 'text/plain': result.toString() ],
128 | metadata: [:]
129 | ]
130 | publish(reply)
131 | }
132 | catch (Exception e) {
133 | logger.error('Unable to execute code block')
134 | logger.error(e.message)
135 | error = e
136 | reply.header = new Header(STREAM, message)
137 | reply.content = [
138 | name: 'stderr',
139 | text: e.message
140 | ]
141 | publish(reply)
142 | }
143 |
144 | // Tell Jupyter that this kernel is idle again.
145 | reply.header = new Header(STATUS, message)
146 | reply.content = [ execution_state: 'idle']
147 | publish(reply)
148 |
149 | // Send the REPLY to the original message. This is NOT the result of
150 | // executing the cell. This is the equivalent of 'exit 0' or 'exit 1'
151 | // at the end of a shell script.
152 | reply.header = new Header(EXECUTE_REPLY, message)
153 | reply.metadata = [
154 | dependencies_met: true,
155 | engine: kernel.id,
156 | started: kernel.timestamp()
157 | ]
158 | reply.content = [
159 | execution_count: executionCount,
160 | ]
161 | if (error) {
162 | reply.metadata.status = 'error'
163 | reply.content.status = 'error'
164 | reply.content.ename = error.class.name
165 | reply.content.evalue = error.message
166 | }
167 | else {
168 | reply.metadata.status = 'ok'
169 | reply.content.status = 'ok'
170 | reply.content.user_expressions = [:]
171 | }
172 | send(reply)
173 | }
174 |
175 | String version() {
176 | return """
177 | Groovy Jupyter Kernel v${Version.version}
178 | Copyright 2017 The Language Application Grid
179 | Distributed under the Apache 2.0 License
180 | See https://github.com/lappsgrid-incubator/jupyter-groovy-kernel for more information.
181 | """
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven2 Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | #
58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look
59 | # for the new JDKs provided by Oracle.
60 | #
61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
62 | #
63 | # Apple JDKs
64 | #
65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
66 | fi
67 |
68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
69 | #
70 | # Apple JDKs
71 | #
72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
73 | fi
74 |
75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
76 | #
77 | # Oracle JDKs
78 | #
79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
80 | fi
81 |
82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
83 | #
84 | # Apple JDKs
85 | #
86 | export JAVA_HOME=`/usr/libexec/java_home`
87 | fi
88 | ;;
89 | esac
90 |
91 | if [ -z "$JAVA_HOME" ] ; then
92 | if [ -r /etc/gentoo-release ] ; then
93 | JAVA_HOME=`java-config --jre-home`
94 | fi
95 | fi
96 |
97 | if [ -z "$M2_HOME" ] ; then
98 | ## resolve links - $0 may be a link to maven's home
99 | PRG="$0"
100 |
101 | # need this for relative symlinks
102 | while [ -h "$PRG" ] ; do
103 | ls=`ls -ld "$PRG"`
104 | link=`expr "$ls" : '.*-> \(.*\)$'`
105 | if expr "$link" : '/.*' > /dev/null; then
106 | PRG="$link"
107 | else
108 | PRG="`dirname "$PRG"`/$link"
109 | fi
110 | done
111 |
112 | saveddir=`pwd`
113 |
114 | M2_HOME=`dirname "$PRG"`/..
115 |
116 | # make it fully qualified
117 | M2_HOME=`cd "$M2_HOME" && pwd`
118 |
119 | cd "$saveddir"
120 | # echo Using m2 at $M2_HOME
121 | fi
122 |
123 | # For Cygwin, ensure paths are in UNIX format before anything is touched
124 | if $cygwin ; then
125 | [ -n "$M2_HOME" ] &&
126 | M2_HOME=`cygpath --unix "$M2_HOME"`
127 | [ -n "$JAVA_HOME" ] &&
128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
129 | [ -n "$CLASSPATH" ] &&
130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
131 | fi
132 |
133 | # For Migwn, ensure paths are in UNIX format before anything is touched
134 | if $mingw ; then
135 | [ -n "$M2_HOME" ] &&
136 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
137 | [ -n "$JAVA_HOME" ] &&
138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
139 | # TODO classpath?
140 | fi
141 |
142 | if [ -z "$JAVA_HOME" ]; then
143 | javaExecutable="`which javac`"
144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
145 | # readlink(1) is not available as standard on Solaris 10.
146 | readLink=`which readlink`
147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
148 | if $darwin ; then
149 | javaHome="`dirname \"$javaExecutable\"`"
150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
151 | else
152 | javaExecutable="`readlink -f \"$javaExecutable\"`"
153 | fi
154 | javaHome="`dirname \"$javaExecutable\"`"
155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
156 | JAVA_HOME="$javaHome"
157 | export JAVA_HOME
158 | fi
159 | fi
160 | fi
161 |
162 | if [ -z "$JAVACMD" ] ; then
163 | if [ -n "$JAVA_HOME" ] ; then
164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
165 | # IBM's JDK on AIX uses strange locations for the executables
166 | JAVACMD="$JAVA_HOME/jre/sh/java"
167 | else
168 | JAVACMD="$JAVA_HOME/bin/java"
169 | fi
170 | else
171 | JAVACMD="`which java`"
172 | fi
173 | fi
174 |
175 | if [ ! -x "$JAVACMD" ] ; then
176 | echo "Error: JAVA_HOME is not defined correctly." >&2
177 | echo " We cannot execute $JAVACMD" >&2
178 | exit 1
179 | fi
180 |
181 | if [ -z "$JAVA_HOME" ] ; then
182 | echo "Warning: JAVA_HOME environment variable is not set."
183 | fi
184 |
185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
186 |
187 | # traverses directory structure from process work directory to filesystem root
188 | # first directory with .mvn subdirectory is considered project base directory
189 | find_maven_basedir() {
190 | local basedir=$(pwd)
191 | local wdir=$(pwd)
192 | while [ "$wdir" != '/' ] ; do
193 | if [ -d "$wdir"/.mvn ] ; then
194 | basedir=$wdir
195 | break
196 | fi
197 | wdir=$(cd "$wdir/.."; pwd)
198 | done
199 | echo "${basedir}"
200 | }
201 |
202 | # concatenates all lines of a file
203 | concat_lines() {
204 | if [ -f "$1" ]; then
205 | echo "$(tr -s '\n' ' ' < "$1")"
206 | fi
207 | }
208 |
209 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
210 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
211 |
212 | # For Cygwin, switch paths to Windows format before running java
213 | if $cygwin; then
214 | [ -n "$M2_HOME" ] &&
215 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
216 | [ -n "$JAVA_HOME" ] &&
217 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
218 | [ -n "$CLASSPATH" ] &&
219 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
220 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
221 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
222 | fi
223 |
224 | # Provide a "standardized" way to retrieve the CLI args that will
225 | # work with both Windows and non-Windows executions.
226 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
227 | export MAVEN_CMD_LINE_ARGS
228 |
229 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
230 |
231 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@
232 | exec "$JAVACMD" \
233 | $MAVEN_OPTS \
234 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
235 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
236 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
237 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | parent-pom
8 | org.lappsgrid.maven
9 | 2.0.2
10 |
11 | org.lappsgrid.jupyter
12 | jupyter-groovy-kernel
13 | 1.2.1
14 | Jupyter Groovy Kernel
15 | A Jupyter kernel for Apache Groovy.
16 | 2016
17 | https://github.com/lappsgrid-incubator/jupyter-groovy-kernel
18 |
19 |
20 | The Apache License, Version 2.0
21 | http://www.apache.org/licenses/LICENSE-2.0.txt
22 |
23 |
24 |
25 | https://github.com/lappsgrid-incubator/jupyter-groovy-kernel
26 | scm:git:https://github.com/lappsgrid-incubator/jupyter-groovy-kernel.git
27 | scm:git:https://github.com/lappsgrid-incubator/jupyter-groovy-kernel.git
28 |
29 |
30 | https://github.com/lappsgrid-incubator/jupyter-groovy-kernel/issues
31 | GitHub issues
32 |
33 |
34 | 2.4.7
35 | 2.4.0
36 |
37 |
38 |
39 |
40 | maven-compiler-plugin
41 | 3.3
42 |
43 | groovy-eclipse-compiler
44 | true
45 | -J -Xmx2G -Xss10M
46 |
47 |
48 |
49 | org.codehaus.groovy
50 | groovy-eclipse-compiler
51 | 2.9.2-01
52 |
53 |
54 | org.codehaus.groovy
55 | groovy-eclipse-batch
56 | 2.4.3-01
57 |
58 |
59 |
60 |
61 | org.codehaus.groovy
62 | groovy-eclipse-compiler
63 | 2.9.2-01
64 | true
65 |
66 |
67 | org.anc.maven.plugins
68 | anc-maven-plugin
69 | 1.0.7
70 |
71 | org.lappsgrid.jupyter.groovy
72 |
73 |
74 |
75 | version
76 | generate-sources
77 |
78 | version
79 | version-class
80 |
81 |
82 |
83 |
84 |
85 | org.apache.maven.plugins
86 | maven-assembly-plugin
87 | 2.6
88 |
89 |
90 | assembly
91 | package
92 |
93 | single
94 |
95 |
96 |
97 |
98 | ${project.artifactId}-${project.version}
99 |
100 | jar-with-dependencies
101 |
102 | false
103 |
104 |
105 | org.lappsgrid.jupyter.groovy.GroovyKernel
106 |
107 |
108 |
109 |
110 |
111 | com.bluetrainsoftware.maven
112 | groovydoc-maven-plugin
113 | 1.3
114 |
115 |
116 | attach-docs
117 | package
118 |
119 | attach-docs
120 |
121 |
122 |
123 |
124 | ${project.reporting.outputDirectory}/groovydoc
125 |
126 |
127 |
128 |
129 |
130 |
131 | org.zeromq
132 | jeromq
133 | 0.3.5
134 |
135 |
136 | org.codehaus.groovy
137 | groovy-all
138 | ${groovy.version}
139 |
140 |
141 | org.apache.ivy
142 | ivy
143 | ${ivy.version}
144 |
145 |
146 | ch.qos.logback
147 | logback-classic
148 | 1.1.3
149 |
150 |
151 | ch.qos.logback
152 | logback-core
153 | 1.1.3
154 |
155 |
156 | commons-codec
157 | commons-codec
158 | 1.9
159 |
160 |
161 | com.fasterxml.jackson.core
162 | jackson-annotations
163 | 2.5.1
164 |
165 |
166 | com.fasterxml.jackson.core
167 | jackson-databind
168 | 2.5.1
169 |
170 |
171 | com.fasterxml.jackson.core
172 | jackson-core
173 | 2.5.1
174 |
175 |
176 |
177 |
178 |
179 | com.bluetrainsoftware.maven
180 | groovydoc-maven-plugin
181 | 1.3
182 |
183 | ${project.reporting.outputDirectory}/groovydoc
184 |
185 |
186 |
187 | org.apache.maven.plugins
188 | maven-javadoc-plugin
189 | 2.9.1
190 |
191 |
192 |
193 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/src/main/groovy/org/lappsgrid/jupyter/groovy/GroovyKernel.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 The Language Application Grid
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.lappsgrid.jupyter.groovy
19 |
20 | import org.lappsgrid.jupyter.groovy.context.DefaultGroovyContext
21 | import org.lappsgrid.jupyter.groovy.context.GroovyContext
22 | import org.lappsgrid.jupyter.groovy.handler.CompleteHandler
23 | import org.lappsgrid.jupyter.groovy.handler.ExecuteHandler
24 | import org.lappsgrid.jupyter.groovy.handler.HistoryHandler
25 | import org.lappsgrid.jupyter.groovy.handler.IHandler
26 | import org.lappsgrid.jupyter.groovy.handler.KernelInfoHandler
27 | import org.lappsgrid.jupyter.groovy.json.Serializer
28 | import org.lappsgrid.jupyter.groovy.msg.Header
29 | import org.lappsgrid.jupyter.groovy.msg.Message
30 | import org.lappsgrid.jupyter.groovy.security.HmacSigner
31 | import org.lappsgrid.jupyter.groovy.threads.AbstractThread
32 | import org.lappsgrid.jupyter.groovy.threads.ControlThread
33 | import org.lappsgrid.jupyter.groovy.threads.HeartbeatThread
34 | import org.lappsgrid.jupyter.groovy.threads.ShellThread
35 | import org.lappsgrid.jupyter.groovy.threads.StdinThread
36 | import org.slf4j.Logger
37 | import org.slf4j.LoggerFactory
38 | import org.zeromq.ZMQ
39 |
40 | import java.security.InvalidKeyException
41 | import java.text.DateFormat
42 | import java.text.SimpleDateFormat
43 |
44 | import static Message.Type.*
45 |
46 | /**
47 | * The entry point for the Jupyter kernel.
48 | *
49 | * @author Keith Suderman
50 | */
51 | class GroovyKernel {
52 | private static final Logger logger = LoggerFactory.getLogger(GroovyKernel)
53 |
54 | /** The timezone to use when generating time stamps. */
55 | static final TimeZone UTC = TimeZone.getTimeZone('UTC')
56 |
57 | private volatile boolean running = false
58 | boolean stdinEnabled = false
59 |
60 | static final String DELIM = ""
61 |
62 | /** Used to generate the HMAC signatures for messages */
63 | HmacSigner hmac
64 |
65 | /** The UUID for this session. */
66 | String id
67 |
68 | /** Information from the connection file from Jupyter. */
69 | File connectionFile
70 | Config configuration
71 |
72 | /** Message handlers. All sockets listeners will dispatch to these handlers. */
73 | Map handlers
74 |
75 | /** Used to configure the Groovy compiler when user code needs to be compiled. */
76 | GroovyContext context
77 |
78 | // The sockets the kernel listens to.
79 | ZMQ.Socket hearbeatSocket
80 | ZMQ.Socket controlSocket
81 | ZMQ.Socket shellSocket
82 | ZMQ.Socket iopubSocket
83 | ZMQ.Socket stdinSocket
84 |
85 | public GroovyKernel() {
86 | this(new DefaultGroovyContext())
87 | }
88 |
89 | public GroovyKernel(GroovyContext context) {
90 | id = uuid()
91 | this.context = context
92 | installHandlers()
93 | }
94 |
95 | static String timestamp() {
96 | // SimpleDateFormat is not thread-safe so we need to create a new one for each
97 | // timestamp that is generated.
98 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ")
99 | df.setTimeZone(UTC)
100 | return df.format(new Date())
101 | }
102 |
103 | static String encode(Object object) {
104 | return Serializer.toJson(object)
105 | }
106 |
107 | static Map decode(String json) {
108 | return Serializer.parse(json, LinkedHashMap)
109 | }
110 |
111 | static T decode(byte[] bytes) {
112 | return decode(new String(bytes))
113 | }
114 | static T decode(String json, Class theClass) {
115 | return Serializer.parse(json, theClass)
116 | }
117 |
118 | Map info() {
119 | return [
120 | protocol_version: '5.0',
121 | implementation: 'groovy',
122 | implementation_version: '1.0.0',
123 | language_info: [
124 | name: 'Groovy',
125 | version: '2.4.7',
126 | mimetype: '',
127 | file_extension: '.groovy',
128 | pygments_lexer: '',
129 | codemirror_mode: '',
130 | nbconverter_exporter: ''
131 | ],
132 | banner: 'Apache Groovy',
133 | help_links: []
134 | ]
135 | }
136 |
137 | void shutdown() {
138 | running = false
139 | }
140 |
141 | static String uuid() {
142 | return UUID.randomUUID()
143 | }
144 |
145 | private void installHandlers() {
146 | handlers = [
147 | execute_request: new ExecuteHandler(this),
148 | kernel_info_request: new KernelInfoHandler(this),
149 | is_complete_request: new CompleteHandler(this),
150 | history_request: new HistoryHandler(this),
151 | ]
152 | }
153 |
154 | void allowStdin(boolean allow) {
155 | stdinEnabled = allow
156 | }
157 |
158 | /** Sends a Message to the iopub socket. */
159 | void publish(Message message) {
160 | send(iopubSocket, message)
161 | }
162 |
163 | // Most things go to the shell socket so that is the default.
164 | void send(Message message) {
165 | send(shellSocket, message)
166 | }
167 |
168 | void send(ZMQ.Socket socket, Message message) {
169 | logger.trace("Sending message: {}", message.asJson())
170 | // Encode the message parts (blobs) and calculate the signature.
171 | List parts = [
172 | encode(message.header),
173 | encode(message.parentHeader),
174 | encode(message.metadata),
175 | encode(message.content)
176 | ]
177 | String signature = hmac.sign(parts)
178 | logger.trace("Signature is {}", signature)
179 |
180 | // Now send the message down the wire.
181 | message.identities.each { socket.sendMore(it) }
182 | socket.sendMore(DELIM)
183 | socket.sendMore(signature)
184 | 3.times { i -> socket.sendMore(parts[i]) }
185 | socket.send(parts[3])
186 | logger.trace("Message sent")
187 | }
188 |
189 | String read(ZMQ.Socket socket) {
190 | return new String(socket.recv())
191 | // }
192 | // catch (Exception e) {
193 | // logger.warn("Error reading the socket.", e)
194 | // return ""
195 | // }
196 | }
197 |
198 | public T read(ZMQ.Socket socket, Class theClass) {
199 | return Serializer.parse(read(socket), theClass)
200 | }
201 |
202 | public T parse(byte[] bytes, Class theClass) {
203 | return Serializer.parse(new String(bytes), theClass)
204 | }
205 |
206 | /**
207 | * Reads a Jupyter message from a ZMQ socket.
208 | *
209 | * Each message consists of at least six blobs of bytes:
210 | *
211 | * - zero or more identities
212 | * - '<IDS|MSG>'
213 | * - HMAC signature
214 | * - header
215 | * - parent header
216 | * - metadata
217 | * - content
218 | *
219 | *
220 | * @param socket The ZMQ.Socket object to read from.
221 | * @return a newly initialized Message object.
222 | */
223 | Message readMessage(ZMQ.Socket socket) {
224 | Message message = new Message()
225 | try {
226 | // Read socket identities until we encounter the delimiter
227 | String identity = read(socket)
228 | while (DELIM != identity) {
229 | message.identities << identity
230 | identity = read(socket)
231 | }
232 | // Read the signature and the four blobs
233 | String expectedSig = read(socket)
234 | byte[] header = socket.recv()
235 | byte[] parent = socket.recv()
236 | byte[] metadata = socket.recv()
237 | byte[] content = socket.recv()
238 |
239 | // Make sure that the signatures match before proceeding.
240 | String actualSig = hmac.signBytes([header, parent, metadata, content])
241 | if (expectedSig != actualSig) {
242 | logger.error("Message signatures do not match")
243 | logger.error("Expected: {}", expectedSig)
244 | logger.error("Actual : {}", actualSig)
245 | throw new RuntimeException("Signatures do not match.")
246 | }
247 |
248 | // Parse the byte buffers into the appropriate types
249 | message.header = parse(header, Header)
250 | message.parentHeader = parse(parent, Header)
251 | message.metadata = parse(metadata, LinkedHashMap)
252 | message.content = parse(content, LinkedHashMap)
253 |
254 | } catch (Exception e) {
255 | throw new RuntimeException("Unable to read the socket.", e)
256 | }
257 | return message
258 | }
259 |
260 | IHandler getHandler(String type) {
261 | return handlers[type]
262 | }
263 |
264 | public void run() {
265 | logger.info("Groovy Jupyter kernel starting.")
266 | running = true
267 |
268 | logger.debug("Parsing the connection file.")
269 | configuration = Serializer.parse(connectionFile.text, Config)
270 |
271 | logger.debug("Creating signing hmac with: {}", configuration.key)
272 | hmac = new HmacSigner(configuration.key)
273 |
274 | String connection = configuration.transport + '://' + configuration.host
275 | ZMQ.Context context = ZMQ.context(1)
276 |
277 | // A factory "method" for creating sockets.
278 | def newSocket = { int type, int port ->
279 | ZMQ.Socket socket = context.socket(type)
280 | socket.bind("$connection:$port")
281 | return socket
282 | }
283 |
284 | // Create all the sockets we need to listen to.
285 | hearbeatSocket = newSocket(ZMQ.REP, configuration.heartbeat)
286 | iopubSocket = newSocket(ZMQ.PUB, configuration.iopub)
287 | controlSocket = newSocket(ZMQ.ROUTER, configuration.control)
288 | stdinSocket = newSocket(ZMQ.ROUTER, configuration.stdin)
289 | shellSocket = newSocket(ZMQ.ROUTER, configuration.shell)
290 |
291 | List sockets = [
292 | hearbeatSocket, iopubSocket, controlSocket, stdinSocket, shellSocket
293 | ]
294 |
295 | // Create all the threads that respond to ZMQ messages.
296 | HeartbeatThread heartbeatThread = new HeartbeatThread(hearbeatSocket, this)
297 | ControlThread controlThread = new ControlThread(controlSocket, this)
298 | ShellThread shellThread = new ShellThread(shellSocket, this)
299 |
300 | // List threads = [
301 | // new HeartbeatThread(hearbeatSocket, this),
302 | // new ControlThread(controlSocket, this),
303 | // new ShellThread(shellSocket, this)
304 | // ]
305 | List threads = [
306 | heartbeatThread, controlThread, shellThread
307 | ]
308 | threads*.start()
309 |
310 | while (running) {
311 | // Nothing to do but navel gaze until another thread sets
312 | // running == false
313 | Thread.sleep(1000)
314 | }
315 |
316 | // Signal all threads that it is time to stop and then wait for
317 | // them to finish.
318 | logger.info("Shutting down.")
319 | logger.debug("Halting threads.")
320 | threads*.halt()
321 | logger.debug("Interrupting theads")
322 | threads*.interrupt()
323 | logger.debug("Joining threads.")
324 | threads*.join()
325 |
326 | // Close the sockets and terminate the context.
327 | logger.debug("Closing sockets.")
328 | sockets*.close()
329 | logger.debug("Terminating the ZMQ.Context")
330 | context.term()
331 |
332 | logger.info("Groovy Kernel shutdown complete.")
333 | }
334 |
335 | public static void main(String[] args) {
336 | if (args.length != 1) {
337 | println "Invalid parameters passed to the Groovy kernel."
338 | println "Expected one parameter, found ${args.length}"
339 | args.each { println it }
340 | System.exit(1)
341 | }
342 | File config = new File(args[0])
343 | if (!config.exists()) {
344 | println "Kernel configuration not found."
345 | System.exit(1)
346 | }
347 |
348 | GroovyKernel kernel = new GroovyKernel()
349 | kernel.connectionFile = config
350 | kernel.run()
351 | }
352 | }
353 |
--------------------------------------------------------------------------------