├── LICENSE.txt
├── README.md
├── SECURITY.md
├── countdown-problem-java21
├── CountDownProblem.java
└── SETUP.md
└── intro-to-jlink
├── LICENSE.txt
├── README.md
├── step-1-hello-world-with-jlink
├── README.md
└── foo
│ ├── HelloWorld.class
│ └── HelloWorld.java
├── step-2-using-jdeps
├── README.md
└── foo
│ ├── GetWebsite.class
│ └── GetWebsite.java
├── step-3-adding-non-jdk-modules
├── README.md
└── foo
│ ├── ReverseString.class
│ └── ReverseString.java
├── step-4-adding-explicit-modules
├── README.md
├── foo
│ ├── ReverseString.class
│ └── ReverseString.java
└── module-info.java
├── step-5-creating-a-launcher
├── README.md
├── foo
│ ├── ReverseString.class
│ └── ReverseString.java
├── module-info.class
└── module-info.java
└── step-6-additional-customization
├── README.md
├── foo
├── ReverseString.class
└── ReverseString.java
├── module-info.class
└── module-info.java
/LICENSE.txt:
--------------------------------------------------------------------------------
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.
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Samples
2 |
3 | This repository contains sample code in order for developers to reproduce complex examples shared on https://inside.java or https://dev.java.
4 |
5 | ## Installation
6 |
7 | Each sample directory contains its own instructions (SETUP.md) on how to run the example(s).
8 |
9 | ## Content
10 |
11 | * [CountDownProblem.java](countdown-problem-java21/CountDownProblem.java) published on https://inside.java/2023/11/03/countdown-haskell-java
12 |
13 |
14 | ## Feedback
15 |
16 | If you spot an error, or you know of an alternative way/an extra question/solution that you can offer, please raise an issue on https://github.com/java/devrel.
17 |
18 | ## Security
19 |
20 | Please consult the [security guide](./SECURITY.md) for our responsible security vulnerability disclosure process
21 |
22 | ## License
23 |
24 | Copyright (c) 2023 Oracle and/or its affiliates.
25 | Released under the Universal Permissive License v1.0 as shown at
26 | .
27 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting security vulnerabilities
2 |
3 | Oracle values the independent security research community and believes that
4 | responsible disclosure of security vulnerabilities helps us ensure the security
5 | and privacy of all our users.
6 |
7 | Please do NOT raise a GitHub Issue to report a security vulnerability. If you
8 | believe you have found a security vulnerability, please submit a report to
9 | [secalert_us@oracle.com][1] preferably with a proof of concept. Please review
10 | some additional information on [how to report security vulnerabilities to Oracle][2].
11 | We encourage people who contact Oracle Security to use email encryption using
12 | [our encryption key][3].
13 |
14 | We ask that you do not use other channels or contact the project maintainers
15 | directly.
16 |
17 | Non-vulnerability related security issues including ideas for new or improved
18 | security features are welcome on GitHub Issues.
19 |
20 | ## Security updates, alerts and bulletins
21 |
22 | Security updates will be released on a regular cadence. Many of our projects
23 | will typically release security fixes in conjunction with the
24 | Oracle Critical Patch Update program. Additional
25 | information, including past advisories, is available on our [security alerts][4]
26 | page.
27 |
28 | ## Security-related information
29 |
30 | We will provide security related information such as a threat model, considerations
31 | for secure use, or any known security issues in our documentation. Please note
32 | that labs and sample code are intended to demonstrate a concept and may not be
33 | sufficiently hardened for production use.
34 |
35 | [1]: mailto:secalert_us@oracle.com
36 | [2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html
37 | [3]: https://www.oracle.com/security-alerts/encryptionkey.html
38 | [4]: https://www.oracle.com/security-alerts/
39 |
--------------------------------------------------------------------------------
/countdown-problem-java21/CountDownProblem.java:
--------------------------------------------------------------------------------
1 | import java.util.ArrayList;
2 | import java.util.List;
3 | import java.util.OptionalInt;
4 | import java.util.Set;
5 | import java.util.stream.Stream;
6 |
7 | /*
8 | * This program is Java port of the Haskell example at
9 | * https://www.cs.nott.ac.uk/~pszgmh/pgp-countdown.hs
10 | *
11 | * The problem and the solution approaches are explained
12 | * in Prof. Graham Hutton's youtube video at
13 | * https://youtu.be/CiXDS3bBBUo?list=PLF1Z-APd9zK7usPMx3LGMZEHrECUGodd3
14 | *
15 | * This Java program requires JDK 21+
16 | */
17 | class CountDownProblem {
18 |
19 | // data Op = Add | Sub | Mul | Div
20 | enum Op {
21 | Add, Sub, Mul, Div;
22 |
23 | // instance show Op
24 | @Override
25 | public String toString() {
26 | return switch (this) {
27 | case Add -> "+";
28 | case Sub -> "-";
29 | case Mul -> "*";
30 | case Div -> "/";
31 | };
32 | }
33 | }
34 |
35 | // cache enum value array
36 | static final Op[] operators = Op.values();
37 |
38 | // valid' :: Op -> Int -> Int -> Bool
39 | static boolean isValid(Op op, int x, int y) {
40 | return switch (op) {
41 | case Add -> x <= y;
42 | case Sub -> x > y;
43 | case Mul -> x != 1 && y != 1 && x <= y;
44 | case Div -> y != 1 && x % y == 0;
45 | };
46 | }
47 |
48 | // apply :: Op -> Int -> Int -> Int
49 | static int apply(Op op, int x, int y) {
50 | return switch (op) {
51 | case Add -> x + y;
52 | case Sub -> x - y;
53 | case Mul -> x * y;
54 | case Div -> x / y;
55 | };
56 | }
57 |
58 | // data Expr = Val Int | App Op Expr Expr
59 | sealed interface Expr {
60 | // brak helper for instance Show Expr
61 | static String brak(Expr expr) {
62 | return switch (expr) {
63 | // brak (Val n) = show n
64 | case Val(var n) -> Integer.toString(n);
65 |
66 | // brak e = "(" ++ show e ++ ")"
67 | default -> "(" + toStr(expr) + ")";
68 | };
69 | }
70 |
71 | // instance Show Expr
72 | static String toStr(Expr expr) {
73 | return switch (expr) {
74 | // show (Val n) = show n
75 | case Val(var n) -> Integer.toString(n);
76 |
77 | // show (App o l r) = brak l ++ show o ++ brak r
78 | // where
79 | // brak (Val n) = show n
80 | // brak e = "(" ++ show e ++ ")"
81 | case App(var op, var l, var r) -> brak(l) + op + brak(r);
82 | };
83 | }
84 | }
85 |
86 | record Val(int v) implements Expr {
87 | // instance Show Expr
88 | @Override
89 | public String toString() {
90 | return Expr.toStr(this);
91 | }
92 | }
93 |
94 | record App(Op op, Expr l, Expr r) implements Expr {
95 | // instance Show Expr
96 | @Override
97 | public String toString() {
98 | return Expr.toStr(this);
99 | }
100 | }
101 |
102 | // eval :: Expr -> [Int]
103 | // Using OptionalInt instead of List
104 | static OptionalInt eval(Expr expr) {
105 | return switch (expr) {
106 | // eval (Val n) = [n | n > 0]
107 | case Val(var n) -> n > 0 ? OptionalInt.of(n) : OptionalInt.empty();
108 |
109 |
110 | // eval (App o l r) = [apply o x y | x <- eval l,
111 | // y <- eval r,
112 | // valid o x y]
113 | case App(var op, var l, var r) -> {
114 | var x = eval(l);
115 | var y = eval(r);
116 | yield (x.isPresent() && y.isPresent() &&
117 | isValid(op, x.getAsInt(), y.getAsInt())) ?
118 | OptionalInt.of(apply(op, x.getAsInt(), y.getAsInt())) :
119 | OptionalInt.empty();
120 | }
121 | };
122 | }
123 |
124 | // type Result = (Expr,Int)
125 | record Result(Expr expr, int value) {
126 | @Override
127 | public String toString() {
128 | return expr.toString() + " = " + value;
129 | }
130 | }
131 |
132 | // combine'' :: Result -> Result -> [Result]
133 | static List combine(Result lx, Result ry) {
134 | // (l,x), (r,y) pattern
135 | var l = lx.expr();
136 | var x = lx.value();
137 | var r = ry.expr();
138 | var y = ry.value();
139 |
140 | // combine'' (l,x) (r,y) = [(App o l r, apply o x y) | o <- ops, valid' o x y]
141 | return Stream.of(operators).
142 | filter(op -> isValid(op, x, y)).
143 | map(op -> new Result(new App(op, l, r), apply(op, x, y))).
144 | toList();
145 | }
146 |
147 | // results' :: [Int] -> [Result]
148 | static List results(List ns) {
149 | // results' [] = []
150 | if (ns.isEmpty()) {
151 | return List.of();
152 | }
153 |
154 | // results' [n] = [(Val n,n) | n > 0]
155 | if (ns.size() == 1) {
156 | var n = head(ns);
157 | return n > 0 ? List.of(new Result(new Val(n), n)) : List.of();
158 | }
159 |
160 | // results' ns = [res | (ls,rs) <- split ns,
161 | // lx <- results' ls,
162 | // ry <- results' rs,
163 | // res <- combine'' lx ry]
164 | var res = new ArrayList();
165 |
166 | // all possible non-empty splits of the input list
167 | // split :: [a] -> [([a],[a])] equivalent for-loop
168 | for (int i = 1; i < ns.size(); i++) {
169 | var ls = ns.subList(0, i);
170 | var rs = ns.subList(i, ns.size());
171 | var lxs = results(ls);
172 | var rys = results(rs);
173 | for (Result lx : lxs) {
174 | for (Result ry : rys) {
175 | res.addAll(combine(lx, ry));
176 | }
177 | }
178 | }
179 | return res;
180 | }
181 |
182 | // List utilities
183 | // : operator
184 | static List cons(T head, List tail) {
185 | final var tailLen = tail.size();
186 | return switch (tailLen) {
187 | case 0 -> List.of(head);
188 | case 1 -> List.of(head, tail.get(0));
189 | case 2 -> List.of(head, tail.get(0), tail.get(1));
190 | case 3 -> List.of(head, tail.get(0), tail.get(1), tail.get(2));
191 | default -> {
192 | var res = new ArrayList(1 + tailLen);
193 | res.add(head);
194 | res.addAll(tail);
195 | yield res;
196 | }
197 | };
198 | }
199 |
200 | static T head(List list) {
201 | return list.get(0);
202 | }
203 |
204 | static List tail(List list) {
205 | final var len = list.size();
206 | return len == 1 ? List.of() : list.subList(1, len);
207 | }
208 |
209 | // subs :: [a] -> [[a]]
210 | static List> subs(List ns) {
211 | // subs [] = [[]]
212 | if (ns.isEmpty()) {
213 | return List.of(List.of());
214 | }
215 |
216 | // subs (x:xs)
217 | var x = head(ns);
218 | var xs = tail(ns);
219 |
220 | // where yss = sub(xs)
221 | var yss = subs(xs);
222 |
223 | // yss ++ map (x:) yss
224 | var res = new ArrayList>();
225 | res.addAll(yss);
226 | yss.stream().
227 | map(l -> cons(x, l)).
228 | forEach(res::add);
229 |
230 | return res;
231 | }
232 |
233 | // interleave :: a -> [a] -> [[a]]
234 | // Using Stream instead of List>
235 | static Stream> interleave(int x, List ns) {
236 | // interleave x [] = [[x]]
237 | if (ns.isEmpty()) {
238 | return Stream.of(List.of(x));
239 | }
240 |
241 | // interleave x (y:ys)
242 | var y = head(ns);
243 | var ys = tail(ns);
244 |
245 | // outer : translated as Stream.concat
246 | // (x:y:ys) : map (y:) (interleave x ys)
247 | return Stream.concat(
248 | // x:y:ys == x:ns
249 | Stream.of(cons(x, ns)),
250 | // map (y:) (interleave x ys)
251 | interleave(x, ys).map(l -> cons(y, l))
252 | );
253 | }
254 |
255 | // perms :: [a] -> [[a]]
256 | // Using Stream instead of List>
257 | static Stream> perms(List ns) {
258 | // perms [] = [[]]
259 | if (ns.isEmpty()) {
260 | return Stream.of(List.of());
261 | }
262 |
263 | // perms (x:xs)
264 | var x = head(ns);
265 | var xs = tail(ns);
266 |
267 | // concat (map ...) is translated as flatMap
268 | // concat (map (interleave x) (perms xs))
269 | return perms(xs).flatMap(l -> interleave(x, l));
270 | }
271 |
272 | // choices :: [a] -> [[a]]
273 | // Using Stream instead of List>
274 | static Stream> choices(List ns) {
275 | // concat . map is translated as flatMap
276 | // choices = concat . map perms . subs
277 | return subs(ns).stream().flatMap(CountDownProblem::perms);
278 | }
279 |
280 | // solutions'' :: [Int] -> Int -> [Expr]
281 | // Using Stream instead of List
282 | static Stream solutions(List ns, int n) {
283 | // solutions'' ns n = [e | ns' <- choices ns, (e,m) <- results' ns', m == n]
284 | return choices(ns).
285 | flatMap(choice -> results(choice).stream()).
286 | filter(res -> res.value() == n).
287 | map(Result::expr);
288 | }
289 |
290 | /*
291 | * usage example:
292 | *
293 | * java CountDownProblem.java 1,3,7,10,25,50 765
294 | */
295 | public static void main(String[] args) {
296 | if (args.length != 2) {
297 | System.err.println("usage: java CountDownProblem.java ");
298 | System.exit(1);
299 | }
300 |
301 | int target = Integer.parseInt(args[1]);
302 | List numbers = Stream.of(args[0].split(",")).map(Integer::parseInt).toList();
303 | // uniqueness check
304 | try {
305 | Set.of(numbers.toArray());
306 | } catch (IllegalArgumentException iae) {
307 | System.err.println(iae);
308 | System.exit(2);
309 | }
310 |
311 | var start = System.currentTimeMillis();
312 | solutions(numbers, target).forEach(e -> {
313 | System.out.println(e);
314 | });
315 | System.out.println("Time taken (ms): " + (System.currentTimeMillis() - start));
316 | }
317 | }
318 |
--------------------------------------------------------------------------------
/countdown-problem-java21/SETUP.md:
--------------------------------------------------------------------------------
1 | # Countdown Problem
2 |
3 | [CountDownProblem.java](CountDownProblem.java) is the complete example used in the article https://inside.java/2023/11/03/countdown-haskell-java.
4 | The code represents the Java port of [a Haskell example](https://www.cs.nott.ac.uk/~pszgmh/pgp-countdown.hs) authored by Prof. Graham Hutton.
5 | The problem and the solution approaches of the Haskell example are explained in https://youtu.be/CiXDS3bBBUo?list=PLF1Z-APd9zK7usPMx3LGMZEHrECUGodd3.
6 |
7 | ## Prerequisites
8 |
9 | You need JDK 21+ installed on your machine to run this sample.
10 | Find out how to achieve that by going through the [setting up a JDK section](https://dev.java/learn/getting-started/#setting-up-jdk) of https://dev.java/.
11 |
12 | ## Run the progam
13 |
14 | Make sure you are inside the _countdown-haskell-java_ directory and then run the following command in a terminal:
15 |
16 | ```shell
17 | java CountDownProblem.java 1,3,7,10,25,50 765
18 | ```
19 |
20 | You may try different arguments to check for different results.
21 |
--------------------------------------------------------------------------------
/intro-to-jlink/LICENSE.txt:
--------------------------------------------------------------------------------
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:
--------------------------------------------------------------------------------
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 |
27 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-1-hello-world-with-jlink/README.md:
--------------------------------------------------------------------------------
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.
--------------------------------------------------------------------------------
/intro-to-jlink/step-1-hello-world-with-jlink/foo/HelloWorld.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/java/samples/11f33f8c4bcdc2fa9930807be353762d03727d70/intro-to-jlink/step-1-hello-world-with-jlink/foo/HelloWorld.class
--------------------------------------------------------------------------------
/intro-to-jlink/step-1-hello-world-with-jlink/foo/HelloWorld.java:
--------------------------------------------------------------------------------
1 | package foo;
2 |
3 | public class HelloWorld{
4 | public static void main(String[] args){
5 | System.out.println("Hello JLink!");
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-2-using-jdeps/README.md:
--------------------------------------------------------------------------------
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.
--------------------------------------------------------------------------------
/intro-to-jlink/step-2-using-jdeps/foo/GetWebsite.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/java/samples/11f33f8c4bcdc2fa9930807be353762d03727d70/intro-to-jlink/step-2-using-jdeps/foo/GetWebsite.class
--------------------------------------------------------------------------------
/intro-to-jlink/step-2-using-jdeps/foo/GetWebsite.java:
--------------------------------------------------------------------------------
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 | }
26 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-3-adding-non-jdk-modules/README.md:
--------------------------------------------------------------------------------
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.
59 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-3-adding-non-jdk-modules/foo/ReverseString.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/java/samples/11f33f8c4bcdc2fa9930807be353762d03727d70/intro-to-jlink/step-3-adding-non-jdk-modules/foo/ReverseString.class
--------------------------------------------------------------------------------
/intro-to-jlink/step-3-adding-non-jdk-modules/foo/ReverseString.java:
--------------------------------------------------------------------------------
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 | }
11 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-4-adding-explicit-modules/README.md:
--------------------------------------------------------------------------------
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.
--------------------------------------------------------------------------------
/intro-to-jlink/step-4-adding-explicit-modules/foo/ReverseString.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/java/samples/11f33f8c4bcdc2fa9930807be353762d03727d70/intro-to-jlink/step-4-adding-explicit-modules/foo/ReverseString.class
--------------------------------------------------------------------------------
/intro-to-jlink/step-4-adding-explicit-modules/foo/ReverseString.java:
--------------------------------------------------------------------------------
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 | }
11 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-4-adding-explicit-modules/module-info.java:
--------------------------------------------------------------------------------
1 | module foo {
2 | requires org.apache.commons.lang3;
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-5-creating-a-launcher/README.md:
--------------------------------------------------------------------------------
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.
--------------------------------------------------------------------------------
/intro-to-jlink/step-5-creating-a-launcher/foo/ReverseString.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/java/samples/11f33f8c4bcdc2fa9930807be353762d03727d70/intro-to-jlink/step-5-creating-a-launcher/foo/ReverseString.class
--------------------------------------------------------------------------------
/intro-to-jlink/step-5-creating-a-launcher/foo/ReverseString.java:
--------------------------------------------------------------------------------
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 | }
11 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-5-creating-a-launcher/module-info.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/java/samples/11f33f8c4bcdc2fa9930807be353762d03727d70/intro-to-jlink/step-5-creating-a-launcher/module-info.class
--------------------------------------------------------------------------------
/intro-to-jlink/step-5-creating-a-launcher/module-info.java:
--------------------------------------------------------------------------------
1 | module foo {
2 | requires org.apache.commons.lang3;
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-6-additional-customization/README.md:
--------------------------------------------------------------------------------
1 | # Customizing a Runtime with JLink
2 |
3 | JLink provides a number of options for customizing the images it creates. Most of them are provide through plugins. To see a full-list of plugins supported by JLink, use the `--list-plugins` option.
4 |
5 | In this exercise we will look at two plugins, `--generate-cds-archive` and `--compress`.
6 |
7 | ## Generating a CDS Archive
8 |
9 | CDS is a feature provided in the JDK the can improve startup performance by building a pre-processed archive of the class the JVM will load on startup. You can learn more about CDS by watching this video.
10 |
11 | JLink can create a CDS archive with the `--generate-cds-archive` option.
12 |
13 | ```
14 | jlink --module-path libs:mods --add-modules java.base,org.apache.commons.lang3,foo --launcher app=foo/foo.ReverseString --generate-cds-archive --output reverse-string-image-cds
15 | ```
16 |
17 | This will provide a modest improvement to the execution time for the program.
18 |
19 | ## Compressing a Runtime
20 |
21 | JLink can also apply compression to an image with the `--compress` option. There are ten levels of compression `zip-[0-9]`, the default is `zip-6`. If we were to provide `zip-9` like in this command:
22 |
23 | ```
24 | jlink --module-path libs:mods --add-modules java.base,org.apache.commons.lang3,foo --launcher app=foo/foo.ReverseString --compress zip-9 --output reverse-string-image-compressed
25 | ```
26 |
27 | We should see a pretty significant reduction in image size:
28 |
29 | ```
30 | Size with zip-9: ~173 MB
31 | Size with zip-6: ~831 MB
32 | ```
33 |
34 | Though this does come with a modest reduction in application performance from the additional compression.
35 |
36 |
37 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-6-additional-customization/foo/ReverseString.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/java/samples/11f33f8c4bcdc2fa9930807be353762d03727d70/intro-to-jlink/step-6-additional-customization/foo/ReverseString.class
--------------------------------------------------------------------------------
/intro-to-jlink/step-6-additional-customization/foo/ReverseString.java:
--------------------------------------------------------------------------------
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 | }
11 |
--------------------------------------------------------------------------------
/intro-to-jlink/step-6-additional-customization/module-info.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/java/samples/11f33f8c4bcdc2fa9930807be353762d03727d70/intro-to-jlink/step-6-additional-customization/module-info.class
--------------------------------------------------------------------------------
/intro-to-jlink/step-6-additional-customization/module-info.java:
--------------------------------------------------------------------------------
1 | module foo {
2 | requires org.apache.commons.lang3;
3 | }
4 |
5 |
--------------------------------------------------------------------------------