Skip to content

Instantly share code, notes, and snippets.

@mike10004
Last active April 5, 2021 17:00
Show Gist options
  • Save mike10004/a619b28b0f361b20d347f5fc4b4783a5 to your computer and use it in GitHub Desktop.
Save mike10004/a619b28b0f361b20d347f5fc4b4783a5 to your computer and use it in GitHub Desktop.
Clean and parse Maven dependency:list output

Code that cleans and parses Maven dependency:list output

Occasionally I need to parse output of mvn dependency:list. When run as a plugin execution in a pom, the output file contains ANSI escape codes.

The code in this gist cleans the output file, stripping the ANSI codes, and parses the dependencies.

This code has a few dependencies:

<dependency>
    <groupId>org.fusesource.jansi</groupId>
    <artifactId>jansi</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.30</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
import org.apache.commons.io.input.ReaderInputStream;
import org.fusesource.jansi.AnsiColors;
import org.fusesource.jansi.AnsiMode;
import org.fusesource.jansi.AnsiType;
import org.fusesource.jansi.io.AnsiOutputStream;
import org.fusesource.jansi.io.AnsiProcessor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class AnsiCodes {
private static final AnsiOutputStream.IoRunnable THROW = new AnsiOutputStream.IoRunnable() {
@Override
public void run() throws IOException {
throw new IOException("intentionally not implemented");
}
};
private static final AnsiOutputStream.WidthSupplier ALWAYS_80 = () -> 80;
public static void strip(InputStream in, OutputStream out) throws IOException {
Charset encoding = StandardCharsets.UTF_8;
AnsiOutputStream strippedOut = new AnsiOutputStream(out,
ALWAYS_80,
AnsiMode.Strip,
new AnsiProcessor(out),
AnsiType.Unsupported,
AnsiColors.Colors256,
encoding, THROW, THROW, false);
int c;
while ((c = in.read()) != -1) {
strippedOut.write(c);
}
}
public static String strip(String inputText, Charset encoding) throws IOException {
InputStream inputStream = new ReaderInputStream(new StringReader(inputText), encoding);
ByteArrayOutputStream buffer = new ByteArrayOutputStream(inputText.length());
strip(inputStream, buffer);
buffer.flush();
return buffer.toString(encoding);
}
}
import org.fusesource.jansi.AnsiColors;
import org.fusesource.jansi.AnsiMode;
import org.fusesource.jansi.AnsiType;
import org.fusesource.jansi.io.AnsiOutputStream;
import org.fusesource.jansi.io.AnsiProcessor;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
public class AnsiCodesTest {
@Test
public void strip() throws Exception {
String input = " org.eclipse:yasson:jar:1.0.6:compile\u001B[36m -- module org.eclipse.yasson\u001B[m\n" +
" com.j256.ormlite:ormlite-core:jar:5.1:compile\u001B[36m -- module ormlite.core\u001B[0;1;33m (auto)\u001B[m\n" +
" org.glassfish.hk2:hk2-locator:jar:2.6.1:compile\u001B[36m -- module hk2.locator\u001B[0;1;33m (auto)\u001B[m\n";
String expected = " org.eclipse:yasson:jar:1.0.6:compile -- module org.eclipse.yasson\n" +
" com.j256.ormlite:ormlite-core:jar:5.1:compile -- module ormlite.core (auto)\n" +
" org.glassfish.hk2:hk2-locator:jar:2.6.1:compile -- module hk2.locator (auto)\n";
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
Charset encoding = StandardCharsets.UTF_8;
byte[] inputBytes = input.getBytes(encoding);
InputStream in = new ByteArrayInputStream(inputBytes);
AnsiCodes.strip(in, buffer);
String actual = buffer.toString(encoding);
assertEquals("stripped output", expected, actual);
}
@Test
public void strip_nocodes() throws Exception {
String textWithNoCodes = " org.eclipse:yasson:jar:1.0.6:compile -- module org.eclipse.yasson\n" +
" com.j256.ormlite:ormlite-core:jar:5.1:compile -- module ormlite.core (auto)\n" +
" org.glassfish.hk2:hk2-locator:jar:2.6.1:compile -- module hk2.locator (auto)\n";
String stripped = AnsiCodes.strip(textWithNoCodes, StandardCharsets.UTF_8);
assertEquals("no codes", textWithNoCodes, stripped);
}
@Test
public void strip_string() throws Exception {
String input = " org.eclipse:yasson:jar:1.0.6:compile\u001B[36m -- module org.eclipse.yasson\u001B[m\n" +
" com.j256.ormlite:ormlite-core:jar:5.1:compile\u001B[36m -- module ormlite.core\u001B[0;1;33m (auto)\u001B[m\n" +
" org.glassfish.hk2:hk2-locator:jar:2.6.1:compile\u001B[36m -- module hk2.locator\u001B[0;1;33m (auto)\u001B[m\n";
String expected = " org.eclipse:yasson:jar:1.0.6:compile -- module org.eclipse.yasson\n" +
" com.j256.ormlite:ormlite-core:jar:5.1:compile -- module ormlite.core (auto)\n" +
" org.glassfish.hk2:hk2-locator:jar:2.6.1:compile -- module hk2.locator (auto)\n";
String actual = AnsiCodes.strip(input, StandardCharsets.UTF_8);
assertEquals("stripped", expected, actual);
}
}
import javax.annotation.Nullable;
import java.util.StringJoiner;
class ArtifactCoordinates {
public final String groupId;
public final String artifactId;
@Nullable
public final String classifier;
public final String type;
public final String version;
public ArtifactCoordinates(String groupId, String artifactId, String type, @Nullable String classifier, String version) {
this.groupId = groupId;
this.classifier = classifier;
this.artifactId = artifactId;
this.type = type;
this.version = version;
}
@Override
public String toString() {
return new StringJoiner(", ", ArtifactCoordinates.class.getSimpleName() + "[", "]")
.add("groupId=" + (groupId == null ? "null" : "'" + groupId + "'"))
.add("artifactId=" + (artifactId == null ? "null" : "'" + artifactId + "'"))
.add("classifier=" + (classifier == null ? "null" : "'" + classifier + "'"))
.add("type=" + (type == null ? "null" : "'" + type + "'"))
.add("version=" + (version == null ? "null" : "'" + version + "'"))
.toString();
}
public String getJarName() {
String classifierPart = "";
if (classifier != null) {
classifierPart = "-" + classifier;
}
return String.format("%s-%s%s.%s", artifactId, version, classifierPart, type);
}
}
import org.junit.Test;
import static org.junit.Assert.*;
public class ArtifactCoordinatesTest {
@Test
public void getJarName_noClassifier() {
ArtifactCoordinates c = new ArtifactCoordinates("foo", "bar", "jar", null, "1.0");
assertEquals("bar-1.0.jar", c.getJarName());
}
@Test
public void getJarName_classifier() {
ArtifactCoordinates c = new ArtifactCoordinates("foo", "bar", "jar", "baz", "1.0");
assertEquals("bar-1.0-baz.jar", c.getJarName());
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.regex.Pattern;
class MavenDependency {
private static final Logger log = LoggerFactory.getLogger(MavenDependency.class);
public final ArtifactCoordinates coordinates;
public final String scope;
public MavenDependency(ArtifactCoordinates coordinates, String scope) {
this.coordinates = coordinates;
this.scope = scope;
}
@Nullable
public static MavenDependency parse(String line) {
// Example lines:
// org.glassfish:jakarta.json:jar:1.1.6:compile -- module java.json [auto]
// org.elasticmq:elasticmq-core_2.12:jar:0.15.6:compile
line = line.trim();
String[] mainParts = line.split(Pattern.quote(" -- "));
String[] depParts = mainParts[0].split(":");
if (depParts.length != 5 && depParts.length != 6) {
log.debug("malformed line: {}", line);
return null;
}
String groupId = depParts[0], artifactId = depParts[1], type = depParts[2];
final String scope, version;
final @Nullable String classifier;
if (depParts.length == 5) {
classifier = null;
version = depParts[3];
scope = depParts[4];
} else {
classifier = depParts[3];
version = depParts[4];
scope = depParts[5];
}
ArtifactCoordinates coordinates = new ArtifactCoordinates(groupId, artifactId, type, classifier, version);
return new MavenDependency(coordinates, scope);
}
}
import org.fusesource.jansi.AnsiColors;
import org.fusesource.jansi.AnsiMode;
import org.fusesource.jansi.AnsiType;
import org.fusesource.jansi.io.AnsiOutputStream;
import org.fusesource.jansi.io.AnsiProcessor;
import org.junit.Test;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
public class MavenDependencyTest {
@Test
public void parse_noModule() throws Exception {
String line = " org.elasticmq:elasticmq-core_2.12:jar:0.15.6:compile";
assertNotNull(MavenDependency.parse(line));
}
@Test
public void parse() throws Exception {
String line = " com.github.scribejava:scribejava-httpclient-apache:jar:6.9.0:compile -- module scribejava.httpclient.apache (auto)";
MavenDependency d = MavenDependency.parse(line);
assertNotNull(d);
assertEquals("com.github.scribejava", d.coordinates.groupId);
assertEquals("scribejava-httpclient-apache", d.coordinates.artifactId);
assertNull(d.coordinates.classifier);
assertEquals("jar", d.coordinates.type);
assertEquals("6.9.0", d.coordinates.version);
assertEquals("compile", d.scope);
}
@Test
public void parse_classifier() throws Exception {
String line = " io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.53.Final:runtime -- module io.netty.transport.epoll [auto]";
MavenDependency d = MavenDependency.parse(line);
assertNotNull(d);
assertEquals("io.netty", d.coordinates.groupId);
assertEquals("netty-transport-native-epoll", d.coordinates.artifactId);
assertEquals("4.1.53.Final", d.coordinates.version);
assertEquals("jar", d.coordinates.type);
assertEquals("linux-x86_64", d.coordinates.classifier);
}
@Test
public void parse_many() throws IOException {
BufferedReader reader = new BufferedReader(new StringReader(dependenciesListTxt));
String line;
List<String> nulls = new ArrayList<>();
List<String> nonjars = new ArrayList<>();
Map<String, MavenDependency> deps = new LinkedHashMap<>();
while ((line = reader.readLine()) != null) {
if (line.startsWith(" ")) {
@Nullable
MavenDependency dependency = MavenDependency.parse(line);
if (dependency != null) {
if ("jar".equals(dependency.coordinates.type)) {
deps.put(line, dependency);
} else {
nonjars.add(line);
}
} else {
nulls.add(line);
}
}
}
assertEquals("nulls", Collections.emptyList(), nulls);
assertEquals("nonjars", Collections.emptyList(), nonjars);
deps.forEach((line_, dep) -> {
assertFalse("groupId of " + line_, dep.coordinates.groupId.isBlank());
assertFalse("artifactId of " + line_, dep.coordinates.artifactId.isBlank());
assertFalse("version of " + line_, dep.coordinates.version.isBlank());
assertFalse("type of " + line_, dep.coordinates.type.isBlank());
assertFalse("scope of " + line_, dep.scope.isBlank());
});
}
private static final String dependenciesListTxt = "\n" +
"The following files have been resolved:\n" +
" org.eclipse:yasson:jar:1.0.6:compile -- module org.eclipse.yasson\n" +
" com.j256.ormlite:ormlite-core:jar:5.1:compile -- module ormlite.core (auto)\n" +
" org.glassfish.hk2:hk2-locator:jar:2.6.1:compile -- module hk2.locator (auto)\n" +
" org.glassfish.hk2.external:jakarta.inject:jar:2.6.1:compile -- module jakarta.inject (auto)\n" +
" org.glassfish.hk2:hk2-utils:jar:2.6.1:compile -- module hk2.utils (auto)\n" +
" jakarta.ws.rs:jakarta.ws.rs-api:jar:2.1.6:compile -- module java.ws.rs\n" +
" org.apache.httpcomponents:httpcore-nio:jar:4.4.10:compile\n" +
" org.elasticmq:elasticmq-rest-sqs_2.12:jar:0.15.6:compile\n" +
" io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.53.Final:runtime -- module io.netty.transport.epoll [auto]\n" +
" org.glassfish.jersey.containers:jersey-container-servlet:jar:2.33:compile -- module jersey.container.servlet (auto)\n" +
" io.netty:netty-common:jar:4.1.53.Final:runtime -- module io.netty.common [auto]\n" +
" org.joda:joda-convert:jar:2.2.1:compile -- module org.joda.convert\n" +
"\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment