Skip to content

Commit 11f33f8

Browse files
authored
Merge pull request #3 from wkorando/main
Adding instructions for using jlink for Youtube video
2 parents b6544bb + d37bdc3 commit 11f33f8

File tree

25 files changed

+423
-0
lines changed

25 files changed

+423
-0
lines changed

intro-to-jlink/LICENSE.txt

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Copyright (c) 2023 Oracle and/or its affiliates.
2+
3+
The Universal Permissive License (UPL), Version 1.0
4+
5+
Subject to the condition set forth below, permission is hereby granted to any
6+
person obtaining a copy of this software, associated documentation and/or data
7+
(collectively the "Software"), free of charge and under any and all copyright
8+
rights in the Software, and any and all patent rights owned or freely
9+
licensable by each licensor hereunder covering either (i) the unmodified
10+
Software as contributed to or provided by such licensor, or (ii) the Larger
11+
Works (as defined below), to deal in both
12+
13+
(a) the Software, and
14+
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
15+
one is included with the Software (each a "Larger Work" to which the Software
16+
is contributed by such licensors),
17+
18+
without restriction, including without limitation the rights to copy, create
19+
derivative works of, display, perform, and distribute the Software and make,
20+
use, sell, offer for sale, import, export, have made, and have sold the
21+
Software and the Larger Work(s), and to sublicense the foregoing rights on
22+
either these or other terms.
23+
24+
This license is subject to the following condition:
25+
The above copyright notice and either this complete permission notice or at
26+
a minimum a reference to the UPL must be included in all copies or
27+
substantial portions of the Software.
28+
29+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35+
SOFTWARE.

