Skip to content

Instantly share code, notes, and snippets.

@zikani03
Created September 14, 2020 19:31
Show Gist options
  • Save zikani03/cced2357a32c8bcab444465c2ef7a78e to your computer and use it in GitHub Desktop.
Save zikani03/cced2357a32c8bcab444465c2ef7a78e to your computer and use it in GitHub Desktop.
SparkJava powered by Project Loom

This is a simple experiment using a build of Java 16 to create a simple Spark Java application that's backed by Project Loom's Virtual Threads as Jetty's ThreadPool implementation. It is based on prior art

What does it do?

It's basically a web server whose routes/endpoints are specified with Spark to serve an HTML page and some JavaScript - written inline using Text Blocks!.

What's needed to run it?

You will need the following dependencies, assuming you use Maven:

<properties>
    <jetty.version>9.4.31.v20200723</jetty.version>
    <spark.version>2.9.2</spark.version>
    <java.version>16</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
  <dependency>
      <groupId>com.sparkjava</groupId>
      <artifactId>spark-core</artifactId>
      <version>${spark.version}</version>
  </dependency>
  <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-server</artifactId>
      <version>${jetty.version}</version>
  </dependency>
</dependencies>
package example;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.thread.ThreadPool;
import spark.Spark;
import spark.embeddedserver.EmbeddedServers;
import spark.embeddedserver.jetty.EmbeddedJettyFactory;
import spark.embeddedserver.jetty.JettyServerFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SparkWithLoom {
public static void main(String... args) {
// We specify the host and address because Spark#ipAddress and Spark#port
// won't set the host or port in this implementation right now, subject to further research...
EmbeddedJettyFactory factory = createEmbeddedFactory("localhost", 4567);
EmbeddedServers.add(EmbeddedServers.Identifiers.JETTY, factory);
Spark.redirect.any("/", "index.html");
Spark.get("/index.html", (request, response) -> {
response.type("text/html;charset=utf-8");
String htmlPage = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello SparkJava + Project Loom Virtual Threads</title>
</head>
<body>
<div>
<h1 id="awesome-header">Awesome</h1>
<p>This is an HTML page served by SparkJava (Jetty) using Project Loom's Virtual Threads</p>
<p id="hidden-message" style="display: none">
Maybe, you didn't notice but text block was made visible by JavaScript which is being served via <code>/js/generated.js</code> endpoint
and implemented with the following code:
<code><pre>
Spark.get("/js/generated.js", (request, response) -> {
response.type("application/javascript");
return ""\"
document.addEventListener("DOMContentLoaded", (event) => {
console.log("Some JavaScript generated by the server!");
let awEl = document.getElementById("awesome-header");
let hiddenEl = document.getElementById("hidden-message");
awEl.innerHTML = "Java is Awesome!";
hiddenEl.style = "display: block;";
});
""\";
});
</pre></code>
</div>
<script type="text/javascript" src="/js/generated.js"></script>
</body>
</html>
""";
return htmlPage;
});
Spark.get("/js/generated.js", (request, response) -> {
response.type("application/javascript");
return """
document.addEventListener("DOMContentLoaded", (event) => {
console.log("Some JavaScript generated by the server!");
let awEl = document.getElementById("awesome-header");
let hiddenEl = document.getElementById("hidden-message");
awEl.innerHTML = "Java is Awesome!";
hiddenEl.style = "display: block;";
});
""";
});
}
public static EmbeddedJettyFactory createEmbeddedFactory(String ipAddress, int port) {
return new EmbeddedJettyFactory(new JettyServerFactory() {
@Override
public Server create(int i, int i1, int i2) {
var server = new Server(new LoomThreadPool());
ServerConnector connector = new ServerConnector(server);
connector.setHost(ipAddress);
connector.setPort(port);
server.setConnectors(new Connector[]{connector});
// server.setRequestLog(requestLog);
return server;
}
@Override
public Server create(ThreadPool threadPool) {
return create(1, 1, 1);
}
});
}
// Copied from: https://github.com/rodrigovedovato/jetty-loom
public static class LoomThreadPool implements ThreadPool {
ExecutorService executorService = Executors.newVirtualThreadExecutor();
@Override
public void join() throws InterruptedException {
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
@Override
public int getThreads() {
return 1;
}
@Override
public int getIdleThreads() {
return 1;
}
@Override
public boolean isLowOnThreads() {
return false;
}
@Override
public void execute(Runnable command) {
executorService.submit(command);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment