Native mvnd client

This commit is contained in:
Peter Palaga
2020-06-01 12:23:11 +02:00
parent 4f2c0fb215
commit 82a97ac38b
13 changed files with 315 additions and 99 deletions

View File

@@ -20,6 +20,11 @@
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>svm</artifactId>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-terminal</artifactId>
@@ -59,4 +64,38 @@
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<skip>false</skip>
<mainClass>org.jboss.fuse.mvnd.client.Client</mainClass>
<imageName>mvnd</imageName>
<buildArgs>
--no-server
--no-fallback
--allow-incomplete-classpath
-H:IncludeResources=org/jboss/fuse/mvnd/.*
-H:IncludeResources=org/jline/utils/.*
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -17,16 +17,17 @@ package org.jboss.fuse.mvnd.client;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.fusesource.jansi.Ansi;
import org.jboss.fuse.mvnd.client.ClientOutput.TerminalOutput;
@@ -48,8 +49,8 @@ public class Client {
public static final int DEFAULT_IDLE_TIMEOUT = (int) TimeUnit.HOURS.toMillis(3);
public static final int DEFAULT_PERIODIC_CHECK_INTERVAL_MILLIS = 10 * 1000;
public static final int CANCEL_TIMEOUT = 10 * 1000;
private final Layout layout;
private final Optional<ClientLayout> clientLayout;
private final ClientLayout layout;
private final Properties buildProperties;
public static void main(String[] argv) throws Exception {
final List<String> args = new ArrayList<>(Arrays.asList(argv));
@@ -66,13 +67,18 @@ public class Client {
}
try (TerminalOutput output = new TerminalOutput(logFile)) {
new Client(Layout.getEnvInstance(), Optional.empty()).execute(output, args);
new Client(ClientLayout.getEnvInstance()).execute(output, args);
}
}
public Client(Layout layout, Optional<ClientLayout> clientLayout) {
public Client(ClientLayout layout) {
this.layout = layout;
this.clientLayout = clientLayout;
this.buildProperties = new Properties();
try (InputStream is = Client.class.getResourceAsStream("build.properties")) {
buildProperties.load(is);
} catch (IOException e) {
throw new RuntimeException("Could not read build.properties");
}
}
public <O extends ClientOutput> ClientResult<O> execute(O output, String... argv) throws IOException {
@@ -89,11 +95,8 @@ public class Client {
boolean showVersion = args.contains("-V") || args.contains("--show-version");
boolean debug = args.contains("-X") || args.contains("--debug");
if (version || showVersion || debug) {
Properties props = new Properties();
try (InputStream is = Client.class.getResourceAsStream("build.properties")) {
props.load(is);
}
String v = Ansi.ansi().bold().a("Maven Daemon " + props.getProperty("version")).reset().toString();
final String nativeSuffix = Layout.isNative() ? " (native)" : "";
final String v = Ansi.ansi().bold().a("Maven Daemon " + buildProperties.getProperty("version") + nativeSuffix).reset().toString();
output.log(v);
/* Do not return, rather pass -v to the server so that the client module does not need to depend on any Maven artifacts */
}
@@ -130,7 +133,16 @@ public class Client {
}
setDefaultArgs(args);
clientLayout.ifPresent(cl -> clientLayout(cl, args));
final Path settings = layout.getSettings();
if (settings != null && !args.stream().anyMatch(arg -> arg.equals("-s") || arg.equals("--settings"))) {
args.add("-s");
args.add(settings.toString());
}
final Path localMavenRepository = layout.getLocalMavenRepository();
if (localMavenRepository != null && !args.stream().anyMatch(arg -> arg.startsWith("-Dmaven.repo.local"))) {
args.add("-Dmaven.repo.local=" + localMavenRepository.toString());
}
DaemonConnector connector = new DaemonConnector(layout, registry, this::startDaemon, new MessageSerializer());
List<String> opts = new ArrayList<>();
@@ -170,16 +182,6 @@ public class Client {
}
static void clientLayout(ClientLayout cl, List<String> args) {
if (!args.stream().anyMatch(arg -> arg.equals("-s") || arg.equals("--settings"))) {
args.add("-s");
args.add(cl.getSettings().toString());
}
if (!args.stream().anyMatch(arg -> arg.startsWith("-Dmaven.repo.local"))) {
args.add("-Dmaven.repo.local=" + cl.getLocalMavenRepository().toString());
}
}
static void setDefaultArgs(List<String> args) {
if (!args.stream().anyMatch(arg -> arg.startsWith("-T") || arg.equals("--threads"))) {
args.add("-T1C");
@@ -200,17 +202,15 @@ public class Client {
// args.add("-cp");
// args.add(classpath);
String uid = UUID.randomUUID().toString();
Path mavenHome = layout.mavenHome();
Path javaHome = layout.javaHome();
Path workingDir = layout.userDir();
final String uid = UUID.randomUUID().toString();
final Path mavenHome = layout.mavenHome();
final Path workingDir = layout.userDir();
String command = "";
try {
String url = Client.class.getClassLoader().getResource(Client.class.getName().replace('.', '/') + ".class").toString();
String classpath = url.substring("file:jar:".length(), url.indexOf('!'));
String java = ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java";
String classpath = findClientJar(mavenHome).toString();
final String java = ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java";
List<String> args = new ArrayList<>();
args.add("\"" + javaHome.resolve(java) + "\"");
args.add("\"" + layout.javaHome().resolve(java) + "\"");
args.add("-classpath");
args.add("\"" + classpath + "\"");
if (Boolean.getBoolean(DAEMON_DEBUG)) {
@@ -239,4 +239,18 @@ public class Client {
}
}
Path findClientJar(Path mavenHome) {
final Path ext = mavenHome.resolve("lib/ext");
final String clientJarName = "mvnd-client-"+ buildProperties.getProperty("version") + ".jar";
try (Stream<Path> files = Files.list(ext)) {
return files
.filter(f -> f.getFileName().toString().equals(clientJarName))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find " + clientJarName + " in " + ext));
} catch (IOException e) {
throw new RuntimeException("Could not find " + clientJarName + " in " + ext, e);
}
}
}

View File

@@ -1,28 +1,89 @@
package org.jboss.fuse.mvnd.client;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Properties;
/**
* Local paths relevant for the {@link Client}.
*/
public class ClientLayout {
public class ClientLayout extends Layout {
private static ClientLayout ENV_INSTANCE;
private final Path localMavenRepository;
private final Path settings;
private final Path javaHome;
public ClientLayout(Path localMavenRepository, Path settings) {
super();
this.localMavenRepository = Objects.requireNonNull(localMavenRepository, "localMavenRepository");
this.settings = Objects.requireNonNull(settings, "settings");
public static ClientLayout getEnvInstance() {
if (ENV_INSTANCE == null) {
final Properties mvndProperties = loadMvndProperties();
final Path pwd = Paths.get(".").toAbsolutePath().normalize();
ENV_INSTANCE = new ClientLayout(
findMavenHome(mvndProperties),
pwd,
findMultiModuleProjectDirectory(pwd),
findJavaHome(mvndProperties),
null,
null);
}
return ENV_INSTANCE;
}
public ClientLayout(Path mavenHome, Path userDir, Path multiModuleProjectDirectory, Path javaHome,
Path localMavenRepository, Path settings) {
super(mavenHome, userDir, multiModuleProjectDirectory);
this.localMavenRepository = localMavenRepository;
this.settings = settings;
this.javaHome = Objects.requireNonNull(javaHome, "javaHome");
}
/**
* @return absolute normalized path to local Maven repository or {@code null}
*/
public Path getLocalMavenRepository() {
return localMavenRepository;
}
/**
* @return absolute normalized path to {@code settings.xml} or {@code null}
*/
public Path getSettings() {
return settings;
}
public Path javaHome() {
return javaHome;
}
static Path findJavaHome(Properties mvndProperties) {
String rawValue = System.getenv("JAVA_HOME");
if (rawValue == null) {
rawValue = mvndProperties.getProperty("java.home");
}
if (rawValue == null) {
rawValue = System.getProperty("java.home");
}
if (rawValue == null) {
throw new IllegalStateException(
"Either environment variable JAVA_HOME or java.home property in ~/.m2/mvnd.properties or system property java.home must be set");
}
final Path path = Paths.get(rawValue);
try {
return path.toRealPath();
} catch (IOException e) {
throw new RuntimeException("Could not get a real path from path " + path);
}
}
@Override
public String toString() {
return "ClientLayout [localMavenRepository=" + localMavenRepository + ", settings=" + settings + ", javaHome="
+ javaHome + ", mavenHome()=" + mavenHome() + ", userDir()=" + userDir() + ", registry()=" + registry()
+ ", multiModuleProjectDirectory()=" + multiModuleProjectDirectory() + "]";
}
}

View File

@@ -63,23 +63,24 @@ public interface ClientOutput extends AutoCloseable {
private void update() {
// no need to refresh the display at every single step
long curTime = System.currentTimeMillis();
if (curTime - lastUpdate >= 10) {
Size size = terminal.getSize();
if (curTime - lastUpdate >= 100) {
final Size size = terminal.getSize();
display.resize(size.getRows(), size.getColumns());
List<AttributedString> lines = new ArrayList<>();
projects.values().stream()
.map(AttributedString::fromAnsi)
.map(s -> s.columnSubSequence(0, size.getColumns() - 1))
.forEachOrdered(lines::add);
// Make sure we don't try to display more lines than the terminal height
int rem = 0;
while (lines.size() >= terminal.getHeight()) {
lines.remove(0);
rem++;
final int displayableProjectCount = size.getRows() - 1;
final int skipRows = projects.size() > displayableProjectCount ? projects.size() - displayableProjectCount : 0;
final List<AttributedString> lines = new ArrayList<>(projects.size() - skipRows);
final int lineMaxLength = size.getColumns();
int i = 0;
for (String line : projects.values()) {
if (i < skipRows) {
i++;
} else {
lines.add(shortenIfNeeded(AttributedString.fromAnsi(line), lineMaxLength));
}
}
lines.add(0, new AttributedString("Building..." + (rem > 0 ? " (" + rem + " more)" : "")));
lines.add(0, new AttributedString("Building..." + (skipRows > 0 ? " (" + skipRows + " more)" : "")));
display.update(lines, -1);
lastUpdate = curTime;
lastUpdate = System.currentTimeMillis();
}
}
@@ -188,4 +189,14 @@ public interface ClientOutput extends AutoCloseable {
}
}
static AttributedString shortenIfNeeded(AttributedString s, int length) {
if (s == null) {
return null;
}
if (s.length() > length) {
return s.columnSubSequence(0, length - 1);
}
return s;
}
}

View File

@@ -15,31 +15,30 @@
*/
package org.jboss.fuse.mvnd.client;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Stream;
public class Layout {
private static Layout ENV_INSTANCE;
private final Path javaHome;
private final Path mavenHome;
private final Path userDir;
private final Path multiModuleProjectDirectory;
public Layout(Path javaHome, Path mavenHome, Path userDir, Path multiModuleProjectDirectory) {
public Layout(Path mavenHome, Path userDir, Path multiModuleProjectDirectory) {
super();
this.javaHome = javaHome;
this.mavenHome = mavenHome;
this.userDir = userDir;
this.multiModuleProjectDirectory = multiModuleProjectDirectory;
}
public Path javaHome() {
return javaHome;
}
public Path mavenHome() {
return mavenHome;
}
@@ -60,18 +59,92 @@ public class Layout {
return multiModuleProjectDirectory;
}
private static String getProperty(String key) {
return Objects.requireNonNull(System.getProperty(key), "Undefined system property: " + key);
public static boolean isNative() {
return "executable".equals(System.getProperty("org.graalvm.nativeimage.kind"));
}
public static Layout getEnvInstance() {
if (ENV_INSTANCE == null) {
ENV_INSTANCE = new Layout(Paths.get(getProperty("java.home")).toAbsolutePath().normalize(),
Paths.get(getProperty("maven.home")).toAbsolutePath().normalize(),
Paths.get(getProperty("user.dir")).toAbsolutePath().normalize(),
Paths.get(getProperty("maven.multiModuleProjectDirectory")).toAbsolutePath().normalize());
final Properties mvndProperties = loadMvndProperties();
final Path pwd = Paths.get(".").toAbsolutePath().normalize();
ENV_INSTANCE = new Layout(
findMavenHome(mvndProperties),
pwd,
findMultiModuleProjectDirectory(pwd));
}
return ENV_INSTANCE;
}
static Properties loadMvndProperties() {
final Properties result = new Properties();
final Path mvndPropsPath = Paths.get(System.getProperty("user.home")).resolve(".m2/mvnd.properties");
if (Files.exists(mvndPropsPath)) {
try (InputStream in = Files.newInputStream(mvndPropsPath)) {
result.load(in);
} catch (IOException e) {
throw new RuntimeException("Could not read " + mvndPropsPath);
}
}
return result;
}
static Path findMavenHome(Properties mvndProperties) {
String rawValue = System.getenv("MAVEN_HOME");
if (rawValue == null) {
rawValue = System.getProperty("maven.home");
}
if (isNative()) {
try {
final Path nativeExecutablePath = Paths.get(Class.forName("org.graalvm.nativeimage.ProcessProperties").getMethod("getExecutableName").invoke(null).toString()).toAbsolutePath().normalize();
final Path bin = nativeExecutablePath.getParent();
if (bin.getFileName().toString().equals("bin")) {
final Path candidateMvnHome = bin.getParent();
final Path libExt = candidateMvnHome.resolve("lib/ext");
if (Files.isDirectory(libExt)) {
try (Stream<Path> files = Files.list(libExt)) {
if (files.filter(path -> path.getFileName().toString().startsWith("mvnd-")).findFirst().isPresent()) {
rawValue = candidateMvnHome.toString();
}
} catch (IOException e) {
throw new RuntimeException("Could not list " + libExt);
}
}
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
| SecurityException | ClassNotFoundException e) {
throw new RuntimeException("Could not invoke org.graalvm.nativeimage.ProcessProperties.getExecutableName() via reflection");
}
}
if (rawValue == null) {
rawValue = mvndProperties.getProperty("maven.home");
}
if (rawValue == null) {
throw new IllegalStateException("Either environment variable MAVEN_HOME or maven.home property in ~/.m2/mvnd.properties or system property maven.home must be set");
}
return Paths.get(rawValue).toAbsolutePath().normalize();
}
static Path findMultiModuleProjectDirectory(Path pwd) {
final String multiModuleProjectDirectory = System.getProperty("maven.multiModuleProjectDirectory");
if (multiModuleProjectDirectory != null) {
return Paths.get(multiModuleProjectDirectory).toAbsolutePath().normalize();
}
Path dir = pwd;
do {
if (Files.isDirectory(dir.resolve(".mvn"))) {
return dir.toAbsolutePath().normalize();
}
dir = dir.getParent();
} while (dir != null);
throw new IllegalStateException("Could not detect maven.multiModuleProjectDirectory by climbing up from ["+ pwd +"] seeking a .mvn directory. You may want to create a .mvn directory in the root directory of your source tree.");
}
@Override
public String toString() {
return "Layout [mavenHome=" + mavenHome + ", userDir=" + userDir + ", multiModuleProjectDirectory="
+ multiModuleProjectDirectory + "]";
}
}

View File

@@ -0,0 +1,18 @@
package org.jboss.fuse.mvnd.client.svm;
import org.slf4j.MDC;
import org.slf4j.impl.StaticMDCBinder;
import org.slf4j.spi.MDCAdapter;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
@TargetClass(MDC.class)
final class StaticMDCBinderSubstitution {
@Substitute
private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {
return StaticMDCBinder.SINGLETON.getMDCA();
}
}

View File

@@ -101,7 +101,7 @@ public class Server implements AutoCloseable, Runnable {
List<String> opts = new ArrayList<>();
long cur = System.currentTimeMillis();
info = new DaemonInfo(uid, layout.javaHome().toString(), layout.mavenHome().toString(),
info = new DaemonInfo(uid, System.getProperty("java.home"), layout.mavenHome().toString(),
DaemonRegistry.getProcessId(), socket.socket().getLocalPort(),
idleTimeout, Locale.getDefault().toLanguageTag(), opts,
Busy, cur, cur);

View File

@@ -13,7 +13,6 @@ import org.jboss.fuse.mvnd.assertj.MatchInOrderAmongOthers;
import org.jboss.fuse.mvnd.client.Client;
import org.jboss.fuse.mvnd.client.ClientLayout;
import org.jboss.fuse.mvnd.client.ClientOutput;
import org.jboss.fuse.mvnd.client.Layout;
import org.jboss.fuse.mvnd.junit.MvndTest;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
@@ -26,10 +25,7 @@ public class MultiModuleTest {
Client client;
@Inject
Layout layout;
@Inject
ClientLayout clientLayout;
ClientLayout layout;
@Test
void cleanInstall() throws IOException {
@@ -45,7 +41,7 @@ public class MultiModuleTest {
}
});
final Path localMavenRepo = clientLayout.getLocalMavenRepository();
final Path localMavenRepo = layout.getLocalMavenRepository();
final Path[] installedJars = {
localMavenRepo.resolve("org/jboss/fuse/mvnd/test/multi-module/multi-module-api/0.0.1-SNAPSHOT/multi-module-api-0.0.1-SNAPSHOT.jar"),
localMavenRepo.resolve("org/jboss/fuse/mvnd/test/multi-module/multi-module-hello/0.0.1-SNAPSHOT/multi-module-hello-0.0.1-SNAPSHOT.jar"),

View File

@@ -12,7 +12,6 @@ import org.jboss.fuse.mvnd.assertj.MatchInOrderAmongOthers;
import org.jboss.fuse.mvnd.client.Client;
import org.jboss.fuse.mvnd.client.ClientLayout;
import org.jboss.fuse.mvnd.client.ClientOutput;
import org.jboss.fuse.mvnd.client.Layout;
import org.jboss.fuse.mvnd.junit.MvndTest;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
@@ -26,10 +25,7 @@ public class SingleModuleTest {
Client client;
@Inject
Layout layout;
@Inject
ClientLayout clientLayout;
ClientLayout layout;
@Test
void cleanInstall() throws IOException {
@@ -38,7 +34,7 @@ public class SingleModuleTest {
Files.delete(helloFilePath);
}
final Path installedJar = clientLayout.getLocalMavenRepository().resolve("org/jboss/fuse/mvnd/test/single-module/single-module/0.0.1-SNAPSHOT/single-module-0.0.1-SNAPSHOT.jar");
final Path installedJar = layout.getLocalMavenRepository().resolve("org/jboss/fuse/mvnd/test/single-module/single-module/0.0.1-SNAPSHOT/single-module-0.0.1-SNAPSHOT.jar");
Assertions.assertThat(installedJar).doesNotExist();
final ClientOutput output = Mockito.mock(ClientOutput.class);

View File

@@ -1,14 +1,11 @@
package org.jboss.fuse.mvnd.it;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.jboss.fuse.mvnd.assertj.MatchInOrderAmongOthers;
import org.jboss.fuse.mvnd.client.Client;
import org.jboss.fuse.mvnd.client.ClientOutput;

View File

@@ -7,8 +7,8 @@ import javax.inject.Inject;
import org.assertj.core.api.Assertions;
import org.jboss.fuse.mvnd.assertj.MatchInOrderAmongOthers;
import org.jboss.fuse.mvnd.client.Client;
import org.jboss.fuse.mvnd.client.ClientLayout;
import org.jboss.fuse.mvnd.client.ClientOutput;
import org.jboss.fuse.mvnd.client.Layout;
import org.jboss.fuse.mvnd.junit.MvndTest;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
@@ -21,7 +21,7 @@ public class VersionTest {
Client client;
@Inject
Layout layout;
ClientLayout layout;
@Test
void version() throws IOException {

View File

@@ -62,12 +62,12 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
f.setAccessible(true);
if (f.getType() == DaemonRegistry.class) {
f.set(testInstance, resource.registry);
} else if (f.getType() == ClientLayout.class) {
f.set(testInstance, resource.layout);
} else if (f.getType() == Layout.class) {
f.set(testInstance, resource.layout);
} else if (f.getType() == Client.class) {
f.set(testInstance, new Client(resource.layout, Optional.of(resource.clientLayout)));
} else if (f.getType() == ClientLayout.class) {
f.set(testInstance, resource.clientLayout);
f.set(testInstance, new Client(resource.layout));
}
}
}
@@ -86,8 +86,7 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
static class MvndResource implements ExtensionContext.Store.CloseableResource {
private final ClientLayout clientLayout;
private final Layout layout;
private final ClientLayout layout;
private final DaemonRegistry registry;
public static MvndResource create(String className, String rawProjectDir) throws IOException {
@@ -125,16 +124,17 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
throw new IllegalStateException(
"The value of mvnd.home system property points at a path that does not exist or is not a directory");
}
final Layout layout = new Layout(Paths.get(System.getProperty("java.home")).toAbsolutePath().normalize(),
mvndHome,
testExecutionDir,
testExecutionDir);
final DaemonRegistry registry = new DaemonRegistry(layout.registry());
final Path localMavenRepository = deleteDir(testDir.resolve("local-maven-repo"));
final Path settingsPath = createSettings(testDir.resolve("settings.xml"));
final ClientLayout clientLayout = new ClientLayout(localMavenRepository, settingsPath);
return new MvndResource(layout, registry, clientLayout);
final ClientLayout layout = new ClientLayout(
mvndHome,
testExecutionDir,
testExecutionDir,
Paths.get(System.getProperty("java.home")).toAbsolutePath().normalize(),
localMavenRepository, settingsPath);
final DaemonRegistry registry = new DaemonRegistry(layout.registry());
return new MvndResource(layout, registry);
}
static Path deleteDir(Path dir) {
@@ -171,11 +171,10 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
return settingsPath;
}
public MvndResource(Layout layout, DaemonRegistry registry, ClientLayout clientLayout) {
public MvndResource(ClientLayout layout, DaemonRegistry registry) {
super();
this.layout = layout;
this.registry = registry;
this.clientLayout = clientLayout;
}
@Override

18
pom.xml
View File

@@ -17,6 +17,7 @@
<!-- dependency versions a..z -->
<assertj.version>3.16.1</assertj.version>
<graalvm.version>20.1.0</graalvm.version>
<groovy.version>3.0.0</groovy.version>
<jakarta.inject.version>1.0</jakarta.inject.version>
<jline.version>3.12.1</jline.version>
@@ -57,6 +58,12 @@
<version>${groovy.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>svm</artifactId>
<version>${graalvm.version}</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
@@ -166,15 +173,20 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>mrm-maven-plugin</artifactId>
<version>${mrm.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>${graalvm.version}</version>
</plugin>
</plugins>
</pluginManagement>