Skip to content

Commit

Permalink
Move brotli native library handling to a feature
Browse files Browse the repository at this point in the history
Always registers the correct native library as the logic runs on the
same architecture we are targeting (even when using containers).

Related to quarkusio#43782
  • Loading branch information
zakkak committed Oct 11, 2024
1 parent aea9c27 commit f3b95db
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 74 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.quarkus.vertx.http.deployment;

import static io.quarkus.deployment.pkg.steps.GraalVM.Version.CURRENT;
import static io.quarkus.runtime.TemplateHtmlBuilder.adjustRoot;
import static io.quarkus.vertx.http.deployment.RequireBodyHandlerBuildItem.getBodyHandlerRequiredConditions;
import static io.quarkus.vertx.http.deployment.RouteBuildItem.RouteType.FRAMEWORK_ROUTE;
Expand Down Expand Up @@ -38,20 +37,16 @@
import io.quarkus.deployment.builditem.ExecutorBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LogCategoryBuildItem;
import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem;
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.ShutdownListenerBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourcePatternsBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
import io.quarkus.deployment.logging.LoggingDecorateBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageRunnerBuildItem;
import io.quarkus.deployment.pkg.steps.GraalVM;
import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
import io.quarkus.deployment.pkg.steps.NoopNativeImageBuildRunner;
import io.quarkus.devui.spi.buildtime.FooterLogBuildItem;
import io.quarkus.kubernetes.spi.KubernetesPortBuildItem;
import io.quarkus.netty.runtime.virtual.VirtualServerChannel;
Expand All @@ -62,7 +57,6 @@
import io.quarkus.runtime.logging.LogBuildTimeConfig;
import io.quarkus.runtime.shutdown.ShutdownConfig;
import io.quarkus.tls.TlsRegistryBuildItem;
import io.quarkus.utilities.OS;
import io.quarkus.vertx.core.deployment.CoreVertxBuildItem;
import io.quarkus.vertx.core.deployment.EventLoopCountBuildItem;
import io.quarkus.vertx.http.HttpServerOptionsCustomizer;
Expand All @@ -82,6 +76,7 @@
import io.quarkus.vertx.http.runtime.cors.CORSRecorder;
import io.quarkus.vertx.http.runtime.filters.Filter;
import io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter;
import io.quarkus.vertx.http.runtime.graal.Brotli4jFeature;
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;
import io.vertx.core.http.impl.Http1xServerRequest;
import io.vertx.core.impl.VertxImpl;
Expand Down Expand Up @@ -560,73 +555,12 @@ private static boolean isSslConfigured() {
return false;
}

