Fix regression in ClassPathResource descriptions

ClassPathResource.getDescription() now returns consistent, meaningful
results for all variants of ClassPathResource's constructors.

Issue: SPR-9413
This commit is contained in:
Sam Brannen 2012-05-16 04:24:53 +02:00
parent 500a4dd995
commit b50f6e19a6
2 changed files with 91 additions and 27 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -35,6 +35,7 @@ import org.springframework.util.StringUtils;
* Always supports resolution as URL. * Always supports resolution as URL.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen
* @since 28.12.2003 * @since 28.12.2003
* @see java.lang.ClassLoader#getResourceAsStream(String) * @see java.lang.ClassLoader#getResourceAsStream(String)
* @see java.lang.Class#getResourceAsStream(String) * @see java.lang.Class#getResourceAsStream(String)
@ -108,7 +109,6 @@ public class ClassPathResource extends AbstractFileResolvingResource {
this.clazz = clazz; this.clazz = clazz;
} }
/** /**
* Return the path for this resource (as resource path within the class path). * Return the path for this resource (as resource path within the class path).
*/ */
@ -123,7 +123,6 @@ public class ClassPathResource extends AbstractFileResolvingResource {
return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader()); return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader());
} }
/** /**
* This implementation checks for the resolution of a resource URL. * This implementation checks for the resolution of a resource URL.
* @see java.lang.ClassLoader#getResource(String) * @see java.lang.ClassLoader#getResource(String)
@ -155,8 +154,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
is = this.classLoader.getResourceAsStream(this.path); is = this.classLoader.getResourceAsStream(this.path);
} }
if (is == null) { if (is == null) {
throw new FileNotFoundException( throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
getDescription() + " cannot be opened because it does not exist");
} }
return is; return is;
} }
@ -176,8 +174,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
url = this.classLoader.getResource(this.path); url = this.classLoader.getResource(this.path);
} }
if (url == null) { if (url == null) {
throw new FileNotFoundException( throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
getDescription() + " cannot be resolved to URL because it does not exist");
} }
return url; return url;
} }
@ -209,17 +206,22 @@ public class ClassPathResource extends AbstractFileResolvingResource {
public String getDescription() { public String getDescription() {
StringBuilder builder = new StringBuilder("class path resource ["); StringBuilder builder = new StringBuilder("class path resource [");
if (this.clazz != null) { String pathToUse = path;
if (this.clazz != null && !pathToUse.startsWith("/")) {
builder.append(ClassUtils.classPackageAsResourcePath(this.clazz)); builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
builder.append('/'); builder.append('/');
} }
builder.append(this.path); if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
builder.append(pathToUse);
builder.append(']'); builder.append(']');
return builder.toString(); return builder.toString();
} }
/** /**
* This implementation compares the underlying class path locations. * This implementation compares the underlying class path locations.
*/ */
@ -230,9 +232,9 @@ public class ClassPathResource extends AbstractFileResolvingResource {
} }
if (obj instanceof ClassPathResource) { if (obj instanceof ClassPathResource) {
ClassPathResource otherRes = (ClassPathResource) obj; ClassPathResource otherRes = (ClassPathResource) obj;
return (this.path.equals(otherRes.path) && return (this.path.equals(otherRes.path)
ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && && ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals(
ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz)); this.clazz, otherRes.clazz));
} }
return false; return false;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,24 +17,59 @@
package org.springframework.core.io; package org.springframework.core.io;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.junit.internal.matchers.StringContains.containsString; import static org.junit.internal.matchers.StringContains.containsString;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Test; import org.junit.Test;
/** /**
* Unit tests cornering bug SPR-6888. * Unit tests that serve as regression tests for the bugs described in SPR-6888
* and SPR-9413.
* *
* @author Chris Beams * @author Chris Beams
* @author Sam Brannen
*/ */
public class ClassPathResourceTests { public class ClassPathResourceTests {
private static final String PACKAGE_PATH = "org/springframework/core/io"; private static final String PACKAGE_PATH = "org/springframework/core/io";
private static final String RESOURCE_NAME = "notexist.xml"; private static final String NONEXISTENT_RESOURCE_NAME = "nonexistent.xml";
private static final String FQ_RESOURCE_PATH = PACKAGE_PATH + '/' + RESOURCE_NAME; private static final String FQ_RESOURCE_PATH = PACKAGE_PATH + '/' + NONEXISTENT_RESOURCE_NAME;
/**
* Absolute path version of {@link #FQ_RESOURCE_PATH}.
*/
private static final String FQ_RESOURCE_PATH_WITH_LEADING_SLASH = '/' + FQ_RESOURCE_PATH;
private static final Pattern DESCRIPTION_PATTERN = Pattern.compile("^class path resource \\[(.+?)\\]$");
private void assertDescriptionContainsExpectedPath(ClassPathResource resource, String expectedPath) {
Matcher matcher = DESCRIPTION_PATTERN.matcher(resource.getDescription());
assertTrue(matcher.matches());
assertEquals(1, matcher.groupCount());
String match = matcher.group(1);
assertEquals(expectedPath, match);
}
private void assertExceptionContainsFullyQualifiedPath(ClassPathResource resource) {
try {
resource.getInputStream();
fail("FileNotFoundException expected for resource: " + resource);
}
catch (IOException ex) {
assertThat(ex, instanceOf(FileNotFoundException.class));
assertThat(ex.getMessage(), containsString(FQ_RESOURCE_PATH));
}
}
@Test @Test
public void stringConstructorRaisesExceptionWithFullyQualifiedPath() { public void stringConstructorRaisesExceptionWithFullyQualifiedPath() {
@ -43,21 +78,48 @@ public class ClassPathResourceTests {
@Test @Test
public void classLiteralConstructorRaisesExceptionWithFullyQualifiedPath() { public void classLiteralConstructorRaisesExceptionWithFullyQualifiedPath() {
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(RESOURCE_NAME, this.getClass())); assertExceptionContainsFullyQualifiedPath(new ClassPathResource(NONEXISTENT_RESOURCE_NAME, this.getClass()));
} }
@Test @Test
public void classLoaderConstructorRaisesExceptionWithFullyQualifiedPath() { public void classLoaderConstructorRaisesExceptionWithFullyQualifiedPath() {
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(FQ_RESOURCE_PATH, this.getClass().getClassLoader())); assertExceptionContainsFullyQualifiedPath(new ClassPathResource(FQ_RESOURCE_PATH,
this.getClass().getClassLoader()));
} }
private void assertExceptionContainsFullyQualifiedPath(ClassPathResource resource) { @Test
try { public void getDescriptionWithStringConstructor() {
resource.getInputStream(); assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH), FQ_RESOURCE_PATH);
fail("FileNotFoundException expected for resource: " + resource);
} catch (IOException ex) {
assertThat(ex, instanceOf(FileNotFoundException.class));
assertThat(ex.getMessage(), containsString(FQ_RESOURCE_PATH));
} }
@Test
public void getDescriptionWithStringConstructorAndLeadingSlash() {
assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH),
FQ_RESOURCE_PATH);
} }
@Test
public void getDescriptionWithClassLiteralConstructor() {
assertDescriptionContainsExpectedPath(new ClassPathResource(NONEXISTENT_RESOURCE_NAME, this.getClass()),
FQ_RESOURCE_PATH);
}
@Test
public void getDescriptionWithClassLiteralConstructorAndLeadingSlash() {
assertDescriptionContainsExpectedPath(
new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH, this.getClass()), FQ_RESOURCE_PATH);
}
@Test
public void getDescriptionWithClassLoaderConstructor() {
assertDescriptionContainsExpectedPath(
new ClassPathResource(FQ_RESOURCE_PATH, this.getClass().getClassLoader()), FQ_RESOURCE_PATH);
}
@Test
public void getDescriptionWithClassLoaderConstructorAndLeadingSlash() {
assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH,
this.getClass().getClassLoader()), FQ_RESOURCE_PATH);
}
} }