Skip to content

Commit b50f6e1

Browse files
committed
Fix regression in ClassPathResource descriptions
ClassPathResource.getDescription() now returns consistent, meaningful results for all variants of ClassPathResource's constructors. Issue: SPR-9413
1 parent 500a4dd commit b50f6e1

File tree

2 files changed

+91
-27
lines changed

2 files changed

+91
-27
lines changed

spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
3535
* Always supports resolution as URL.
3636
*
3737
* @author Juergen Hoeller
38+
* @author Sam Brannen
3839
* @since 28.12.2003
3940
* @see java.lang.ClassLoader#getResourceAsStream(String)
4041
* @see java.lang.Class#getResourceAsStream(String)
@@ -108,7 +109,6 @@ protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz
108109
this.clazz = clazz;
109110
}
110111

111-
112112
/**
113113
* Return the path for this resource (as resource path within the class path).
114114
*/
@@ -123,7 +123,6 @@ public final ClassLoader getClassLoader() {
123123
return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader());
124124
}
125125

126-
127126
/**
128127
* This implementation checks for the resolution of a resource URL.
129128
* @see java.lang.ClassLoader#getResource(String)
@@ -155,8 +154,7 @@ public InputStream getInputStream() throws IOException {
155154
is = this.classLoader.getResourceAsStream(this.path);
156155
}
157156
if (is == null) {
158-
throw new FileNotFoundException(
159-
getDescription() + " cannot be opened because it does not exist");
157+
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
160158
}
161159
return is;
162160
}
@@ -176,8 +174,7 @@ public URL getURL() throws IOException {
176174
url = this.classLoader.getResource(this.path);
177175
}
178176
if (url == null) {
179-
throw new FileNotFoundException(
180-
getDescription() + " cannot be resolved to URL because it does not exist");
177+
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
181178
}
182179
return url;
183180
}
@@ -209,17 +206,22 @@ public String getFilename() {
209206
public String getDescription() {
210207
StringBuilder builder = new StringBuilder("class path resource [");
211208

212-
if (this.clazz != null) {
209+
String pathToUse = path;
210+
211+
if (this.clazz != null && !pathToUse.startsWith("/")) {
213212
builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
214213
builder.append('/');
215214
}
216215

217-
builder.append(this.path);
216+
if (pathToUse.startsWith("/")) {
217+
pathToUse = pathToUse.substring(1);
218+
}
219+
220+
builder.append(pathToUse);
218221
builder.append(']');
219222
return builder.toString();
220223
}
221224

222-
223225
/**
224226
* This implementation compares the underlying class path locations.
225227
*/
@@ -230,9 +232,9 @@ public boolean equals(Object obj) {
230232
}
231233
if (obj instanceof ClassPathResource) {
232234
ClassPathResource otherRes = (ClassPathResource) obj;
233-
return (this.path.equals(otherRes.path) &&
234-
ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) &&
235-
ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz));
235+
return (this.path.equals(otherRes.path)
236+
&& ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals(
237+
this.clazz, otherRes.clazz));
236238
}
237239
return false;
238240
}
Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,24 +17,59 @@
1717
package org.springframework.core.io;
1818

1919
import static org.hamcrest.CoreMatchers.instanceOf;
20+
import static org.junit.Assert.assertEquals;
2021
import static org.junit.Assert.assertThat;
22+
import static org.junit.Assert.assertTrue;
2123
import static org.junit.Assert.fail;
2224
import static org.junit.internal.matchers.StringContains.containsString;
2325

2426
import java.io.FileNotFoundException;
2527
import java.io.IOException;
28+
import java.util.regex.Matcher;
29+
import java.util.regex.Pattern;
2630

2731
import org.junit.Test;
2832

