Skip to content

Commit

Permalink
Fix dependency collection for new Spring Boot nested jars (#7931)
Browse files Browse the repository at this point in the history
  • Loading branch information
smola authored Nov 14, 2024
1 parent 8dcc168 commit e7571fd
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,19 @@ public static List<Dependency> resolve(URI uri) {

static List<Dependency> internalResolve(final URI uri) throws IOException {
final String scheme = uri.getScheme();
JarReader.Extracted metadata;
String path;
if ("file".equals(scheme)) {
File f;
if (uri.isOpaque()) {
f = new File(uri.getSchemeSpecificPart());
} else {
f = new File(uri);
}
path = f.getAbsolutePath();
metadata = JarReader.readJarFile(path);
} else if ("jar".equals(scheme) && uri.getSchemeSpecificPart().startsWith("file:")) {
path = uri.getSchemeSpecificPart().substring("file:".length());
metadata = JarReader.readNestedJarFile(path);
} else {
JarReader.Extracted metadata = null;
switch (scheme) {
case "file":
final File f = uri.isOpaque() ? new File(uri.getSchemeSpecificPart()) : new File(uri);
final String path = f.getAbsolutePath();
metadata = JarReader.readJarFile(path);
break;
case "jar":
metadata = resolveNestedJar(uri);
break;
default:
}
if (metadata == null) {
log.debug("unsupported dependency type: {}", uri);
return Collections.emptyList();
}
Expand All @@ -56,4 +54,42 @@ static List<Dependency> internalResolve(final URI uri) throws IOException {
Dependency.guessFallbackNoPom(metadata.manifest, metadata.jarName, is));
}
}

private static JarReader.Extracted resolveNestedJar(final URI uri) throws IOException {
String path = uri.getSchemeSpecificPart();

// Strip optional trailing '!' or '!/'.
if (path.endsWith("!")) {
path = path.substring(0, path.length() - 1);
} else if (path.endsWith("!/")) {
path = path.substring(0, path.length() - 2);
}

if (path.startsWith("file:")) {
// Old style nested dependencies, as seen in Spring Boot 2 and others.
// These look like jar:file:/path/to.jar!/path/to/nested.jar!/
path = path.substring("file:".length());
final int sepIdx = path.indexOf("!/");
if (sepIdx == -1) {
throw new IllegalArgumentException("Invalid nested jar path: " + path);
}
final String outerPath = path.substring(0, sepIdx);
final String innerPath = path.substring(sepIdx + 2);
return JarReader.readNestedJarFile(outerPath, innerPath);
} else if (path.startsWith("nested:")) {
// New style nested dependencies, for Spring 3.2+.
// These look like jar:nested:/path/to.jar/!path/to/nested.jar!/ (yes, /!, not !/).
// See https://docs.spring.io/spring-boot/specification/executable-jar/jarfile-class.html
path = path.substring("nested:".length());
final int sepIdx = path.indexOf("/!");
if (sepIdx == -1) {
throw new IllegalArgumentException("Invalid nested jar path: " + path);
}
final String outerPath = path.substring(0, sepIdx);
final String innerPath = path.substring(sepIdx + 2);
return JarReader.readNestedJarFile(outerPath, innerPath);
} else {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package datadog.telemetry.dependency;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -67,21 +68,16 @@ public static Extracted readJarFile(String jarPath) throws IOException {
pomProperties,
attributes,
false,
() -> new FileInputStream(jarPath));
() -> Files.newInputStream(Paths.get(jarPath)));
}
}

public static Extracted readNestedJarFile(final String jarPath) throws IOException {
final int sepIdx = jarPath.indexOf("!/");
if (sepIdx == -1) {
throw new IllegalArgumentException("Invalid nested jar path: " + jarPath);
}
final String outerJarPath = jarPath.substring(0, sepIdx);
final String innerJarPath = getInnerJarPath(jarPath);
public static Extracted readNestedJarFile(final String outerJarPath, final String innerJarPath)
throws IOException {
try (final JarFile outerJar = new JarFile(outerJarPath, false /* no verify */)) {
final ZipEntry entry = outerJar.getEntry(innerJarPath);
if (entry == null) {
throw new NoSuchFileException("Nested jar not found: " + jarPath);
throw new NoSuchFileException("Nested jar not found: " + innerJarPath);
}
if (entry.isDirectory()) {
return new Extracted(
Expand Down Expand Up @@ -111,19 +107,6 @@ public static Extracted readNestedJarFile(final String jarPath) throws IOExcepti
}
}

private static String getInnerJarPath(final String jarPath) {
final int sepIdx = jarPath.indexOf("!/");
if (sepIdx == -1) {
throw new IllegalArgumentException("Invalid nested jar path: " + jarPath);
}
String innerJarPath = jarPath.substring(sepIdx + 2);
final int innerSepIdx = innerJarPath.indexOf('!');
if (innerSepIdx != -1) {
innerJarPath = innerJarPath.substring(0, innerSepIdx);
}
return innerJarPath;
}

static class NestedJarInputStream extends InputStream implements AutoCloseable {
private final JarFile outerJar;
private final InputStream innerInputStream;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,48 @@ class DependencyResolverSpecification extends DepSpecification {
dep.source == 'opentracing-util-0.33.0.jar'
}

void 'spring boot dependency new style'() throws IOException {
when:
String zipPath = Classloader.classLoader.getResource('datadog/telemetry/dependencies/spring-boot-app.jar').path
URI uri = new URI("jar:nested:$zipPath/!BOOT-INF/lib/opentracing-util-0.33.0.jar!/")

Dependency dep = DependencyResolver.resolve(uri).get(0)

then:
dep != null
dep.name == 'io.opentracing:opentracing-util'
dep.version == '0.33.0'
dep.hash == null
dep.source == 'opentracing-util-0.33.0.jar'
}

void 'spring boot dependency new style empty path'() throws IOException {
when:
URI uri = new URI("jar:nested:")
List<Dependency> deps = DependencyResolver.resolve(uri)

then:
deps.isEmpty()
}

void 'spring boot dependency old style empty path'() throws IOException {
when:
URI uri = new URI("jar:file:")
List<Dependency> deps = DependencyResolver.resolve(uri)

then:
deps.isEmpty()
}

void 'jar unknown'() throws IOException {
when:
URI uri = new URI("jar:unknown")
List<Dependency> deps = DependencyResolver.resolve(uri)

then:
deps.isEmpty()
}

void 'spring boot dependency without maven metadata'() throws IOException {
given:
def innerJarData = new ByteArrayOutputStream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,9 @@ class JarReaderSpecification extends DepSpecification {
void 'read nested jar'() {
given:
String outerPath = getJar("spring-boot-app.jar").getAbsolutePath()
String jarPath = "$outerPath!/BOOT-INF/lib/opentracing-util-0.33.0.jar"

when:
def result = JarReader.readNestedJarFile(jarPath)

then:
result.jarName == "opentracing-util-0.33.0.jar"
result.pomProperties.size() == 1
def properties = result.pomProperties['META-INF/maven/io.opentracing/opentracing-util/pom.properties']
properties.groupId == "io.opentracing"
properties.artifactId == "opentracing-util"
properties.version == "0.33.0"
result.manifest != null
result.manifest.getValue("Automatic-Module-Name") == "io.opentracing.util"
}

void 'read nested jar with ending separator'() {
given:
String outerPath = getJar("spring-boot-app.jar").getAbsolutePath()
String jarPath = "$outerPath!/BOOT-INF/lib/opentracing-util-0.33.0.jar!/"

when:
def result = JarReader.readNestedJarFile(jarPath)
def result = JarReader.readNestedJarFile(outerPath, "BOOT-INF/lib/opentracing-util-0.33.0.jar")

then:
result.jarName == "opentracing-util-0.33.0.jar"
Expand All @@ -99,11 +79,8 @@ class JarReaderSpecification extends DepSpecification {
}

void 'non-existent outer jar for nested jar'() {
given:
String jarPath = "non-existent.jar!/BOOT-INF/lib/opentracing-util-0.33.0.jar!/"

when:
JarReader.readNestedJarFile(jarPath)
JarReader.readNestedJarFile("non-existent.jar", "BOOT-INF/lib/opentracing-util-0.33.0.jar")

then:
thrown(IOException)
Expand All @@ -112,34 +89,27 @@ class JarReaderSpecification extends DepSpecification {
void 'non-existent inner jar for nested jar'() {
given:
String outerPath = getJar("spring-boot-app.jar").getAbsolutePath()
String jarPath = "$outerPath!/BOOT-INF/lib/non-existent.jar"

when:
JarReader.readNestedJarFile(jarPath)
JarReader.readNestedJarFile(outerPath, "BOOT-INF/lib/non-existent.jar")

then:
thrown(IOException)
}

void 'doubly nested jar path'() {
given:
String jarPath = "non-existent.jar!/BOOT-INF/lib/opentracing-util-0.33.0.jar!/third"

when:
JarReader.readNestedJarFile(jarPath)
JarReader.readNestedJarFile("non-existent.jar", "BOOT-INF/lib/opentracing-util-0.33.0.jar/third")

then:
thrown(IOException)
}

void 'lack of nested jar path'() {
given:
String jarPath = "non-existent.jar"

void 'empty nested jar path'() {
when:
JarReader.readNestedJarFile(jarPath)
JarReader.readNestedJarFile("non-existent.jar", "")

then:
thrown(IllegalArgumentException)
thrown(IOException)
}
}

0 comments on commit e7571fd

Please sign in to comment.