/**
* Compressors, deals with adding brotli compression via Brotli4J JNI wrapper.
*/
@BuildStep(onlyIf = NativeOrNativeSourcesBuild.class)
void brotliResources(HttpBuildTimeConfig httpBuildTimeConfig,
BuildProducer<NativeImageResourcePatternsBuildItem> resources,
BuildProducer<RuntimeInitializedClassBuildItem> runtimeInitializedClasses,
NativeImageRunnerBuildItem nativeImageRunnerBuildItem) throws BuildException {

if (httpBuildTimeConfig.compressors.isPresent() &&
httpBuildTimeConfig.compressors.get().stream().anyMatch(s -> s.equalsIgnoreCase("br"))) {
final String arch = System.getProperty("os.arch");
final boolean amd64 = arch.matches("^(amd64|x64|x86_64)$");
final boolean aarch64 = "aarch64".equals(arch);
final String lib;
if (OS.determineOS() == OS.LINUX) {
if (amd64) {
lib = "linux-x86_64/libbrotli.so";
} else if (aarch64) {
lib = "linux-aarch64/libbrotli.so";
} else {
throw new BuildException("Brotli compressor: No library for linux-" + arch);
}
} else if (OS.determineOS() == OS.WINDOWS) {
if (amd64) {
lib = "windows-x86_64/brotli.dll";
} else if (aarch64) {
lib = "windows-aarch64/brotli.dll";
} else {
throw new BuildException("Brotli compressor: No library for windows-" + arch);
}
} else if (OS.determineOS() == OS.MAC) {
if (amd64) {
lib = "osx-x86_64/libbrotli.dylib";
} else if (aarch64) {
lib = "osx-aarch64/libbrotli.dylib";
} else {
throw new BuildException("Brotli compressor: No library for osx-" + arch);
}
} else {
throw new BuildException("Brotli compressor: Your platform is not supported.");
}

resources.produce(NativeImageResourcePatternsBuildItem.builder()
// We do have Brotli4J on classpath thanks to Vert.X -> Netty dependencies.
.includePattern("\\QMETA-INF/services/com.aayushatharva.brotli4j.service.BrotliNativeProvider\\E")
// Native library. We pick only the one relevant to our system.
.includePattern("\\Qlib/" + lib + "\\E")
.build());

// Static initializer tries to load the native library in Brotli4jLoader; must be done at runtime.
runtimeInitializedClasses
.produce(new RuntimeInitializedClassBuildItem("com.aayushatharva.brotli4j.Brotli4jLoader"));
final GraalVM.Version v;
if (nativeImageRunnerBuildItem.getBuildRunner() instanceof NoopNativeImageBuildRunner) {
v = CURRENT;
logger.warnf("native-image is not installed. " +
"Using the default %s version as a reference to build native-sources step.", v.getVersionAsString());
} else {
v = nativeImageRunnerBuildItem.getBuildRunner().getGraalVMVersion();
}
// Newer 23.1+ GraalVM/Mandrel does not need this explicitly marked for runtime init thanks
// to a different strategy: https://github.com/oracle/graal/blob/vm-23.1.0/substratevm/CHANGELOG.md?plain=1#L10
if (v.compareTo(GraalVM.Version.VERSION_23_1_0) <= 0) {
runtimeInitializedClasses
.produce(new RuntimeInitializedClassBuildItem("io.netty.handler.codec.compression.Brotli"));
}
@BuildStep
NativeImageFeatureBuildItem Brotli4jFeature(HttpBuildTimeConfig httpBuildTimeConfig) {
if (httpBuildTimeConfig.compressors.isPresent()
&& httpBuildTimeConfig.compressors.get().stream().anyMatch(s -> s.equalsIgnoreCase("br"))) {
return new NativeImageFeatureBuildItem(Brotli4jFeature.class.getName());
}
return null;
}
}
8 changes: 8 additions & 0 deletions extensions/vertx-http/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>nativeimage</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devtools-utilities</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.quarkus.vertx.http.runtime.graal;

import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
import org.graalvm.nativeimage.hosted.RuntimeResourceAccess;

import io.quarkus.runtime.graal.GraalVM;
import io.quarkus.utilities.OS;

public class Brotli4jFeature implements Feature {

@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
// TODO reuse Brotli4jLoader's code instead of reinventing the wheel?
final String arch = System.getProperty("os.arch");
final boolean amd64 = arch.matches("^(amd64|x64|x86_64)$");
final boolean aarch64 = "aarch64".equals(arch);
final String lib;
if (OS.determineOS() == OS.LINUX) {
if (amd64) {
lib = "linux-x86_64/libbrotli.so";
} else if (aarch64) {
lib = "linux-aarch64/libbrotli.so";
} else {
throw new IllegalStateException("Brotli compressor: No library for linux-" + arch);
}
} else if (OS.determineOS() == OS.WINDOWS) {
if (amd64) {
lib = "windows-x86_64/brotli.dll";
} else if (aarch64) {
lib = "windows-aarch64/brotli.dll";
} else {
throw new IllegalStateException("Brotli compressor: No library for windows-" + arch);
}
} else if (OS.determineOS() == OS.MAC) {
if (amd64) {
lib = "osx-x86_64/libbrotli.dylib";
} else if (aarch64) {
lib = "osx-aarch64/libbrotli.dylib";
} else {
throw new IllegalStateException("Brotli compressor: No library for osx-" + arch);
}
} else {
throw new IllegalStateException("Brotli compressor: Your platform is not supported.");
}

// We do have Brotli4J on classpath thanks to Vert.X -> Netty dependencies.
RuntimeResourceAccess.addResource(Brotli4jFeature.class.getModule(),
"META-INF/services/com.aayushatharva.brotli4j.service.BrotliNativeProvider");
// Register the Native library. We pick only the one relevant to our system.
RuntimeResourceAccess.addResource(Brotli4jFeature.class.getModule(), "lib/" + lib);

// Static initializer tries to load the native library in Brotli4jLoader; must be done at runtime.
RuntimeClassInitialization.initializeAtRunTime("com.aayushatharva.brotli4j.Brotli4jLoader");
final GraalVM.Version v = GraalVM.Version.getCurrent();
// Newer 23.1+ GraalVM/Mandrel does not need this explicitly marked for runtime init thanks
// to a different strategy: https://github.com/oracle/graal/blob/vm-23.1.0/substratevm/CHANGELOG.md?plain=1#L10
if (v.compareTo(GraalVM.Version.VERSION_23_1_0) <= 0) {
RuntimeClassInitialization.initializeAtRunTime("io.netty.handler.codec.compression.Brotli");
}
}

}

0 comments on commit f3b95db

Please sign in to comment.