From 07042e7a23805a9035a4683a7af26e0b8b9221c1 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 22 Jul 2021 15:50:03 +0200 Subject: [PATCH] Fix IpcClient on windows, improve logging --- .../org/mvndaemon/mvnd/sync/IpcClient.java | 60 ++++++++++++++----- .../mvnd/sync/IpcSyncContextFactory.java | 6 +- .../mvnd/sync/IpcSyncContextTest.java | 12 +++- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcClient.java b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcClient.java index fe3b787a3..9803db75a 100644 --- a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcClient.java +++ b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcClient.java @@ -19,8 +19,10 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.InterruptedIOException; +import java.io.PrintWriter; import java.io.RandomAccessFile; import java.net.SocketAddress; import java.nio.channels.ByteChannel; @@ -49,6 +51,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.mvndaemon.mvnd.common.ByteChannelWrapper; import org.mvndaemon.mvnd.common.JavaVersion; +import org.mvndaemon.mvnd.common.Os; import org.mvndaemon.mvnd.common.SocketFamily; import static org.mvndaemon.mvnd.sync.IpcMessages.REQUEST_ACQUIRE; @@ -65,8 +68,9 @@ */ public class IpcClient { - Path repository; - Path syncServerPath; + Path lockPath; + Path logPath; + Path syncPath; SocketChannel socket; DataOutputStream output; DataInputStream input; @@ -74,9 +78,10 @@ public class IpcClient { AtomicInteger requestId = new AtomicInteger(); Map>> responses = new ConcurrentHashMap<>(); - IpcClient(Path repository, Path syncServerPath) { - this.repository = repository; - this.syncServerPath = syncServerPath; + IpcClient(Path lockPath, Path logPath, Path syncPath) { + this.lockPath = lockPath; + this.logPath = logPath; + this.syncPath = syncPath; } synchronized void ensureInitialized() throws IOException { @@ -97,8 +102,8 @@ SocketChannel createClient() throws IOException { ? SocketFamily.valueOf(familyProp) : JavaVersion.getJavaSpec() >= 16.0f ? SocketFamily.unix : SocketFamily.inet; - Path lockFile = repository.resolve(".maven-resolver-ipc-lock-" + family.name().toLowerCase()) - .toAbsolutePath().normalize(); + Path lockPath = this.lockPath.toAbsolutePath().normalize(); + Path lockFile = lockPath.resolve(".maven-resolver-ipc-lock-" + family.name().toLowerCase()); if (!Files.isRegularFile(lockFile)) { if (!Files.isDirectory(lockFile.getParent())) { Files.createDirectories(lockFile.getParent()); @@ -125,16 +130,18 @@ SocketChannel createClient() throws IOException { boolean win = System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("win"); if (!noNative) { String syncCmd = win ? "mvnd-sync.exe" : "mvnd-sync"; - noNative = !Files.isExecutable(syncServerPath.resolve(syncCmd)); + noNative = !Files.isExecutable(syncPath.resolve(syncCmd)); } Closeable close; + Path logFile = logPath.resolve("mvnd-sync-" + rand + ".log"); + List args = null; if (noNative) { String noFork = System.getProperty(IpcServer.NO_FORK_PROP); if (Boolean.parseBoolean(noFork)) { IpcServer server = IpcServer.runServer(family, tmpaddr, rand); close = server::close; } else { - List args = new ArrayList<>(); + args = new ArrayList<>(); String javaHome = System.getenv("JAVA_HOME"); if (javaHome == null) { javaHome = System.getProperty("java.home"); @@ -156,6 +163,8 @@ SocketChannel createClient() throws IOException { ProcessBuilder processBuilder = new ProcessBuilder(); ProcessBuilder.Redirect discard = ProcessBuilder.Redirect.to(new File(win ? "NUL" : "/dev/null")); discard = ProcessBuilder.Redirect.INHERIT; + Files.createDirectories(logPath); + discard = ProcessBuilder.Redirect.to(logFile.toFile()); Process process = processBuilder .directory(lockFile.getParent().toFile()) .command(args) @@ -165,9 +174,9 @@ SocketChannel createClient() throws IOException { close = process::destroyForcibly; } } else { - List args = new ArrayList<>(); + args = new ArrayList<>(); String syncCmd = win ? "mvnd-sync.exe" : "mvnd-sync"; - args.add(syncServerPath.resolve(syncCmd).toString()); + args.add(syncPath.resolve(syncCmd).toString()); String timeout = System.getProperty(IpcServer.IDLE_TIMEOUT_PROP); if (timeout != null) { args.add("-D" + IpcServer.IDLE_TIMEOUT_PROP + "=" + timeout); @@ -178,6 +187,8 @@ SocketChannel createClient() throws IOException { ProcessBuilder processBuilder = new ProcessBuilder(); ProcessBuilder.Redirect discard = ProcessBuilder.Redirect.to(new File(win ? "NUL" : "/dev/null")); discard = ProcessBuilder.Redirect.INHERIT; + Files.createDirectories(logPath); + discard = ProcessBuilder.Redirect.to(logFile.toFile()); Process process = processBuilder .directory(lockFile.getParent().toFile()) .command(args) @@ -199,6 +210,13 @@ SocketChannel createClient() throws IOException { try { res = future.get(5, TimeUnit.SECONDS); } catch (Exception e) { + try (PrintWriter writer = new PrintWriter(new FileWriter(logFile.toFile(), true))) { + writer.println("Arguments:"); + args.forEach(writer::println); + writer.println(); + writer.println("Exception:"); + e.printStackTrace(writer); + } close.close(); throw e; } finally { @@ -227,12 +245,24 @@ private String getJarPath(Class clazz) { String className = clazz.getName().replace('.', '/') + ".class"; String url = clazz.getClassLoader().getResource(className).toString(); if (url.startsWith("jar:")) { - classpath = url.substring("jar:".length(), url.indexOf("!/")); + url = url.substring("jar:".length(), url.indexOf("!/")); + if (url.startsWith("file:")) { + classpath = url.substring("file:".length()); + } else { + throw new IllegalStateException(); + } } else if (url.startsWith("file:")) { classpath = url.substring("file:".length(), url.indexOf(className)); } else { throw new IllegalStateException(); } + if (Os.current() == Os.WINDOWS) { + if (classpath.startsWith("/")) { + classpath = classpath.substring(1); + } + classpath = classpath.replace('/', '\\'); + } + return classpath; } @@ -354,9 +384,9 @@ void unlock(String contextId) { @Override public String toString() { return "IpcClient{" - + "repository=" + repository + ',' - + "address=" + (socket != null ? getAddress() : 0) - + '}'; + + "lockPath=" + lockPath + "," + + "syncServerPath=" + syncPath + "," + + "address='" + getAddress() + "'}"; } private String getAddress() { diff --git a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContextFactory.java b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContextFactory.java index 30b6e809e..496317dbf 100644 --- a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContextFactory.java +++ b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContextFactory.java @@ -26,6 +26,7 @@ import org.eclipse.aether.SyncContext; import org.eclipse.aether.impl.SyncContextFactory; import org.eclipse.sisu.Priority; +import org.mvndaemon.mvnd.common.Environment; /** * The SyncContextFactory implementation. @@ -40,9 +41,10 @@ public class IpcSyncContextFactory implements SyncContextFactory { @Override public SyncContext newInstance(RepositorySystemSession session, boolean shared) { Path repository = session.getLocalRepository().getBasedir().toPath(); - String mvndHome = System.getProperty("mvnd.home"); + Path logPath = Environment.MVND_DAEMON_STORAGE.asPath(); + String mvndHome = Environment.MVND_HOME.asOptional().orElse(null); Path syncPath = mvndHome != null ? Paths.get(mvndHome).resolve("bin") : null; - IpcClient client = clients.computeIfAbsent(repository, r -> new IpcClient(r, syncPath)); + IpcClient client = clients.computeIfAbsent(repository, r -> new IpcClient(r, logPath, syncPath)); return new IpcSyncContext(client, shared); } diff --git a/sync/src/test/java/org/mvndaemon/mvnd/sync/IpcSyncContextTest.java b/sync/src/test/java/org/mvndaemon/mvnd/sync/IpcSyncContextTest.java index ac7c24c35..f64fb30c7 100644 --- a/sync/src/test/java/org/mvndaemon/mvnd/sync/IpcSyncContextTest.java +++ b/sync/src/test/java/org/mvndaemon/mvnd/sync/IpcSyncContextTest.java @@ -16,6 +16,8 @@ package org.mvndaemon.mvnd.sync; import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.SyncContext; @@ -28,6 +30,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mvndaemon.mvnd.common.Environment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,6 +40,9 @@ public class IpcSyncContextTest { @BeforeAll static void setup() { + Path target = Paths.get(System.getProperty("basedir", "")).resolve("target"); + System.setProperty(Environment.MVND_DAEMON_STORAGE.getProperty(), target.resolve("mvnd/storage").toString()); + System.setProperty(Environment.MVND_HOME.getProperty(), target.resolve("mvnd/home").toString()); System.setProperty(IpcServer.IDLE_TIMEOUT_PROP, "5"); System.setProperty(IpcServer.FAMILY_PROP, "inet"); System.setProperty(IpcServer.NO_NATIVE_PROP, "true"); @@ -52,7 +58,7 @@ public void testContextSimple() throws Exception { SyncContextFactory factory = new IpcSyncContextFactory(); DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(); - LocalRepository repository = new LocalRepository(new File("target/test-repo")); + LocalRepository repository = new LocalRepository(new File("target/mvnd/test-repo")); LocalRepositoryManager localRepositoryManager = new SimpleLocalRepositoryManagerFactory() .newInstance(session, repository); session.setLocalRepositoryManager(localRepositoryManager); @@ -69,7 +75,7 @@ public void testContext() throws Exception { SyncContextFactory factory = new IpcSyncContextFactory(); DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(); - LocalRepository repository = new LocalRepository(new File("target/test-repo")); + LocalRepository repository = new LocalRepository(new File("target/mvnd/test-repo")); LocalRepositoryManager localRepositoryManager = new SimpleLocalRepositoryManagerFactory() .newInstance(session, repository); session.setLocalRepositoryManager(localRepositoryManager); @@ -107,7 +113,7 @@ void testTimeoutAndConnect() throws Exception { SyncContextFactory factory = new IpcSyncContextFactory(); DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(); - LocalRepository repository = new LocalRepository(new File("target/test-repo")); + LocalRepository repository = new LocalRepository(new File("target/mvnd/test-repo")); LocalRepositoryManager localRepositoryManager = new SimpleLocalRepositoryManagerFactory() .newInstance(session, repository); session.setLocalRepositoryManager(localRepositoryManager);