Java Integration with Jupyter Notebooks
In this post, I am delighted to share my journey of seamlessly integrating Java programming within Jupyter notebooks.
Setup
Commencing with the selection of a pertinent Jupyter Docker Stack image, as detailed in the Jupyter Docker Stacks documentation, the following Docker command initializes the setup:
docker pull quay.io/jupyter/minimal-notebook:notebook-7.0.6
Subsequently, the Docker image is run on a Windows WSL environment, with the host IP set to 192.168.68.114:
docker run -p 192.168.68.114:10000:8888 -v ~/work:/home/jovyan/work quay.io/jupyter/minimal-notebook:notebook-7.0.6
The Jupyter environment becomes accessible by navigating to:
http://192.168.68.114:10000/lab?token=52212ee09c057a6c8ef0c6db38eed07bf47037f76a2d8199
Further, the Docker image is tagged and pushed to a local registry:
docker tag quay.io/jupyter/minimal-notebook:notebook-7.0.6 registry.local:5000/jupyter-minimal-notebook:notebook-7.0.6
docker push registry.local:5000/jupyter-minimal-notebook:notebook-7.0.6
Deployment in HomeLab
The deployment in the HomeLab environment commences with the creation of a designated namespace:
kc create ns jupyter
Following this, the deployment configuration is provided via a deploy.yaml file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: jupyter-minimal-notebook
namespace: jupyter
spec:
replicas: 1
selector:
matchLabels:
app: jupyter-minimal-notebook
template:
metadata:
labels:
app: jupyter-minimal-notebook
spec:
containers:
- name: jupyter-minimal-notebook
image: registry.local:5000/jupyter-minimal-notebook:notebook-7.0.6
ports:
- containerPort: 8888
volumeMounts:
- name: jupyter-store
mountPath: /work
subPath: ./jupyter-work
imagePullSecrets:
- name: regcred
volumes:
- name: jupyter-store
persistentVolumeClaim:
claimName: jupyter-nfs
Additionally, a svc.yaml file specifies the service configuration:
apiVersion: v1
kind: Service
metadata:
name: jupyter-minimal-notebook-svc
namespace: jupyter
spec:
selector:
app: jupyter-minimal-notebook
type: LoadBalancer
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8888
Upon successful application of these changes in the HomeLab, the application logs yield the following output:
[C 2024-01-20 15:35:02.221 ServerApp]
To access the server, open this file in a browser:
file:///home/jovyan/.local/share/jupyter/runtime/jpserver-7-open.html
Or copy and paste one of these URLs:
http://jupyter-minimal-notebook-b4fb98bbd-cxttl:8888/lab?token=2251c5c06cdfc487297057534dbc62de7589bcfa24f6be4a
http://127.0.0.1:8888/lab?token=2251c5c06cdfc487297057534dbc62de7589bcfa24f6be4a
[I 2024-01-20 15:35:03.813 ServerApp] Skipped non-installed server(s): bash-language-server, dockerfile-language-server-nodejs, javascript-typescript-langserver, jedi-language-server, julia-language-server, pyright, python-language-server, python-lsp-server, r-languageserver, sql-language-server, texlab, typescript-language-server, unified-language-server, vscode-css-languageserver-bin, vscode-html-languageserver-bin, vscode-json-languageserver-bin, yaml-language-server
In this instance, the external IP is recorded as 192.168.68.225. Access to Jupyter is achieved by navigating to:
http://192.168.68.225/lab?token=2251c5c06cdfc487297057534dbc62de7589bcfa24f6be4a
Java Kernel Integration
The integration of Java within the Jupyter notebook environment is facilitated by JBang, a tool simplifying the creation, editing, and execution of Java programs. Following the guidelines provided by Jupyter Java, let’s create a new notebook within the designated work folder.
!pip install jbang
import jbang
jbang.exec("trust add https://github.com/jupyter-java")
jbang.exec("install-kernel@jupyter-java --java 17")
The Java Kernel can be selected seamlessly:
Testing with Java Kernel
A simple “Hello World” program is executed in the new IJava Notebook:
System.out.println("Hello World!")
Next, let’s try with importing the commons-lang3 library:
%%loadFromPOM
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
Here’s the sample Java code snippet which demonstrates the usage of the Apache Commons Lang library:
import org.apache.commons.lang3.StringUtils;
String sampleString = "Hello, Apache Commons!";
// Check if the string is empty or null using Commons Lang
if (StringUtils.isNotEmpty(sampleString)) {
System.out.println("The string is not empty or null.");
} else {
System.out.println("The string is empty or null.");
}
Exploring Java in PyKernel
Referencing to the JBang Jupyter tutorial, an alternate method to experiment with Java involves using JBang within the Python kernel:
import os
if os.name == 'nt':
!iex "& { $(iwr https://ps.jbang.dev) } app setup"
else:
!curl -Ls https://sh.jbang.dev | bash -s - app setup
os.environ["PATH"] = os.path.expanduser('~/.jbang/bin') + os.pathsep + os.environ["PATH"]
A Java program is created using the following command:
!jbang init -t cli src/hello.java
This generates the hello.java program:
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS info.picocli:picocli:4.6.3
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;
import java.util.concurrent.Callable;
@Command(name = "hello", mixinStandardHelpOptions = true, version = "hello 0.1",
description = "hello made with jbang")
class hello implements Callable<Integer> {
@Parameters(index = "0", description = "The greeting to print", defaultValue = "World!")
private String greeting;
public static void main(String... args) {
int exitCode = new CommandLine(new hello()).execute(args);
System.exit(exitCode);
}
@Override
public Integer call() throws Exception { // your business logic goes here...
System.out.println("Hello " + greeting);
return 0;
}
}
Similarly, the previous StringChecker.java is refactored:
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.apache.commons:commons-lang3:3.0
import org.apache.commons.lang3.StringUtils;
public class StringChecker {
public static void main(String[] args) {
String sampleString = "Hello, Apache Commons!";
// Check if the string is empty or null using Commons Lang
if (StringUtils.isNotEmpty(sampleString)) {
System.out.println("The string is not empty or null.");
} else {
System.out.println("The string is empty or null.");
}
}
}
Execution of the programs is accomplished with the following commands:
!jbang src/StringChecker.java
!jbang src/hello.java
Thank you for joining me on this Java-in-Jupyter journey. May your coding endeavors be seamless, exploratory, and filled with the joy of discovery. Happy coding!