Skip to content

Commit

Permalink
Use Matrix based CI builds for JPro Platform (#44)
Browse files Browse the repository at this point in the history
* Configure a matrix-based CI build that iterates over multiple platforms and Java versions

* Fix `CommandRunner` test for Windows platform

* Bind server port in local server implementation only when needed and not when the server is created

* Replace string concatenation with text blocks in AuthenticationServer tests

* Normalize line endings depending on the platform after reading the resource content

* Correct `javadoc` encoding for `jpro-mdfx` module

* Update changelog

* Run CI builds for Java versions `17` and `21` LTS and the latest `23`
  • Loading branch information
besidev authored Oct 1, 2024
1 parent 5aa38e6 commit 38b5fe8
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 58 deletions.
23 changes: 0 additions & 23 deletions .github/workflows/linux.yml

This file was deleted.

42 changes: 42 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI build

on: [push]

jobs:
builds:
name: '${{ matrix.os }} with Java ${{ matrix.jdk }}'
runs-on: ${{ matrix.os }}
strategy:
matrix:
jdk: [17, 21, 23]
os: [ubuntu-latest, windows-latest] #, macos-13]
fail-fast: false
max-parallel: 6
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Java ${{ matrix.jdk }}
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ matrix.jdk }}

- name: Compile
run: |
./gradlew jar
./gradlew example:jar
- name: Test
run: |
if [[ "$RUNNER_OS" == "Linux" ]]; then
export DISPLAY=:99.0 && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16;
fi
./gradlew -DciTest=true test
shell: bash

- name: Javadoc
run: |
./gradlew javadoc
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
#### Improvements
* Updated **JPro** to version `2024.3.3`.

#### Bugfixes
* Fixed the binding of the port in the local server implementation inside the `jpro-core` module to occur only when
necessary, rather than during server creation.

----------------------

