Modern Java in Action

Developer Advocate

Java Team at Oracle

Let’s get started!

Crawling GitHub

Starting with a seed URL:

  1. connect to URL

  2. identify kind of page

  3. identify interesting section

  4. identify outgoing links

  5. for each link, start at 1.

Crawling GitHub

Then:

  1. print statistics

  2. print page list

  3. show pages on localhost

Gameplan

Domain model:

  • create with records and sealed interfaces

  • operate on with pattern matching

Fetching pages:

  • HTTP client to fetch from GitHub

  • virtual threads via structured concurrency

Gameplan

Present results:

  • format with text blocks and string templates

  • host with simple file server

(And modules for reliability.)

Code!

Review

What Java was That?!

JDK 23 EA with preview features!

Features that aren’t final on JDK 21:

  • unnamed patterns (22: JEP 456)

  • string templates (23?: JEP 465)

  • StructuredTaskScope

Domain Model

public sealed interface Page
		permits ErrorPage, SuccessfulPage {

	URI url();

}

Domain Model

public record GitHubPrPage(
	URI url, String content, Set<Page> links, int number)
	implements GitHubPage {

	public GitHubPrPage {
		// argument validation
	}

	public GitHubPrPage(
			URI url, String content, int number) {
		this(url, content, new HashSet<>(), number);
	}

	// `equals` and `hashcode` based on `url`

}

Operations

public static String pageName(Page page) {
	return switch (page) {
		case ErrorPage(var url, _)
			-> "💥 ERROR: " + url.getHost();
		case ExternalPage(var url, _)
			-> "💤 EXTERNAL: " + url.getHost();
		case GitHubIssuePage(_, _, _, int number)
			-> "🐈 ISSUE #" + number;
		case GitHubPrPage(_, _, _, int number)
			-> "🐙 PR #" + number;
	};
}

HTTP Client

// creation
var client = HttpClient.newHttpClient();

// use
var request = HttpRequest
	.newBuilder(url)
	.GET()
	.build();
return client
	.send(request, BodyHandlers.ofString())
	.body();

Structured Concurrency

try (var scope =
		new StructuredTaskScope.ShutdownOnFailure()) {
	var futurePages = links.stream()
		.map(link -> scope.fork(
			() -> createPage(link, depth)))
		.toList();

	scope.join();
	scope.throwIfFailed();

	return futurePages.stream()
		.map(Subtask::get)
		.collect(toSet());
} catch (ExecutionException ex) {
	// [...]
}

String Templates

var html = HTML."""
	<!DOCTYPE html>
	<html lang="en">
		<head>
			<meta charset="utf-8">
			<title>\{Pretty.pageName(rootPage)}</title>
			<link rel="stylesheet" href="style.css">
		</head>
		<body>
			<div class="container">
				\{pageTreeHtml(rootPage)}
			</div>
		</body>
	</html>
	""";

String Templates

public class Html {

	public static final Processor<Document, ...> HTML =
		new JsoupHtmlProcessor();

	private static class JsoupHtmlProcessor
			implements Processor<Document, ...> {

		@Override
		public Document process(StringTemplate template) {
			return Jsoup.parse(template.interpolate());
		}

	}

}

Simple File Server

SimpleFileServer.createFileServer(
		address,
		serverDir.toAbsolutePath(),
		OutputLevel.INFO)
	.start();

JPackage

Let’s watch Jose’s exploration…​

So long…​

37% off with
code fccparlog

bit.ly/the-jms

More

Slides at slides.nipafx.dev
⇜ Get my book!

Follow Nicolai

nipafx.dev
/nipafx

Follow Java

inside.java // dev.java
/java    //    /openjdk

Image Credits