2933
/**
30-
* Unit tests cornering bug SPR-6888.
34+
* Unit tests that serve as regression tests for the bugs described in SPR-6888
35+
* and SPR-9413.
3136
*
3237
* @author Chris Beams
38+
* @author Sam Brannen
3339
*/
3440
public class ClassPathResourceTests {
41+
3542
private static final String PACKAGE_PATH = "org/springframework/core/io";
36-
private static final String RESOURCE_NAME = "notexist.xml";
37-
private static final String FQ_RESOURCE_PATH = PACKAGE_PATH + '/' + RESOURCE_NAME;
43+
private static final String NONEXISTENT_RESOURCE_NAME = "nonexistent.xml";
44+
private static final String FQ_RESOURCE_PATH = PACKAGE_PATH + '/' + NONEXISTENT_RESOURCE_NAME;
45+
46+
/**
47+
* Absolute path version of {@link #FQ_RESOURCE_PATH}.
48+
*/
49+
private static final String FQ_RESOURCE_PATH_WITH_LEADING_SLASH = '/' + FQ_RESOURCE_PATH;
50+
51+
private static final Pattern DESCRIPTION_PATTERN = Pattern.compile("^class path resource \\[(.+?)\\]$");
52+
53+
54+
private void assertDescriptionContainsExpectedPath(ClassPathResource resource, String expectedPath) {
55+
Matcher matcher = DESCRIPTION_PATTERN.matcher(resource.getDescription());
56+
assertTrue(matcher.matches());
57+
assertEquals(1, matcher.groupCount());
58+
String match = matcher.group(1);
59+
60+
assertEquals(expectedPath, match);
61+
}
62+
63+
private void assertExceptionContainsFullyQualifiedPath(ClassPathResource resource) {
64+
try {
65+
resource.getInputStream();
66+
fail("FileNotFoundException expected for resource: " + resource);
67+
}
68+
catch (IOException ex) {
69+
assertThat(ex, instanceOf(FileNotFoundException.class));
70+
assertThat(ex.getMessage(), containsString(FQ_RESOURCE_PATH));
71+
}
72+
}
3873

3974
@Test
4075
public void stringConstructorRaisesExceptionWithFullyQualifiedPath() {
@@ -43,21 +78,48 @@ public void stringConstructorRaisesExceptionWithFullyQualifiedPath() {
4378

4479
@Test
4580
public void classLiteralConstructorRaisesExceptionWithFullyQualifiedPath() {
46-
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(RESOURCE_NAME, this.getClass()));
81+
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(NONEXISTENT_RESOURCE_NAME, this.getClass()));
4782
}
4883

4984
@Test
5085
public void classLoaderConstructorRaisesExceptionWithFullyQualifiedPath() {
51-
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(FQ_RESOURCE_PATH, this.getClass().getClassLoader()));
86+
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(FQ_RESOURCE_PATH,
87+
this.getClass().getClassLoader()));
5288
}
5389

54-
private void assertExceptionContainsFullyQualifiedPath(ClassPathResource resource) {
55-
try {
56-
resource.getInputStream();
57-
fail("FileNotFoundException expected for resource: " + resource);
58-
} catch (IOException ex) {
59-
assertThat(ex, instanceOf(FileNotFoundException.class));
60-
assertThat(ex.getMessage(), containsString(FQ_RESOURCE_PATH));
61-
}
90+
@Test
91+
public void getDescriptionWithStringConstructor() {
92+
assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH), FQ_RESOURCE_PATH);
93+
}
94+
95+
@Test
96+
public void getDescriptionWithStringConstructorAndLeadingSlash() {
97+
assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH),
98+
FQ_RESOURCE_PATH);
99+
}
100+
101+
@Test
102+
public void getDescriptionWithClassLiteralConstructor() {
103+
assertDescriptionContainsExpectedPath(new ClassPathResource(NONEXISTENT_RESOURCE_NAME, this.getClass()),
104+
FQ_RESOURCE_PATH);
62105
}
106+
107+
@Test
108+
public void getDescriptionWithClassLiteralConstructorAndLeadingSlash() {
109+
assertDescriptionContainsExpectedPath(
110+
new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH, this.getClass()), FQ_RESOURCE_PATH);
111+
}
112+
113+
@Test
114+
public void getDescriptionWithClassLoaderConstructor() {
115+
assertDescriptionContainsExpectedPath(
116+
new ClassPathResource(FQ_RESOURCE_PATH, this.getClass().getClassLoader()), FQ_RESOURCE_PATH);
117+
}
118+
119+
@Test
120+
public void getDescriptionWithClassLoaderConstructorAndLeadingSlash() {
121+
assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH,
122+
this.getClass().getClassLoader()), FQ_RESOURCE_PATH);
123+
}
124+
63125
}

0 commit comments

Comments
 (0)