diff --git a/agent/pom.xml b/agent/pom.xml
new file mode 100644
index 000000000..0449f80ad
--- /dev/null
+++ b/agent/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+ 4.0.0
+
+ org.mvndaemon.mvnd
+ mvnd
+ 0.2.1-SNAPSHOT
+
+
+ mvnd-agent
+
+ jar
+ Maven Daemon - Agent
+
+
+
+
+ org.javassist
+ javassist
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.mvndaemon.mvnd.agent.Agent
+ mvnd-pump-${version}.jar
+
+
+
+
+
+
+
+
diff --git a/agent/src/main/java/org/mvndaemon/mvnd/agent/Agent.java b/agent/src/main/java/org/mvndaemon/mvnd/agent/Agent.java
new file mode 100644
index 000000000..9d8054ffe
--- /dev/null
+++ b/agent/src/main/java/org/mvndaemon/mvnd/agent/Agent.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mvndaemon.mvnd.agent;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.security.ProtectionDomain;
+import javassist.ClassPool;
+import javassist.CtClass;
+
+public class Agent {
+
+ public static final String START_WITH_PIPES = "if (redirects != null\n"
+ + " && redirects[1] == ProcessBuilder$Redirect.INHERIT\n"
+ + " && redirects[2] == ProcessBuilder$Redirect.INHERIT) {\n"
+ + " redirects[1] = redirects[2] = ProcessBuilder$Redirect.PIPE;"
+ + " Process p = start(redirects);\n"
+ + " PumpThread.start(p.getInputStream(), System.out);\n"
+ + " PumpThread.start(p.getErrorStream(), System.err);\n"
+ + " return p;\n"
+ + "}";
+
+ public static void premain(String args, Instrumentation instrumentation) throws Exception {
+ instrumentation.addTransformer(new ClassFileTransformer() {
+ @Override
+ public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+ if ("java/lang/ProcessBuilder".equals(className)) {
+ try {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass clazz = pool.get("java.lang.ProcessBuilder");
+ pool.importPackage("org.mvndaemon.mvnd.pump");
+ clazz.getDeclaredMethod("start",
+ new CtClass[] { clazz.getClassPool().get("java.lang.ProcessBuilder$Redirect[]") })
+ .insertBefore(START_WITH_PIPES);
+ byte[] data = clazz.toBytecode();
+ clazz.detach();
+ return data;
+ } catch (Throwable e) {
+ throw new IllegalClassFormatException(e.toString());
+ }
+ } else {
+ return classfileBuffer;
+ }
+ }
+ });
+ }
+
+}
diff --git a/client/src/main/java/org/mvndaemon/mvnd/client/DaemonConnector.java b/client/src/main/java/org/mvndaemon/mvnd/client/DaemonConnector.java
index 74963bb14..933eca641 100644
--- a/client/src/main/java/org/mvndaemon/mvnd/client/DaemonConnector.java
+++ b/client/src/main/java/org/mvndaemon/mvnd/client/DaemonConnector.java
@@ -15,10 +15,12 @@
*/
package org.mvndaemon.mvnd.client;
+import java.io.File;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.SocketChannel;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -32,7 +34,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
-import org.mvndaemon.mvnd.common.BuildProperties;
import org.mvndaemon.mvnd.common.DaemonCompatibilitySpec;
import org.mvndaemon.mvnd.common.DaemonCompatibilitySpec.Result;
import org.mvndaemon.mvnd.common.DaemonConnection;
@@ -300,10 +301,33 @@ private Process startDaemon(String uid) {
final String java = Os.current().isUnixLike() ? "bin/java" : "bin\\java.exe";
args.add(parameters.javaHome().resolve(java).toString());
// classpath
+ String mvndCommonPath = null;
+ String mvndAgentPath = null;
+ String javassistPath = null;
+ for (Path jar : Files.newDirectoryStream(mvndHome.resolve("mvn/lib/ext"))) {
+ String s = jar.getFileName().toString();
+ if (s.endsWith(".jar")) {
+ if (s.startsWith("mvnd-common-")) {
+ mvndCommonPath = jar.toString();
+ } else if (s.startsWith("mvnd-agent-")) {
+ mvndAgentPath = jar.toString();
+ } else if (s.startsWith("javassist-")) {
+ javassistPath = jar.toString();
+ }
+ }
+ }
+ if (mvndCommonPath == null) {
+ throw new IllegalStateException("Could not find mvnd-common jar in mvn/lib/ext/");
+ }
+ if (mvndAgentPath == null) {
+ throw new IllegalStateException("Could not find mvnd-agent jar in mvn/lib/ext/");
+ }
+ if (javassistPath == null) {
+ throw new IllegalStateException("Could not find javassist jar in mvn/lib/ext/");
+ }
args.add("-classpath");
- final String mvndCommonPath = "mvn/lib/ext/mvnd-common-" + BuildProperties.getInstance().getVersion() + ".jar";
- final String classpath = mvndHome.resolve(mvndCommonPath).toString();
- args.add(classpath);
+ args.add(mvndCommonPath + File.pathSeparator + mvndAgentPath + File.pathSeparator + javassistPath);
+ args.add("-javaagent:" + mvndAgentPath);
// debug options
if (parameters.property(Environment.MVND_DEBUG).asBoolean()) {
args.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000");
diff --git a/dist/pom.xml b/dist/pom.xml
index a68d64620..661a41971 100644
--- a/dist/pom.xml
+++ b/dist/pom.xml
@@ -1,6 +1,6 @@
+
+
+ 4.0.0
+
+ org.mvndaemon.mvnd
+ mvnd
+ 0.2.1-SNAPSHOT
+
+
+ mvnd-pump
+
+ jar
+ Maven Daemon - Pump
+
+
diff --git a/pump/src/main/java/org/mvndaemon/mvnd/pump/PumpThread.java b/pump/src/main/java/org/mvndaemon/mvnd/pump/PumpThread.java
new file mode 100644
index 000000000..9206f33df
--- /dev/null
+++ b/pump/src/main/java/org/mvndaemon/mvnd/pump/PumpThread.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mvndaemon.mvnd.pump;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+
+public class PumpThread extends Thread {
+
+ private final InputStream stream;
+ private final PrintStream out;
+
+ public static void start(InputStream stream, PrintStream out) {
+ new PumpThread(stream, out).start();
+ }
+
+ public PumpThread(InputStream stream, PrintStream out) {
+ this.stream = stream;
+ this.out = out;
+ }
+
+ @Override
+ public void run() {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+ try {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ out.println(line);
+ }
+ } finally {
+ reader.close();
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+}