Skip to content

Commit

Permalink
Use an agent to support processes launched with redirected io, fixes a…
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet committed Jan 6, 2021
1 parent 2a1272e commit 1adf527
Show file tree
Hide file tree
Showing 13 changed files with 460 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml

# Eclipse
.project
Expand Down
81 changes: 81 additions & 0 deletions agent/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<!--
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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mvndaemon.mvnd</groupId>
<artifactId>mvnd</artifactId>
<version>0.2.1-SNAPSHOT</version>
</parent>

<artifactId>mvnd-agent</artifactId>

<packaging>jar</packaging>
<name>Maven Daemon - Agent</name>

<dependencies>

<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>org.mvndaemon.mvnd.agent.Agent</Premain-Class>
<Boot-Class-Path>mvnd-helper-agent-${project.version}.jar</Boot-Class-Path>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>org.javassist:javassist</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
64 changes: 64 additions & 0 deletions agent/src/main/java/org/mvndaemon/mvnd/agent/Agent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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"
+ " AgentHelper.pump(p.getInputStream(), System.out);\n"
+ " AgentHelper.pump(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) {
System.err.println(e);
throw new IllegalClassFormatException(e.toString());
}
} else {
return classfileBuffer;
}
}
});
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-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.
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -300,10 +301,27 @@ 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;
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();
}
}
}
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/");
}
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);
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");
Expand Down
10 changes: 9 additions & 1 deletion dist/pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--
Copyright 2019 the original author or authors.
Copyright 2019-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.
Expand Down Expand Up @@ -31,6 +31,14 @@
<name>Maven Daemon - Distribution</name>

<dependencies>
<dependency>
<groupId>org.mvndaemon.mvnd</groupId>
<artifactId>mvnd-agent</artifactId>
</dependency>
<dependency>
<groupId>org.mvndaemon.mvnd</groupId>
<artifactId>mvnd-helper-agent</artifactId>
</dependency>
<dependency>
<groupId>org.mvndaemon.mvnd</groupId>
<artifactId>mvnd-client</artifactId>
Expand Down
4 changes: 3 additions & 1 deletion dist/src/main/provisio/maven-distro.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--
Copyright 2019 the original author or authors.
Copyright 2019-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.
Expand Down Expand Up @@ -63,6 +63,8 @@
<artifact id="org.mvndaemon.mvnd:mvnd-client:${project.version}">
<exclusion id="*:*"/>
</artifact>
<artifact id="org.mvndaemon.mvnd:mvnd-agent:${project.version}"/>
<artifact id="org.mvndaemon.mvnd:mvnd-helper-agent:${project.version}"/>
</artifactSet>

<fileSet to="/">
Expand Down
33 changes: 33 additions & 0 deletions helper/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!--
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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mvndaemon.mvnd</groupId>
<artifactId>mvnd</artifactId>
<version>0.2.1-SNAPSHOT</version>
</parent>

<artifactId>mvnd-helper-agent</artifactId>

<packaging>jar</packaging>
<name>Maven Daemon - Helper Agent</name>

</project>
29 changes: 29 additions & 0 deletions helper/src/main/java/org/mvndaemon/mvnd/pump/AgentHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;

public class AgentHelper {

public static void pump(InputStream stream, PrintStream out) {
new Thread(() -> new BufferedReader(new InputStreamReader(stream)).lines().forEach(out::println)).start();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.it;

import javax.inject.Inject;
import org.junit.jupiter.api.Test;
import org.mvndaemon.mvnd.assertj.TestClientOutput;
import org.mvndaemon.mvnd.client.Client;
import org.mvndaemon.mvnd.client.DaemonParameters;
import org.mvndaemon.mvnd.common.Message;
import org.mvndaemon.mvnd.junit.MvndTest;

import static junit.framework.Assert.assertTrue;

@MvndTest(projectDir = "src/test/projects/junit-platform")
public class JUnitPlatformTest {

@Inject
Client client;

@Inject
DaemonParameters parameters;

@Test
void cleanTestInheritIO() throws InterruptedException {

final TestClientOutput output = new TestClientOutput();
client.execute(output, "clean", "test", "-e", "-Dmvnd.log.level=DEBUG").assertSuccess();
assertHasTestMessage(output);

}

private void assertHasTestMessage(final TestClientOutput output) {
assertTrue(output.getMessages().stream()
.filter(Message.ProjectEvent.class::isInstance)
.map(Message.ProjectEvent.class::cast)
.anyMatch(it -> it.getMessage().contains("[stdout] From test")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-Dmaven.wagon.httpconnectionManager.ttlSeconds=120
-Dmaven.wagon.http.retryHandler.requestSentEnabled=true
-Dmaven.wagon.http.retryHandler.count=10
Loading

0 comments on commit 1adf527

Please sign in to comment.