Skip to content

Commit c0a0c4c

Browse files
committed
Show the source jar of a ClasspathResource
Update `TextResourceOrigin` so that it shows the source jar file of a `ClasspathResource`. Closes gh-23019
1 parent 04a40a4 commit c0a0c4c

File tree

4 files changed

+195
-4
lines changed

4 files changed

+195
-4
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.origin;
18+
19+
import java.net.URI;
20+
21+
/**
22+
* Simple class that understands Jar URLs can can provide short descriptions.
23+
*
24+
* @author Phillip Webb
25+
*/
26+
final class JarUri {
27+
28+
private static final String JAR_SCHEME = "jar:";
29+
30+
private static final String JAR_EXTENSION = ".jar";
31+
32+
private final String uri;
33+
34+
private final String description;
35+
36+
private JarUri(String uri) {
37+
this.uri = uri;
38+
this.description = extractDescription(uri);
39+
}
40+
41+
private String extractDescription(String uri) {
42+
uri = uri.substring(JAR_SCHEME.length());
43+
int firstDotJar = uri.indexOf(JAR_EXTENSION);
44+
String firstJar = getFilename(uri.substring(0, firstDotJar + JAR_EXTENSION.length()));
45+
uri = uri.substring(firstDotJar + JAR_EXTENSION.length());
46+
int lastDotJar = uri.lastIndexOf(JAR_EXTENSION);
47+
if (lastDotJar == -1) {
48+
return firstJar;
49+
}
50+
return firstJar + uri.substring(0, lastDotJar + JAR_EXTENSION.length());
51+
}
52+
53+
private String getFilename(String string) {
54+
int lastSlash = string.lastIndexOf('/');
55+
return (lastSlash == -1) ? string : string.substring(lastSlash + 1);
56+
}
57+
58+
String getDescription() {
59+
return this.description;
60+
}
61+
62+
String getDescription(String existing) {
63+
return existing + " from " + this.description;
64+
}
65+
66+
@Override
67+
public String toString() {
68+
return this.uri;
69+
}
70+
71+
static JarUri from(URI uri) {
72+
return from(uri.toString());
73+
}
74+
75+
static JarUri from(String uri) {
76+
if (uri.startsWith(JAR_SCHEME) && uri.contains(JAR_EXTENSION)) {
77+
return new JarUri(uri);
78+
}
79+
return null;
80+
}
81+
82+
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/origin/TextResourceOrigin.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package org.springframework.boot.origin;
1818

19+
import java.io.IOException;
20+
21+
import org.springframework.core.io.ClassPathResource;
1922
import org.springframework.core.io.Resource;
2023
import org.springframework.util.ObjectUtils;
2124

@@ -91,13 +94,38 @@ public int hashCode() {
9194
@Override
9295
public String toString() {
9396
StringBuilder result = new StringBuilder();
94-
result.append((this.resource != null) ? this.resource.getDescription() : "unknown resource [?]");
97+
result.append(getResourceDescription(this.resource));
9598
if (this.location != null) {
96-
result.append(":").append(this.location);
99+
result.append(" - ").append(this.location);
97100
}
98101
return result.toString();
99102
}
100103

104+
private String getResourceDescription(Resource resource) {
105+
if (resource instanceof OriginTrackedResource) {
106+
return getResourceDescription(((OriginTrackedResource) resource).getResource());
107+
}
108+
if (resource == null) {
109+
return "unknown resource [?]";
110+
}
111+
if (resource instanceof ClassPathResource) {
112+
return getResourceDescription((ClassPathResource) resource);
113+
}
114+
return resource.getDescription();
115+
}
116+
117+
private String getResourceDescription(ClassPathResource resource) {
118+
try {
119+
JarUri jarUri = JarUri.from(resource.getURI());
120+
if (jarUri != null) {
121+
return jarUri.getDescription(resource.getDescription());
122+
}
123+
}
124+
catch (IOException ex) {
125+
}
126+
return resource.getDescription();
127+
}
128+
101129
/**
102130
* A location (line and column number) within the resource.
103131
*/
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.origin;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
23+
/**
24+
* @author pwebb
25+
*/
26+
class JarUriTests {
27+
28+
@Test
29+
void describeBootInfClassesUri() {
30+
JarUri uri = JarUri.from("jar:file:/home/user/project/target/project-0.0.1-SNAPSHOT.jar"
31+
+ "!/BOOT-INF/classes!/application.properties");
32+
assertThat(uri.getDescription()).isEqualTo("project-0.0.1-SNAPSHOT.jar");
33+
}
34+
35+
@Test
36+
void describeBootInfLibUri() {
37+
JarUri uri = JarUri.from("jar:file:/home/user/project/target/project-0.0.1-SNAPSHOT.jar"
38+
+ "!/BOOT-INF/lib/nested.jar!/application.properties");
39+
assertThat(uri.getDescription()).isEqualTo("project-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/nested.jar");
40+
}
41+
42+
@Test
43+
void describeRegularJar() {
44+
JarUri uri = JarUri
45+
.from("jar:file:/home/user/project/target/project-0.0.1-SNAPSHOT.jar!/application.properties");
46+
assertThat(uri.getDescription()).isEqualTo("project-0.0.1-SNAPSHOT.jar");
47+
}
48+
49+
@Test
50+
void getDescriptionMergedWithExisting() {
51+
JarUri uri = JarUri.from("jar:file:/project-0.0.1-SNAPSHOT.jar!/application.properties");
52+
assertThat(uri.getDescription("classpath: [application.properties]"))
53+
.isEqualTo("classpath: [application.properties] from project-0.0.1-SNAPSHOT.jar");
54+
}
55+
56+
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/origin/TextResourceOriginTests.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616

1717
package org.springframework.boot.origin;
1818

19+
import java.io.IOException;
20+
import java.net.URI;
21+
import java.net.URISyntaxException;
22+
1923
import org.junit.jupiter.api.Test;
2024

2125
import org.springframework.boot.origin.TextResourceOrigin.Location;
@@ -95,14 +99,14 @@ void toStringReturnsNiceString() {
9599
ClassPathResource resource = new ClassPathResource("foo.txt");
96100
Location location = new Location(1, 2);
97101
TextResourceOrigin origin = new TextResourceOrigin(resource, location);
98-
assertThat(origin.toString()).isEqualTo("class path resource [foo.txt]:2:3");
102+
assertThat(origin.toString()).isEqualTo("class path resource [foo.txt] - 2:3");
99103
}
100104

101105
@Test
102106
void toStringWhenResourceIsNullReturnsNiceString() {
103107
Location location = new Location(1, 2);
104108
TextResourceOrigin origin = new TextResourceOrigin(null, location);
105-
assertThat(origin.toString()).isEqualTo("unknown resource [?]:2:3");
109+
assertThat(origin.toString()).isEqualTo("unknown resource [?] - 2:3");
106110
}
107111

108112
@Test
@@ -112,6 +116,27 @@ void toStringWhenLocationIsNullReturnsNiceString() {
112116
assertThat(origin.toString()).isEqualTo("class path resource [foo.txt]");
113117
}
114118

119+
@Test
120+
void toStringWhenResourceIsClasspathResourceReturnsToStringWithJar() {
121+
ClassPathResource resource = new ClassPathResource("foo.txt") {
122+
123+
@Override
124+
public URI getURI() throws IOException {
125+
try {
126+
return new URI("jar:file:/home/user/project/target/project-0.0.1-SNAPSHOT.jar"
127+
+ "!/BOOT-INF/classes!/foo.txt");
128+
}
129+
catch (URISyntaxException ex) {
130+
throw new IllegalStateException(ex);
131+
}
132+
}
133+
134+
};
135+
Location location = new Location(1, 2);
136+
TextResourceOrigin origin = new TextResourceOrigin(resource, location);
137+
assertThat(origin.toString()).isEqualTo("class path resource [foo.txt] from project-0.0.1-SNAPSHOT.jar - 2:3");
138+
}
139+
115140
@Test
116141
void locationEqualsAndHashCodeUsesLineAndColumn() {
117142
Location location1 = new Location(1, 2);

0 commit comments

Comments
 (0)