### 0.4.1 (August 29, 2024)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# JPro Platform
![Build](https://github.com/jpro-one/jpro-platform/actions/workflows/linux.yml/badge.svg)
![Build](https://github.com/jpro-one/jpro-platform/actions/workflows/main.yml/badge.svg)
[![JPro supported](https://img.shields.io/badge/JPro-supported-brightgreen.svg)](https://www.jpro.one/)

The JPro Platform represents the foundation of cross-platform application development,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,27 @@ public void runAsync() throws IOException {
} else {
commandRunner.addArgs("ls", "build.gradle");
}
Process process = commandRunner.runAsync("ls");
Process process = commandRunner.runAsync("async-ls");
assertThat(process.getClass()).isAssignableTo(Process.class);
}

@Test
public void runAsyncWithMockDirectoryThrowsException() {
if (PlatformUtils.isWindows()) {
commandRunner.addArgs("cmd", "/c", "dir", "/b", "build.gradle");
assertThatThrownBy(() -> commandRunner.runAsync("async-cmd-dir", mockFile))
.hasMessageContaining("Cannot run program \"cmd\"")
.hasMessageContaining("CreateProcess error=267, The directory name is invalid")
.hasRootCauseMessage("CreateProcess error=267, The directory name is invalid")
.hasCauseInstanceOf(IOException.class);
} else {
commandRunner.addArgs("ls", "build.gradle");
assertThatThrownBy(() -> commandRunner.runAsync("async-ls", mockFile))
.hasMessageContaining("Cannot run program \"ls\"")
.hasMessageContaining("error=2, No such file or directory")
.hasRootCauseMessage("error=2, No such file or directory")
.hasCauseInstanceOf(IOException.class);
}
assertThatThrownBy(() -> commandRunner.runAsync("ls", mockFile))
.hasMessageContaining("Cannot run program \"ls\"")
.hasMessageContaining("error=2, No such file or directory")
.hasRootCauseMessage("error=2, No such file or directory")
.hasCauseInstanceOf(IOException.class);
}

@Test
Expand All @@ -102,7 +107,7 @@ public void runAsyncWithDirectoryAndOutput() throws IOException, InterruptedExce
} else {
commandRunner.addArgs("mkdir", "runner");
}
Process process = commandRunner.runAsync("dir", tempDir.toFile());
Process process = commandRunner.runAsync("async-dir-list", tempDir.toFile());
int result = process.waitFor();
assertThat(result).isEqualTo(0); // Successful execution
assertThat(commandRunner.getLastResponse()).isEqualTo("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -52,6 +53,8 @@ public final class HttpServerImpl implements HttpServer {
static final byte[] CRLF = "\r\n".getBytes();

private String uri;
private boolean isReusePortSupported;
private boolean isPortBound;

@Nullable
private final Stage stage;
Expand Down Expand Up @@ -116,11 +119,9 @@ public HttpServerImpl(@Nullable final Stage stage, @NotNull final HttpOptions op
thread = new Thread(this::run, "http-server-thread");
thread.setDaemon(true);

InetSocketAddress address = options.getHost() == null
? new InetSocketAddress(options.getPort()) // wildcard address
: new InetSocketAddress(options.getHost(), options.getPort());

serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);

final Set<SocketOption<?>> supportedOptions = serverSocketChannel.supportedOptions();
if (options.isReuseAddr()) {
if (supportedOptions.contains(StandardSocketOptions.SO_REUSEADDR)) {
Expand All @@ -132,26 +133,46 @@ public HttpServerImpl(@Nullable final Stage stage, @NotNull final HttpOptions op
if (options.isReusePort()) {
if (supportedOptions.contains(StandardSocketOptions.SO_REUSEPORT)) {
serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEPORT, options.isReusePort());
isReusePortSupported = true;
} else {
isReusePortSupported = false;
logger.warn("The 'SO_REUSEPORT' option is not supported on this platform.");
}
}
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(address, options.getAcceptLength());
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}

private byte[] getResourceAsBytes(@NotNull final String name) throws IOException {
try (InputStream is = HttpServer.class.getResourceAsStream(name)) {
if (is != null) {
return is.readAllBytes();
// Read all bytes from the input stream
byte[] bytes = is.readAllBytes();
// Convert bytes to a string, normalize line endings, and convert back to bytes
String content = new String(bytes, StandardCharsets.UTF_8);
String normalizedContent = content.replace("\r\n", "\n")
.replace("\r", "\n");
return normalizedContent.getBytes(StandardCharsets.UTF_8);
}
}
return SPACE;
}

@Override
public void start() {
if (!isReusePortSupported && isPortBound) {
// Reuse port is not supported, so we cannot bind the port again
return;
} else {
try {
final InetSocketAddress address = options.getHost() == null
? new InetSocketAddress(options.getPort()) // wildcard address
: new InetSocketAddress(options.getHost(), options.getPort());
serverSocketChannel.bind(address, options.getAcceptLength());
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
isPortBound = true;
} catch (IOException ex) {
throw new HttpServerException(ex);
}
}
thread.start();
connectionEventLoops.forEach(ConnectionEventLoop::start);
logger.info("Starting server on port: {}", getServerPort());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,20 @@ public void testHttpServer() throws IOException, InterruptedException {
assertEquals(HttpStatus.OK, HttpStatus.fromCode(httpResponse.statusCode()));
assertEquals(HttpMethod.GET, HttpMethod.valueOf(request.method()));
assertEquals(URI.create("http://localhost:8080/auth?foo&bar=HTTP/1.1"), request.uri());
assertEquals("<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta http-equiv=\"Content-Type\" content=\"text/html\" charset=\"UTF-8\">\n" +
" <title>Authentication</title>\n" +
"</head>\n" +
"<body>\n" +
" <div style=\"text-align: center; font-family: sans-serif; margin-top: 20px;\">\n" +
" <h3>Authentication Successful</h3>\n" +
" <i>Please close the page.</i>\n" +
" </div>\n" +
"</body>\n" +
"</html>", httpResponse.body());
assertEquals("""
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html" charset="UTF-8">
<title>Authentication</title>
</head>
<body>
<div style="text-align: center; font-family: sans-serif; margin-top: 20px;">
<h3>Authentication Successful</h3>
<i>Please close the page.</i>
</div>
</body>
</html>""", httpResponse.body());
assertEquals("{ {content-length=[350], content-type=[text/html]} }",
"{ " + httpResponse.headers().map() + " }");
assertEquals("localhost", httpServer.getServerHost());
Expand Down
9 changes: 9 additions & 0 deletions jpro-mail/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,13 @@ dependencies {
api "org.eclipse.collections:eclipse-collections-api:$ECLIPSE_COLLECTIONS_VERSION"
implementation "org.eclipse.collections:eclipse-collections:$ECLIPSE_COLLECTIONS_VERSION"
api "org.slf4j:slf4j-api:$SLF4J_API_VERSION"
}

javadoc {
options {
encoding = 'UTF-8'
version = true
author = true
// addStringOption('Xdoclint:none', '-quiet')
}
}
21 changes: 15 additions & 6 deletions jpro-mdfx/build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
plugins {
id 'org.javamodularity.moduleplugin' version "$MODULE_PLUGIN_VERSION"
id 'org.javamodularity.moduleplugin' version "$MODULE_PLUGIN_VERSION"
}

dependencies {
implementation project(':jpro-youtube')
implementation "com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:$FLEXMARK_VERSION"
implementation "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:$FLEXMARK_VERSION"
implementation "com.vladsch.flexmark:flexmark-ext-tables:$FLEXMARK_VERSION"
implementation "com.vladsch.flexmark:flexmark-ext-attributes:$FLEXMARK_VERSION"
implementation project(':jpro-youtube')
implementation "com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:$FLEXMARK_VERSION"
implementation "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:$FLEXMARK_VERSION"
implementation "com.vladsch.flexmark:flexmark-ext-tables:$FLEXMARK_VERSION"
implementation "com.vladsch.flexmark:flexmark-ext-attributes:$FLEXMARK_VERSION"
}

javadoc {
options {
encoding = 'UTF-8'
version = true
author = true
// addStringOption('Xdoclint:none', '-quiet')
}
}

0 comments on commit 38b5fe8

Please sign in to comment.