intro-to-jlink/README.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Intro to JLink
2+
3+
This repo provides an introduction to using some of the key functionality of `jlink` to create custom runtime images for running a Java application.
4+
5+
Be sure to check out the YouTube video for additional details: https://youtu.be/mJKlxqQQeyI
6+
7+
The repo is divided into five distinct parts;
8+
9+
## Step 1 Hello World with JLink
10+
We start by using the most basic functionality jlink, by creating a runtime image for a "Hello World!" program. [Link](step-1-hello-world-with-jlink)
11+
12+
## Step 2 Using JDeps
13+
Step 2 covers how to use JDeps to find the dependent modules for an application, and build a runtime image with those modules. [Link](step-2-using-jdeps)
14+
15+
## Step 3 Adding Non-JDK Modules to an Image
16+
In this step we look at how to add non-JDK modules, in this `org.apache.commons.lang3` to a runtime image. [Link](step-3-adding-non-jdk-modules)
17+
18+
## Step 4 Adding Explicit Modules to an Image
19+
During this step we walk-through the process of creating our own explicit module and adding it to a runtime image. [Link](step-4-adding-explicit-modules)
20+
21+
## Step 5 Creating a Launcher
22+
We create a launcher for our runtime image, which can be used to streamline the process of running an application. [Link](step-5-creating-a-launcher)
23+
24+
## Step 6 Additional Customization Options
25+
We look at a couple of the customization options; generating a CDS archive, and compressing the runtime image, `jlink` provides. [Link](step-6-additional-customization)
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Hello World with JLink
2+
3+
In this exercise we will create a simple custom runtime image with JLink that cna be used to run a Hello World program.
4+
5+
## The Program
6+
7+
The program is a standard "Hello World!", it is located in the `foo` package.
8+
9+
```java
10+
package foo;
11+
12+
public class HelloWorld{
13+
public static void main(String[] args){
14+
System.out.println("Hello JLink!");
15+
}
16+
}
17+
```
18+
19+
## Creating a Runtime Image with JLink
20+
21+
To create a runtime image with jlink that can run `HelloWorld` we will run the following command:
22+
23+
```
24+
$ jlink --add-modules java.base --output hello-jlink-image
25+
```
26+
27+
In the above command:
28+
29+
* `--add-modules`: defines the modules to be added to the image. `java.base` all the classes needed to execute `HelloWorld`
30+
31+
* `--output`: is the name of the resulting image produced by `jlink`. This can be arbitrary, but should be descriptive.
32+
33+
## Running an Application with a JLink Image
34+
35+
Running an application with a jlink image would be no different than with the JDK, just reference the `java` command located in the bin directory.
36+
37+
```
38+
$ ./hello-jlink-image/bin/java foo.HelloWorld
39+
```
40+
41+
Which should return:
42+
43+
```
44+
Hello JLink!
45+
```
46+
47+
[In the next exercise](../step-2-using-jdeps) we will look at using JDeps to find out which modules to include in an image.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package foo;
2+
3+
public class HelloWorld{
4+
public static void main(String[] args){
5+
System.out.println("Hello JLink!");
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Using JDeps
2+
3+
In this exercise, we will using the `jdeps` tool to resolve the modules we need to include in a runtime.
4+
5+
## The Program
6+
7+
This program is designed to call a url endpoint and print to the JDK logger the response it receives.
8+
9+
```java
10+
package foo;
11+
12+
import java.net.URI;
13+
import java.net.http.HttpClient;
14+
import java.net.http.HttpRequest;
15+
import java.net.http.HttpResponse;
16+
import java.net.http.HttpResponse.BodyHandlers;
17+
import java.time.Duration;
18+
import java.util.logging.Level;
19+
import java.util.logging.Logger;
20+
21+
public class GetWebsite {
22+
23+
private static final Logger LOGGER = Logger.getLogger(GetWebsite.class.getName());
24+
public static void main(String... args) throws InterruptedException {
25+
String url = args[0];
26+
try (HttpClient client = HttpClient.newHttpClient();) {
27+
LOGGER.log(Level.INFO, "Calling: " + url);
28+
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build();
29+
client.sendAsync(request, BodyHandlers.ofString())
30+
.thenApply(HttpResponse::body).thenAccept(r -> LOGGER.log(Level.INFO, r));
31+
client.awaitTermination(Duration.ofMillis(1000L));
32+
}
33+
}
34+
}
35+
```
36+
37+
## Resolving Module Dependencies with JDeps
38+
Attempting to run `GetWebsite` with the runtime we created in the previous exercise will fail, throwing `ClassNotFound` exceptions. This is because some of the classes referenced in `GetWebsite` are located outside the `java.base` module.
39+
40+
The `jdeps` tool can be used to find which modules `GetWebsite` depends upon, which can be done with:
41+
42+
```
43+
$jdeps GetWebsite.class
44+
```
45+
46+
Which will return this table:
47+
48+
```
49+
jdeps GetWebsite.class
50+
GetWebsite.class -> java.base
51+
GetWebsite.class -> java.logging
52+
GetWebsite.class -> java.net.http
53+
foo -> java.lang java.base
54+
foo -> java.lang.invoke java.base
55+
foo -> java.net java.base
56+
foo -> java.net.http java.net.http
57+
foo -> java.time java.base
58+
foo -> java.util.concurrent java.base
59+
foo -> java.util.function java.base
60+
foo -> java.util.logging java.logging
61+
```
62+
63+
The top of the table describes the modules the class depends on, the bottom portion shows how those values were calculated.
64+
65+
JDeps can also be configured to provide a more user friendly output with the option `--print-module-deps` which will return a comma-separated list of the modules:
66+
67+
```
68+
$ jdeps --print-module-deps GetWebsite.class
69+
java.base,java.logging,java.net.http
70+
```
71+
72+
This can then be copied into jlink to build runtime that can run `GetWebsite` like in this command:
73+
74+
```
75+
$ jlink --add-modules java.base,java.logging,java.net.http --output get-website-image
76+
```
77+
78+
This runtime can be used to successfully execute `GetWebsite`
79+
80+
```
81+
$ ./get-website-image/bin/java GetWebsite http://localhost:8000
82+
```
83+
84+
[In the next exercise](../step-3-adding-non-jdk-modules) we will look at how to add non-JDK modules to a runtime image created with JLink.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package foo;
2+
3+
import java.net.URI;
4+
import java.net.http.HttpClient;
5+
import java.net.http.HttpRequest;
6+
import java.net.http.HttpResponse;
7+
import java.net.http.HttpResponse.BodyHandlers;
8+
import java.time.Duration;
9+
import java.util.logging.Level;
10+
import java.util.logging.Logger;
11+
12+
public class GetWebsite {
13+
14+
private static final Logger LOGGER = Logger.getLogger(GetWebsite.class.getName());
15+
public static void main(String... args) throws InterruptedException {
16+
String url = args[0];
17+
try (HttpClient client = HttpClient.newHttpClient();) {
18+
LOGGER.log(Level.INFO, "Calling: " + url);
19+
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build();
20+
client.sendAsync(request, BodyHandlers.ofString())
21+
.thenApply(HttpResponse::body).thenAccept(r -> LOGGER.log(Level.INFO, r));
22+
client.awaitTermination(Duration.ofMillis(1000L));
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Adding Non-JDK Modules to a Runtime
2+
In this excerise we will walk through steps of adding a Non-JDK module to a runtime image built with JLink.
3+
4+
## The Program
5+
6+
This is a simple program that's using the `StringUtils.reverse()` located in the [apache commons lang3 library](https://mvnrepository.com/artifact/org.apache.commons/commons-lang3) to reverse a user-provided value.
7+
8+
💡 **Note:** You will need to download the library from maven central and place it in the `/libs` directory to complete this exercise.
9+
10+
```java
11+
package foo;
12+
13+
import org.apache.commons.lang3.StringUtils;
14+
15+
public class ReverseString{
16+
public static void main(String... args){
17+
String reversedString = StringUtils.reverse(args[0]);
18+
System.out.println(reversedString);
19+
}
20+
}
21+
```
22+
23+
## Using JDeps to Resolve external JDK Modules
24+
25+
While we already know that we need to bring in the `org.apache.commons.lang3` module, understanding how to look up this information is important.
26+
27+
Using `jdeps` we will need to use the `--module-path` option, and pass it the location of where the `apache-commons-lang3.jar` is located, which sould be the `libs` directory.
28+
29+
```
30+
$ jdeps --module-path libs --multi-release 21 foo/ReverseString.class
31+
```
32+
33+
This will return a table, like in the previous excerise.
34+
35+
```
36+
ReverseString.class -> java.base
37+
ReverseString.class -> org.apache.commons.lang3
38+
foo -> java.io java.base
39+
foo -> java.lang java.base
40+
foo -> org.apache.commons.lang3 org.apache.commons.lang3
41+
```
42+
43+
## Building a Runtime with a Non-JDK Module
44+
45+
Building a runtime with a Non-JDK module is similar to building a runtime like we have done in the previous exercise, we just need to include the path to where the additional modules are located, in this case `libs` like we did in the previous step using `jdeps`:
46+
47+
```
48+
$ jlink --module-path libs --add-modules java.base,org.apache.commons.lang3 --output reverse-string-image
49+
```
50+
51+
We can use this runtime, like in the previous exercises to run the `ReserveString` program:
52+
53+
```
54+
$ ./reverse-string-image/bin/java foo.ReverseString Java
55+
avaJ
56+
```
57+
58+
[In the next exercise](../step-4-adding-explicit-modules) we will walk through the steps of creating our own module and adding it to a runtime image.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package foo;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
5+
public class ReverseString{
6+
public static void main(String... args){
7+
String reversedString = StringUtils.reverse(args[0]);
8+
System.out.println(reversedString);
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Creating and Adding a Module to a Runtime
2+
Adding modules to a runtime image isn't restricted to JDK or other pre-existing modules, we can create our own! We will walk through those steps in this exercise.
3+
4+
## Creating a Module
5+
To create a module we will need to provide a `module-info.java`. `module-info.java` is a special type of Java file used for defining a module. In this file we will provide a name, and the modules our module depends on.
6+
7+
```java
8+
module foo {
9+
requires org.apache.commons.lang3;
10+
}
11+
```
12+
13+
💡 **Note:** `java.base` is implicitly required by allow modules, so does not need to be defined.
14+
15+
Next we can create a module, using the `jmod` tool with this command:
16+
17+
```
18+
$ jmod create --class-path . mods/foo.jmod
19+
```
20+
21+
After a few moments the module will be created, we can add it to a runtime image, just like we did in the previous exercise, just make sure to update `--module-path` to include `mods`.
22+
23+
```
24+
$ jlink --module-path libs:mods --add-modules java.base,org.apache.commons.lang3,foo --output reverse-string-image
25+
```
26+
27+
We can run the program like we did in the previous exercise:
28+
29+
```
30+
$ ./reverse-string-image/bin/java foo.ReverseString Java
31+
avaJ
32+
```
33+
34+
[In the next exercise](../step-5-creating-a-launcher) we will use JLink to create a launcher to streamline the process of running an application packaged a jlink artifact.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package foo;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
5+
public class ReverseString{
6+
public static void main(String... args){
7+
String reversedString = StringUtils.reverse(args[0]);
8+
System.out.println(reversedString);
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module foo {
2+
requires org.apache.commons.lang3;
3+
}
4+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Creating a Launcher with JLink
2+
For Java developers distributing an artifact to clients, streamlining the process of starting a running the application has many benefits. JLink provides the ability to include a launcher which can provide a mapping to the main class with a JLink image.
3+
4+
## Adding a Launcher to a JLink Image
5+
6+
The process of adding a launcher is simple. Just include the `--launcher` option and provide a name, and the module and fully qualified class-path to the main class like in this command:
7+
8+
```
9+
$ jlink --add-modules java.base,foo,org.apache.commons.lang3 --module-path mods:libs --launcher app=foo/foo.ReverseString --output reverse-string-image
10+
```
11+
12+
With the launcher added, we can simply reference the launcher `app` instead of main class:
13+
14+
```
15+
$ ./reverse-string-image/bin/app Java
16+
avaJ
17+
```
18+
19+
[In the next exercise](../step-6-additional-customization) we will look at a couple of options JLink provides for customizing a runtime.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package foo;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
5+
public class ReverseString{
6+
public static void main(String... args){
7+
String reversedString = StringUtils.reverse(args[0]);
8+
System.out.println(reversedString);
9+
}
10+
}
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module foo {
2+
requires org.apache.commons.lang3;
3+
}
4+

0 commit comments

Comments
 (0)