├── 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 | --------------------------------------------------------------------------------