From 17e5d6e349ae0c99ef15a874f05d49115622b854 Mon Sep 17 00:00:00 2001 From: Maurizio Turatti Date: Tue, 19 Nov 2024 19:33:08 +0100 Subject: [PATCH] Fix PluginsScanner errors on Windows --- .../org/restheart/plugins/PluginsScanner.java | 384 ++++++++++-------- 1 file changed, 216 insertions(+), 168 deletions(-) diff --git a/core/src/main/java/org/restheart/plugins/PluginsScanner.java b/core/src/main/java/org/restheart/plugins/PluginsScanner.java index ab673908d..f3e5fe78c 100644 --- a/core/src/main/java/org/restheart/plugins/PluginsScanner.java +++ b/core/src/main/java/org/restheart/plugins/PluginsScanner.java @@ -1,52 +1,43 @@ /*- - * ========================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================================== - */ +* ========================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================================== +*/ package org.restheart.plugins; -import io.github.classgraph.AnnotationEnumValue; -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ClassInfo; -import io.github.classgraph.ClassInfoList; -import io.github.classgraph.ScanResult; - import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream.Filter; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.AbstractMap; - -import org.restheart.graal.ImageInfo; import org.restheart.Bootstrapper; +import org.restheart.graal.ImageInfo; import org.restheart.plugins.security.AuthMechanism; import org.restheart.plugins.security.Authenticator; import org.restheart.plugins.security.Authorizer; @@ -54,6 +45,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.github.classgraph.AnnotationEnumValue; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassInfoList; +import io.github.classgraph.ScanResult; + /** * this class is configured to be initialized at build time by native-image * note: we cannot use logging in this class, otherwise native-image will fail @@ -83,12 +80,12 @@ public class PluginsScanner { static { if (!ImageInfo.inImageBuildtimeCode()) { - var rtcg = new RuntimeClassGraph(); + final 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 - var pluginsPackages = Bootstrapper.getConfiguration().coreModule().pluginsPackages(); + final var pluginsPackages = Bootstrapper.getConfiguration().coreModule().pluginsPackages(); if (!Bootstrapper.getConfiguration().coreModule().pluginsPackages().isEmpty()) { classGraph = classGraph.acceptPackages(pluginsPackages.toArray(String[]::new)); } @@ -114,24 +111,27 @@ public class PluginsScanner { // generation with GraalVM // see https://github.com/SoftInstigate/classgraph-on-graalvm public static void initAtBuildTime() { - if (!ImageInfo.inImageBuildtimeCode()) { + 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(); + final 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))); + 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)); @@ -146,7 +146,7 @@ public static void initAtBuildTime() { } public static List allPluginsClassNames() { - var ret = new ArrayList(); + final var ret = new ArrayList(); INITIALIZERS.stream().map(p -> p.clazz()).forEachOrdered(ret::add); AUTH_MECHANISMS.stream().map(p -> p.clazz()).forEachOrdered(ret::add); AUTHORIZERS.stream().map(p -> p.clazz()).forEachOrdered(ret::add); @@ -194,10 +194,10 @@ static final List services() { /** * @param type the class of the plugin , e.g. Initializer.class */ - private static List collectPlugins(ScanResult scanResult, String className) { - var ret = new ArrayList(); + private static List collectPlugins(final ScanResult scanResult, final String className) { + final var ret = new ArrayList(); - var registeredPlugins = scanResult.getClassesWithAnnotation(REGISTER_PLUGIN_CLASS_NAME); + final var registeredPlugins = scanResult.getClassesWithAnnotation(REGISTER_PLUGIN_CLASS_NAME); if (registeredPlugins == null || registeredPlugins.isEmpty()) { return ret; @@ -206,25 +206,25 @@ private static List collectPlugins(ScanResult scanResult, Stri ClassInfoList listOfType; if (className.equals(AUTHENTICATOR_CLASS_NAME)) { - var tms = scanResult.getClassesImplementing(TOKEN_MANAGER_CLASS_NAME); + final var tms = scanResult.getClassesImplementing(TOKEN_MANAGER_CLASS_NAME); listOfType = scanResult.getClassesImplementing(className).exclude(tms); } else { listOfType = scanResult.getClassesImplementing(className); } - var plugins = registeredPlugins.intersect(listOfType); + final var plugins = registeredPlugins.intersect(listOfType); return plugins.stream().map(c -> descriptor(c)).collect(Collectors.toList()); } /** - * - */ - private static List collectProviders(ScanResult scanResult) { - var ret = new ArrayList(); + * + */ + private static List collectProviders(final ScanResult scanResult) { + final var ret = new ArrayList(); - var providers = scanResult.getClassesImplementing(PROVIDER_CLASS_NAME); + final var providers = scanResult.getClassesImplementing(PROVIDER_CLASS_NAME); if (providers == null || providers.isEmpty()) { return ret; @@ -233,16 +233,16 @@ private static List collectProviders(ScanResult scanResult) { return providers.stream().map(c -> descriptor(c)).collect(Collectors.toList()); } - private static PluginDescriptor descriptor(ClassInfo pluginClassInfo) { - var clazz = pluginClassInfo.getName(); - var name = pluginClassInfo.getAnnotationInfo(REGISTER_PLUGIN_CLASS_NAME).getParameterValues().stream() + private static PluginDescriptor descriptor(final ClassInfo pluginClassInfo) { + final var clazz = pluginClassInfo.getName(); + final var name = pluginClassInfo.getAnnotationInfo(REGISTER_PLUGIN_CLASS_NAME).getParameterValues().stream() .filter(p -> "name".equals(p.getName())).map(p -> p.getValue()).findAny().get().toString(); return new PluginDescriptor(name, clazz, isEnabled(name, pluginClassInfo), collectInjections(pluginClassInfo)); } - private static ArrayList collectInjections(ClassInfo pluginClassInfo) { - var ret = new ArrayList(); + private static ArrayList collectInjections(final ClassInfo pluginClassInfo) { + final var ret = new ArrayList(); ret.addAll(collectFieldInjections(pluginClassInfo, Inject.class)); ret.addAll(collectMethodInjections(pluginClassInfo, OnInit.class)); @@ -256,69 +256,76 @@ private static ArrayList collectInjections(ClassInfo plugin * * @param name * @param pluginClassInfo - * @return true if the plugin is enabled, taking into account enabledByDefault and its configuration + * @return true if the plugin is enabled, taking into account enabledByDefault + * and its configuration */ - private static boolean isEnabled(String name, ClassInfo pluginClassInfo) { + private static boolean isEnabled(final String name, final ClassInfo pluginClassInfo) { if (ImageInfo.inImageBuildtimeCode()) { return true; } else { - var isEnabledByDefault = (boolean) pluginClassInfo.getAnnotationInfo(REGISTER_PLUGIN_CLASS_NAME).getParameterValues().stream() - .filter(p -> "enabledByDefault".equals(p.getName())).map(p -> p.getValue()).findAny().get(); + final var isEnabledByDefault = (boolean) pluginClassInfo.getAnnotationInfo(REGISTER_PLUGIN_CLASS_NAME) + .getParameterValues().stream() + .filter(p -> "enabledByDefault".equals(p.getName())).map(p -> p.getValue()).findAny().get(); - Map confArgs = Bootstrapper.getConfiguration().getOrDefault(name, null); + final Map confArgs = Bootstrapper.getConfiguration().getOrDefault(name, null); return PluginRecord.isEnabled(isEnabledByDefault, confArgs); } } - private static ArrayList collectMethodInjections(ClassInfo pluginClassInfo, Class clazz) { - var ret = new ArrayList(); + private static ArrayList collectMethodInjections(final ClassInfo pluginClassInfo, + final Class clazz) { + final var ret = new ArrayList(); - var mil = pluginClassInfo.getDeclaredMethodInfo(); + final var mil = pluginClassInfo.getDeclaredMethodInfo(); - for (var mi : mil) { + for (final var mi : mil) { if (mi.hasAnnotation(clazz.getName())) { - ArrayList> annotationParams = new ArrayList<>(); - for (var p : mi.getAnnotationInfo(clazz.getName()).getParameterValues()) { - var value = p.getValue(); - if (value instanceof AnnotationEnumValue annotationEnumValue) { + final ArrayList> annotationParams = new ArrayList<>(); + for (final var p : mi.getAnnotationInfo(clazz.getName()).getParameterValues()) { + final var value = p.getValue(); + if (value instanceof final AnnotationEnumValue annotationEnumValue) { removeRefToScanResult(annotationEnumValue); } annotationParams.add(new AbstractMap.SimpleEntry<>(p.getName(), value)); } - var methodParams = new ArrayList(); + final var methodParams = new ArrayList(); - Arrays.stream(mi.getParameterInfo()).forEachOrdered(pi -> methodParams.add(pi.getTypeDescriptor().toString())); + Arrays.stream(mi.getParameterInfo()) + .forEachOrdered(pi -> methodParams.add(pi.getTypeDescriptor().toString())); - ret.add(new MethodInjectionDescriptor(mi.getName(), clazz, annotationParams, methodParams, mi.hashCode())); + ret.add(new MethodInjectionDescriptor(mi.getName(), clazz, annotationParams, methodParams, + mi.hashCode())); } } return ret; } - private static ArrayList collectFieldInjections(ClassInfo pluginClassInfo, Class clazz) { - var ret = new ArrayList(); + private static ArrayList collectFieldInjections(final ClassInfo pluginClassInfo, + final Class clazz) { + final var ret = new ArrayList(); - var fil = pluginClassInfo.getDeclaredFieldInfo(); + final var fil = pluginClassInfo.getDeclaredFieldInfo(); - for (var fi : fil) { + for (final var fi : fil) { if (fi.hasAnnotation(clazz.getName())) { - var annotationParams = new ArrayList>(); - for (var p : fi.getAnnotationInfo(clazz.getName()).getParameterValues()) { - var value = p.getValue(); - if (value instanceof AnnotationEnumValue annotationEnumValue) { + final var annotationParams = new ArrayList>(); + for (final var p : fi.getAnnotationInfo(clazz.getName()).getParameterValues()) { + final var value = p.getValue(); + if (value instanceof final AnnotationEnumValue annotationEnumValue) { removeRefToScanResult(annotationEnumValue); } annotationParams.add(new AbstractMap.SimpleEntry<>(p.getName(), value)); } try { - var fieldClass = PluginsClassloader.getInstance().loadClass(fi.getTypeDescriptor().toString()); + final var fieldClass = PluginsClassloader.getInstance() + .loadClass(fi.getTypeDescriptor().toString()); ret.add(new FieldInjectionDescriptor(fi.getName(), fieldClass, annotationParams, fi.hashCode())); - } catch(ClassNotFoundException cnfe) { + } catch (final ClassNotFoundException cnfe) { // should not happen - throw new RuntimeException(cnfe); + throw new IllegalStateException(cnfe); } } } @@ -332,9 +339,9 @@ private static ArrayList collectFieldInjections(ClassInfo p * * @param obj */ - private static void removeRefToScanResult(AnnotationEnumValue obj) { + private static void removeRefToScanResult(final AnnotationEnumValue obj) { try { - var f = AnnotationEnumValue.class.getSuperclass().getDeclaredField("scanResult"); + final var f = AnnotationEnumValue.class.getSuperclass().getDeclaredField("scanResult"); f.setAccessible(true); f.set(obj, null); } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException ex) { @@ -350,35 +357,49 @@ static class RuntimeClassGraph { URL[] jars = null; public RuntimeClassGraph() { - var pdir = getPluginsDirectory(); + final var pdir = getPluginsDirectory(); this.jars = findPluginsJars(pdir); if (!PluginsClassloader.isInitialized()) { PluginsClassloader.init(this.jars); } - var libJars = Arrays.stream(this.jars) - .map(jar -> jar.getPath()) - .map(path -> Path.of(path)) - .filter(jar -> isLibJar(jar)) - .map(path -> path.getFileName().toString()) - .toArray(String[]::new); + final var libJars = Arrays.stream(this.jars) + .map(jar -> { + try { + URI uri = jar.toURI(); + + // Correct malformed URIs by ensuring a leading "/" in the path + if ("file".equalsIgnoreCase(uri.getScheme()) && uri.getAuthority() == null + && !uri.getPath().startsWith("/")) { + uri = new URI("file", null, "/" + uri.getPath(), null); + } + + // Convert the corrected URI to a Path + return Paths.get(uri); + } catch (final Exception e) { + LOGGER.error("Error processing JAR URL: {}", jar, e); + throw new IllegalStateException("Invalid JAR URL: " + jar, e); + } + }) + .filter(this::isLibJar) + .map(path -> path.getFileName().toString()) + .toArray(String[]::new); this.classGraph = new ClassGraph().disableModuleScanning().disableDirScanning() - .disableNestedJarScanning() - .disableRuntimeInvisibleAnnotations() - .addClassLoader(PluginsClassloader.getInstance()) - .addClassLoader(ClassLoader.getSystemClassLoader()) - .rejectJars(libJars) // avoids scanning lib jars - .enableAnnotationInfo() - .enableMethodInfo() - .enableFieldInfo() - .ignoreFieldVisibility() - .initializeLoadedClasses(); + .disableNestedJarScanning() + .disableRuntimeInvisibleAnnotations() + .addClassLoader(PluginsClassloader.getInstance()) + .addClassLoader(ClassLoader.getSystemClassLoader()) + .rejectJars(libJars) // avoids scanning lib jars + .enableAnnotationInfo() + .enableMethodInfo() + .enableFieldInfo() + .ignoreFieldVisibility() + .initializeLoadedClasses(); } private long starScanTime = 0; - private long endScanTime = 0; public void logStartScan() { LOGGER.info("Scanning jars for plugins started"); @@ -386,8 +407,7 @@ public void logStartScan() { } public void logEndScan() { - this.endScanTime = System.currentTimeMillis(); - LOGGER.info("Scanning jars for plugins completed in {} msec", endScanTime-starScanTime); + LOGGER.info("Scanning jars for plugins completed in {} msec", System.currentTimeMillis() - starScanTime); } public ClassGraph get() { @@ -395,37 +415,46 @@ public ClassGraph get() { } public static Path getPluginsDirectory() { - var pluginsDir = Bootstrapper.getConfiguration().coreModule().pluginsDirectory(); + final String pluginsDir = Bootstrapper.getConfiguration().coreModule().pluginsDirectory(); if (pluginsDir == null) { return null; } - var pluginsPath = Path.of(pluginsDir); + final Path pluginsPath = Path.of(pluginsDir); if (pluginsPath.isAbsolute()) { - return Paths.get(pluginsDir); - } else { - // this is to allow specifying the plugins directory path - // relative to the jar (also working when running from classes) - var location = PluginsFactory.class.getProtectionDomain().getCodeSource().getLocation(); + return pluginsPath; + } - try { - return Path.of(URLDecoder.decode(location.getPath(), StandardCharsets.UTF_8.toString())) // url -> path - .getParent() - .resolve(pluginsPath); - } catch(UnsupportedEncodingException uee) { - throw new RuntimeException(uee); + try { + final URL location = PluginsFactory.class.getProtectionDomain().getCodeSource().getLocation(); + URI locationUri; + + // Handle Windows paths correctly + if (location.getProtocol().equals("file")) { + String path = location.getPath(); + // Remove leading slash from Windows paths + if (path.matches("^/[A-Za-z]:/.*")) { + path = path.substring(1); + } + locationUri = new File(path).toURI(); + } else { + locationUri = location.toURI(); } + + return Path.of(locationUri).getParent().resolve(pluginsPath); + } catch (final URISyntaxException e) { + throw new IllegalStateException("Failed to resolve plugins directory", e); } } - private URL[] findPluginsJars(Path pluginsDirectory) { + private URL[] findPluginsJars(final Path pluginsDirectory) { return _findPluginsJars(pluginsDirectory, 0); } - private URL[] _findPluginsJars(Path dir, int depth) { - var pluginsPackages = Bootstrapper.getConfiguration().coreModule().pluginsPackages(); + private URL[] _findPluginsJars(final Path dir, final int depth) { + final var pluginsPackages = Bootstrapper.getConfiguration().coreModule().pluginsPackages(); if (!pluginsPackages.isEmpty()) { LOGGER.info("Limiting the scanning of plugins to packages {}", pluginsPackages); } @@ -434,59 +463,63 @@ private URL[] _findPluginsJars(Path dir, int depth) { } else { try { checkPluginDirectory(dir); - } catch(IllegalStateException ise) { + } catch (final IllegalStateException ise) { return new URL[0]; } } - var urls = new ArrayList(); + final var urls = new ArrayList(); try (var ds = Files.newDirectoryStream(dir, "*.jar")) { - for (Path path : ds) { - var jar = path.toUri().toURL(); - - if (!Files.isReadable(path)) { - LOGGER.error("Plugin jar {} is not readable", jar); - throw new IllegalStateException("Plugin jar " + jar + " is not readable"); - } + for (final Path path : ds) { + try { + // Convert to File first to handle Windows paths correctly + final URL jar = path.toFile().toURI().toURL(); + + if (!Files.isReadable(path)) { + LOGGER.error("Plugin jar {} is not readable", jar); + throw new IllegalStateException("Plugin jar " + jar + " is not readable"); + } - urls.add(jar); + urls.add(jar); - if (isLibJar(path)) { - LOGGER.debug("Found lib jar {}", URLDecoder.decode(jar.getPath(), StandardCharsets.UTF_8.toString())); - } else { - LOGGER.info("Found plugin jar {}", URLDecoder.decode(jar.getPath(), StandardCharsets.UTF_8.toString())); + if (isLibJar(path)) { + LOGGER.debug("Found lib jar {}", path.toString()); + } else { + LOGGER.info("Found plugin jar {}", path.toString()); + } + } catch (final Exception e) { + LOGGER.error("Error processing jar file: {}", path, e); } } - } catch (IOException ex) { - LOGGER.error("Cannot read jars in plugins directory {}", Bootstrapper.getConfiguration().coreModule().pluginsDirectory(), ex.getMessage()); + } catch (final IOException ex) { + LOGGER.error("Cannot read jars in plugins directory {}", + Bootstrapper.getConfiguration().coreModule().pluginsDirectory(), ex); } // Scans the plugins directory up to two levels deep if (depth < 2) { - try (var ds = Files.newDirectoryStream(dir, (Filter) (Path entry) -> Files.isDirectory(entry))) { - for (Path subdir : ds) { + try (var ds = Files.newDirectoryStream(dir, (Filter) Files::isDirectory)) { + for (final Path subdir : ds) { if (Files.isReadable(subdir)) { - var subjars = _findPluginsJars(subdir, depth + 1); + final var subjars = _findPluginsJars(subdir, depth + 1); if (subjars != null && subjars.length > 0) { - Arrays.stream(subjars).forEach(jar -> urls.add(jar)); + urls.addAll(Arrays.asList(subjars)); } } else { - LOGGER.warn("Subdirectory {} of plugins directory {} is not readable", subdir, Bootstrapper.getConfiguration().coreModule().pluginsDirectory()); + LOGGER.warn("Subdirectory {} of plugins directory {} is not readable", + subdir, Bootstrapper.getConfiguration().coreModule().pluginsDirectory()); } } - } catch (IOException ex) { - LOGGER.error("Cannot read jars in plugins subdirectory", ex.getMessage()); + } catch (final IOException ex) { + LOGGER.error("Cannot read jars in plugins subdirectory", ex); } } return urls.toArray(URL[]::new); } - /** - * - */ - private void checkPluginDirectory(Path pluginsDirectory) { + private void checkPluginDirectory(final Path pluginsDirectory) { if (!Files.exists(pluginsDirectory)) { LOGGER.warn("Plugin directory {} does not exist", pluginsDirectory); throw new IllegalStateException("Plugins directory " + pluginsDirectory + " does not exist"); @@ -500,18 +533,21 @@ private void checkPluginDirectory(Path pluginsDirectory) { /** * Determines whether the given JAR file is classified as a library. - * A JAR is considered a library if it is located within a subdirectory of the plugins direcory - * whose relative path contains "lib", "-lib", or "_lib" as part of the directory name. + * A JAR is considered a library if it is located within a subdirectory of the + * plugins direcory + * whose relative path contains "lib", "-lib", or "_lib" as part of the + * directory name. * * @param path the path of the JAR file to be checked - * @return {@code true} if the JAR file is under a subdirectory of the plugins directory that contains "lib", "-lib", + * @return {@code true} if the JAR file is under a subdirectory of the plugins + * directory that contains "lib", "-lib", * or "_lib" in its relative path; {@code false} otherwise */ - private boolean isLibJar(Path path) { - var pluginsDirectory = getPluginsDirectory(); + private boolean isLibJar(final Path path) { + final var pluginsDirectory = getPluginsDirectory(); try { - var rpath = pluginsDirectory.relativize(path).toString(); + final var rpath = pluginsDirectory.relativize(path).toString(); /* * This regular expression matches paths containing directories with names @@ -522,29 +558,41 @@ private boolean isLibJar(Path path) { * The pattern is explained as follows: * * (^|.*[\\/\\\\]) Matches either the beginning of the string - * (for relative paths like lib/pippo.jar or my-lib/pippo.jar), - * or any preceding directories in the path for absolute paths. + * (for relative paths like lib/pippo.jar or my-lib/pippo.jar), + * or any preceding directories in the path for absolute paths. * - * ([^\\/\\\\]+[-_])? Optionally matches a prefix consisting of one or more characters, - * excluding directory separators, followed by either a hyphen ("-") or underscore ("_"). + * ([^\\/\\\\]+[-_])? Optionally matches a prefix consisting of one or more + * characters, + * excluding directory separators, followed by either a hyphen ("-") or + * underscore ("_"). * * lib Matches the literal string "lib" * - * ([\\/\\\\].*|$) Matches either a directory separator followed by any characters + * ([\\/\\\\].*|$) Matches either a directory separator followed by any + * characters * * ([\\/\\\\].*) or the end of the string ($). */ return rpath.matches("(^|.*[\\/\\\\])([^\\/\\\\]+[-_])?lib([\\/\\\\].*|$)"); - } catch(IllegalArgumentException iae) { + } catch (final IllegalArgumentException iae) { return false; } } } } -record PluginDescriptor(String name, String clazz, boolean enabled, ArrayList injections) {} -interface InjectionDescriptor {} +record PluginDescriptor(String name, String clazz, boolean enabled, ArrayList injections) { +} + +interface InjectionDescriptor { +} -record MethodInjectionDescriptor(String method, Class clazz, ArrayList> annotationParams, ArrayList methodParams, int methodHash) implements InjectionDescriptor {} +record MethodInjectionDescriptor(String method, Class clazz, + ArrayList> annotationParams, ArrayList methodParams, + int methodHash) implements InjectionDescriptor { +} -record FieldInjectionDescriptor(String field, Class clazz, ArrayList> annotationParams, int fieldHash) implements InjectionDescriptor {} +record FieldInjectionDescriptor(String field, Class clazz, + ArrayList> annotationParams, int fieldHash) + implements InjectionDescriptor { +}