Skip to content

Commit

Permalink
✨ Fix Native Image Build Breakage Due to Transition from Fat Jar to r…
Browse files Browse the repository at this point in the history
…estheart-core Packaging #520
  • Loading branch information
ujibang committed Aug 7, 2024
1 parent 9f4d1ba commit 354021a
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 41 deletions.
4 changes: 4 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
<groupId>org.kohsuke</groupId>
<artifactId>akuma</artifactId>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
</dependency>
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*-
* ========================LICENSE_START=================================
* restheart-core
* %%
* Copyright (C) 2014 - 2024 SoftInstigate
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* =========================LICENSE_END==================================
*/
/** WIP
* Automate reflect configuratio currently done via GenerateGraalvmReflectConfig
*
*/

package org.restheart.graal;

import org.graalvm.nativeimage.hosted.Feature;
import org.restheart.plugins.PluginsClassloader;
import org.restheart.plugins.PluginsScanner;

/**
* Initializes PluginsClassloader with the native image classpath and triggers PluginsScanner
*
*/
public class PluginsClassloaderInitFeature implements Feature {
@Override
public void afterRegistration(AfterRegistrationAccess access) {
// System.out.println("***** afterRegistration " + access.getApplicationClassPath().toString());
// initialize PluginsClassloader with the native image classpath
PluginsClassloader.init(access.getApplicationClassPath());
// initialize PluginScanner class
PluginsScanner.initAtBuildTime();
}

@Override
public String getDescription() {
return "Initializes PluginsClassloader with the native image classpath and triggers PluginsScanner";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@
public class PluginsReflectionRegistrationFeature implements Feature {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
System.out.println("[PluginsReflectionRegistrationFeature] configuring reflection for:");
if (PluginsScanner.allPluginsClassNames().isEmpty()) {
System.err.println("[PluginsReflectionRegistrationFeature] Error: no plugins found in classpath. This indicates a build misconfiguration, as at least the plugins in 'restheart-core' should be detected.");
throw new IllegalStateException("Error: No plugins found in classpath.");
} else {
System.out.println("[PluginsReflectionRegistrationFeature] configuring reflection for:");
}

PluginsScanner.allPluginsClassNames().stream()
.map(this::clazz)
Expand Down
25 changes: 25 additions & 0 deletions core/src/main/java/org/restheart/plugins/PluginsClassloader.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@
package org.restheart.plugins;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.List;

import org.restheart.utils.LambdaUtils;

/**
* Loads a class, including searching within all plugin JAR files.
Expand Down Expand Up @@ -52,6 +57,26 @@ public static void init(URL[] jars) {
}
}

public static void init(List<Path> paths) {
if (SINGLETON != null) {
throw new IllegalStateException("already initialized");
} else {
var urls = paths.stream().map(p -> {
try {
return p.toUri().toURL();
} catch(MalformedURLException murle) {
LambdaUtils.throwsSneakyException(murle);
return null;
}
}).toArray(size -> new URL[size]);
try {
SINGLETON = new PluginsClassloader(urls);
} catch(IOException ioe) {
throw new RuntimeException("error initializing", ioe);
}
}
}

public static boolean isInitialized() {
return SINGLETON != null;
}
Expand Down
78 changes: 40 additions & 38 deletions core/src/main/java/org/restheart/plugins/PluginsScanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -82,40 +81,10 @@ public class PluginsScanner {
private static final ArrayList<PluginDescriptor> SERVICES = new ArrayList<>();
private static final ArrayList<PluginDescriptor> PROVIDERS = new ArrayList<>();

// ClassGraph.scan() at class initialization time to support native image
// generation with GraalVM
// see https://github.com/SoftInstigate/classgraph-on-graalvm
static {
ClassGraph classGraph;
RuntimeClassGraph rtcg = null;

if (ImageInfo.inImageBuildtimeCode()) {
// initizialize PluginsClassloader with the restheart jar
var jarPath = PluginsScanner.class.getProtectionDomain().getCodeSource().getLocation().getPath();
try {
var jarFile = new File(jarPath);
var jarURL = jarFile.toURI().toURL();
URL[] urls = { jarURL };

PluginsClassloader.init(urls);
} catch(MalformedURLException mue) {
System.err.println("Error initilizing PluginsClassloader on restheart uber jar " + jarPath + ". Exception: " + mue.getMessage());
}

final var cg = new ClassGraph();

classGraph = cg
.disableDirScanning() // added for GraalVM
.disableNestedJarScanning() // added for GraalVM
.disableRuntimeInvisibleAnnotations() // added for GraalVM
.overrideClassLoaders(PluginsClassloader.getInstance()) // added for GraalVM. Mandatory, otherwise build fails
.ignoreParentClassLoaders()
.enableAnnotationInfo().enableMethodInfo().enableFieldInfo().ignoreFieldVisibility().initializeLoadedClasses();

System.out.println("[PluginsScanner] Scanning plugins at build time with following classpath: " + cg.getClasspathURIs().stream().map(uri -> uri.getPath()).collect(Collectors.joining(File.pathSeparator)));
} else {
rtcg = new RuntimeClassGraph();
classGraph = rtcg.get();
if (!ImageInfo.inImageBuildtimeCode()) {
var rtcg = new RuntimeClassGraph();
var classGraph = rtcg.get();
// apply plugins-scanning-verbose configuration option
classGraph = classGraph.verbose(Bootstrapper.getConfiguration().coreModule().pluginsScanningVerbose());
// apply plugins-packages configuration option
Expand All @@ -125,7 +94,44 @@ public class PluginsScanner {
}

rtcg.logStartScan();

try (var scanResult = classGraph.scan(Runtime.getRuntime().availableProcessors())) {
INITIALIZERS.addAll(collectPlugins(scanResult, INITIALIZER_CLASS_NAME));
AUTH_MECHANISMS.addAll(collectPlugins(scanResult, AUTHMECHANISM_CLASS_NAME));
AUTHORIZERS.addAll(collectPlugins(scanResult, AUTHORIZER_CLASS_NAME));
TOKEN_MANAGERS.addAll(collectPlugins(scanResult, TOKEN_MANAGER_CLASS_NAME));
AUTHENTICATORS.addAll(collectPlugins(scanResult, AUTHENTICATOR_CLASS_NAME));
INTERCEPTORS.addAll(collectPlugins(scanResult, INTERCEPTOR_CLASS_NAME));
SERVICES.addAll(collectPlugins(scanResult, SERVICE_CLASS_NAME));
PROVIDERS.addAll(collectProviders(scanResult));
}

rtcg.logEndScan();
}
}

// ClassGraph.scan() at class initialization time to support native image
// generation with GraalVM
// see https://github.com/SoftInstigate/classgraph-on-graalvm
public static void initAtBuildTime() {
if (!ImageInfo.inImageBuildtimeCode()) {
throw new IllegalStateException("Called initAtBuildTime() but we are not at build time");
}

// requires PluginsClassloader being initialized
// this is done by PluginsClassloaderInitFeature

final var cg = new ClassGraph();

var classGraph = cg
.disableDirScanning() // added for GraalVM
.disableNestedJarScanning() // added for GraalVM
.disableRuntimeInvisibleAnnotations() // added for GraalVM
.overrideClassLoaders(PluginsClassloader.getInstance()) // added for GraalVM. Mandatory, otherwise build fails
.ignoreParentClassLoaders()
.enableAnnotationInfo().enableMethodInfo().enableFieldInfo().ignoreFieldVisibility().initializeLoadedClasses();

System.out.println("[PluginsScanner] Scanning plugins at build time with following classpath: " + cg.getClasspathURIs().stream().map(uri -> uri.getPath()).collect(Collectors.joining(File.pathSeparator)));

try (var scanResult = classGraph.scan(Runtime.getRuntime().availableProcessors())) {
INITIALIZERS.addAll(collectPlugins(scanResult, INITIALIZER_CLASS_NAME));
Expand All @@ -137,10 +143,6 @@ public class PluginsScanner {
SERVICES.addAll(collectPlugins(scanResult, SERVICE_CLASS_NAME));
PROVIDERS.addAll(collectProviders(scanResult));
}

if (rtcg != null) {
rtcg.logEndScan();
}
}

public static List<String> allPluginsClassNames() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Args = --initialize-at-build-time=org.restheart.plugins.PluginsScanner,io.github
--report-unsupported-elements-at-runtime \
--no-fallback \
--install-exit-handlers \
--features=org.restheart.graal.PluginsReflectionRegistrationFeature \
--features=org.restheart.graal.PluginsReflectionRegistrationFeature,org.restheart.graal.PluginsClassloaderInitFeature \
--add-exports=java.net.http/jdk.internal.net.http=org.graalvm.truffle \
--add-modules=org.graalvm.polyglot \
-march=native
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"pattern":"\\Qlogback.xml\\E"
},
{
"pattern":"\\Qlrestheart-default-config.json\\E"
"pattern":"\\Qrestheart-default-config.json\\E"
},
{
"pattern":"\\Qorg/apache/tika/mime/tika-mimetypes.xml\\E"
Expand Down

0 comments on commit 354021a

Please sign in to comment.