diff --git a/core/pom.xml b/core/pom.xml
index 6200adc2b..d82584356 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -40,6 +40,10 @@
org.kohsuke
akuma
+
+ net.java.dev.jna
+ jna
+
io.github.classgraph
classgraph
diff --git a/core/src/main/java/org/restheart/graal/PluginsClassloaderInitFeature.java b/core/src/main/java/org/restheart/graal/PluginsClassloaderInitFeature.java
new file mode 100644
index 000000000..126ecf8a3
--- /dev/null
+++ b/core/src/main/java/org/restheart/graal/PluginsClassloaderInitFeature.java
@@ -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 .
+ * =========================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";
+ }
+}
diff --git a/core/src/main/java/org/restheart/graal/PluginsReflectionRegistrationFeature.java b/core/src/main/java/org/restheart/graal/PluginsReflectionRegistrationFeature.java
index acb003ef6..2adeee455 100644
--- a/core/src/main/java/org/restheart/graal/PluginsReflectionRegistrationFeature.java
+++ b/core/src/main/java/org/restheart/graal/PluginsReflectionRegistrationFeature.java
@@ -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)
diff --git a/core/src/main/java/org/restheart/plugins/PluginsClassloader.java b/core/src/main/java/org/restheart/plugins/PluginsClassloader.java
index ff0e5d0e6..ac5737210 100644
--- a/core/src/main/java/org/restheart/plugins/PluginsClassloader.java
+++ b/core/src/main/java/org/restheart/plugins/PluginsClassloader.java
@@ -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.
@@ -52,6 +57,26 @@ public static void init(URL[] jars) {
}
}
+ public static void init(List 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;
}
diff --git a/core/src/main/java/org/restheart/plugins/PluginsScanner.java b/core/src/main/java/org/restheart/plugins/PluginsScanner.java
index c1bd38adf..30b4ec764 100644
--- a/core/src/main/java/org/restheart/plugins/PluginsScanner.java
+++ b/core/src/main/java/org/restheart/plugins/PluginsScanner.java
@@ -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;
@@ -82,40 +81,10 @@ public class PluginsScanner {
private static final ArrayList SERVICES = new ArrayList<>();
private static final ArrayList 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
@@ -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));
@@ -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 allPluginsClassNames() {
diff --git a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/jni-config.json b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/jni-config.json
similarity index 100%
rename from core/src/main/resources/META-INF/native-image/org.restheart/restheart/jni-config.json
rename to core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/jni-config.json
diff --git a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/native-image.properties b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/native-image.properties
similarity index 93%
rename from core/src/main/resources/META-INF/native-image/org.restheart/restheart/native-image.properties
rename to core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/native-image.properties
index 2c3307c0c..848aeb895 100644
--- a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/native-image.properties
+++ b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/native-image.properties
@@ -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
diff --git a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/predefined-classes-config.json b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/predefined-classes-config.json
similarity index 100%
rename from core/src/main/resources/META-INF/native-image/org.restheart/restheart/predefined-classes-config.json
rename to core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/predefined-classes-config.json
diff --git a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/proxy-config.json b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/proxy-config.json
similarity index 100%
rename from core/src/main/resources/META-INF/native-image/org.restheart/restheart/proxy-config.json
rename to core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/proxy-config.json
diff --git a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/reflect-config.json b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/reflect-config.json
similarity index 100%
rename from core/src/main/resources/META-INF/native-image/org.restheart/restheart/reflect-config.json
rename to core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/reflect-config.json
diff --git a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/resource-config.json b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/resource-config.json
similarity index 98%
rename from core/src/main/resources/META-INF/native-image/org.restheart/restheart/resource-config.json
rename to core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/resource-config.json
index 8e144f828..b7963536a 100644
--- a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/resource-config.json
+++ b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/resource-config.json
@@ -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"
diff --git a/core/src/main/resources/META-INF/native-image/org.restheart/restheart/serialization-config.json b/core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/serialization-config.json
similarity index 100%
rename from core/src/main/resources/META-INF/native-image/org.restheart/restheart/serialization-config.json
rename to core/src/main/resources/META-INF/native-image/org.restheart/restheart-core/serialization-config.json