Merge pull request #295 from marschall/SPR-10608

# By Philippe Marschall
* SPR-10608-rebase:
  Introduce java.nio.file.Path based Resource
This commit is contained in:
Phillip Webb
2013-06-03 16:25:56 -07:00
3 changed files with 531 additions and 0 deletions

View File

@@ -0,0 +1,253 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.springframework.util.Assert;
/**
* {@link Resource} implementation for {@code java.nio.file.Path} handles.
* Supports resolution as File, and also as URL.
* Implements the extended {@link WritableResource} interface.
*
* @author Philippe Marschall
* @since 4.0
* @see java.nio.file.Path
*/
public class PathResource extends AbstractResource implements WritableResource {
private final Path path;
/**
* Create a new PathResource from a Path handle.
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built <i>underneath</i> the
* given root:
* e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
* @param path a Path handle
*/
public PathResource(Path path) {
Assert.notNull(path, "Path must not be null");
this.path = path.normalize();
}
/**
* Create a new PathResource from a Path handle.
* Create a new PathResource from a Path handle.
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built <i>underneath</i> the
* given root:
* e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
* @param path a path
* @see java.nio.file.Paths#get(String, String...)
*/
public PathResource(String path) {
Assert.notNull(path, "Path must not be null");
this.path = Paths.get(path).normalize();
}
/**
* Create a new PathResource from a Path handle.
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built <i>underneath</i> the
* given root:
* e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
* @see java.nio.file.Paths#get(URI)
* @param uri a path URI
*/
public PathResource(URI uri) {
Assert.notNull(uri, "URI must not be null");
this.path = Paths.get(uri).normalize();
}
/**
* Return the file path for this resource.
*/
public final String getPath() {
return this.path.toString();
}
/**
* This implementation returns whether the underlying file exists.
* @see org.springframework.core.io.PathResource#exists()
*/
@Override
public boolean exists() {
return Files.exists(this.path);
}
/**
* This implementation checks whether the underlying file is marked as readable
* (and corresponds to an actual file with content, not to a directory).
* @see java.nio.file.Files#isReadable(Path)
* @see java.nio.file.Files#isDirectory(Path, java.nio.file.LinkOption...)
*/
@Override
public boolean isReadable() {
return (Files.isReadable(this.path) && !Files.isDirectory(this.path));
}
/**
* This implementation opens a InputStream for the underlying file.
* @see java.nio.file.spi.FileSystemProvider#newInputStream(Path, OpenOption...)
*/
@Override
public InputStream getInputStream() throws IOException {
if(!exists()) {
throw new FileNotFoundException(getPath() + " (No such file or directory)");
}
if(Files.isDirectory(this.path)) {
throw new FileNotFoundException(getPath() + " (Is a directory)");
}
return Files.newInputStream(this.path);
}
/**
* This implementation returns a URL for the underlying file.
* @see java.nio.file.Path#toUri()
* @see java.net.URI#toURL()
*/
@Override
public URL getURL() throws IOException {
return this.path.toUri().toURL();
}
/**
* This implementation returns a URI for the underlying file.
* @see java.nio.file.Path#toUri()
*/
@Override
public URI getURI() throws IOException {
return this.path.toUri();
}
/**
* This implementation returns the underlying File reference.
*/
@Override
public File getFile() throws IOException {
try {
return this.path.toFile();
}
catch (UnsupportedOperationException ex) {
// only Paths on the default file system can be converted to a File
// do exception translation for cases where conversion is not possible
throw new FileNotFoundException(this.path + " cannot be resolved to "
+ "absolute file path");
}
}
/**
* This implementation returns the underlying File's length.
*/
@Override
public long contentLength() throws IOException {
return Files.size(this.path);
}
/**
* This implementation returns the underlying File's timestamp.
* @see java.nio.file.Files#getLastModifiedTime(Path, java.nio.file.LinkOption...)
*/
@Override
public long lastModified() throws IOException {
// we can not use the super class method since it uses conversion to a File and
// only Paths on the default file system can be converted to a File
return Files.getLastModifiedTime(path).toMillis();
}
/**
* This implementation creates a FileResource, applying the given path
* relative to the path of the underlying file of this resource descriptor.
* @see java.nio.file.Path#resolve(String)
*/
@Override
public Resource createRelative(String relativePath) throws IOException {
return new PathResource(this.path.resolve(relativePath));
}
/**
* This implementation returns the name of the file.
* @see java.nio.file.Path#getFileName()
*/
@Override
public String getFilename() {
return this.path.getFileName().toString();
}
@Override
public String getDescription() {
return "path [" + this.path.toAbsolutePath() + "]";
}
// implementation of WritableResource
/**
* This implementation checks whether the underlying file is marked as writable
* (and corresponds to an actual file with content, not to a directory).
* @see java.nio.file.Files#isWritable(Path)
* @see java.nio.file.Files#isDirectory(Path, java.nio.file.LinkOption...)
*/
@Override
public boolean isWritable() {
return Files.isWritable(this.path) && !Files.isDirectory(this.path);
}
/**
* This implementation opens a OutputStream for the underlying file.
* @see java.nio.file.spi.FileSystemProvider#newOutputStream(Path, OpenOption...)
*/
@Override
public OutputStream getOutputStream() throws IOException {
if(Files.isDirectory(this.path)) {
throw new FileNotFoundException(getPath() + " (Is a directory)");
}
return Files.newOutputStream(this.path);
}
/**
* This implementation compares the underlying Path references.
*/
@Override
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof PathResource && this.path.equals(((PathResource) obj).path)));
}
/**
* This implementation returns the hash code of the underlying Path reference.
*/
@Override
public int hashCode() {
return this.path.hashCode();
}
}

View File

@@ -42,6 +42,7 @@ import java.net.URL;
* @see UrlResource
* @see ByteArrayResource
* @see InputStreamResource
* @see PathResource
*/
public interface Resource extends InputStreamSource {

View File

@@ -0,0 +1,277 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.springframework.util.FileCopyUtils;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/**
* Unit tests for the {@link PathResource} class.
*
* @author Philippe Marschall
* @author Phillip Webb
*/
public class PathResourceTests {
private static final String TEST_DIR = "src/test/java/org/springframework/core/io";
private static final String TEST_FILE = "src/test/java/org/springframework/core/io/example.properties";
private static final String NON_EXISTING_FILE = "src/test/java/org/springframework/core/io/doesnotexist.properties";
@Rule
public ExpectedException thrown = ExpectedException.none();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void nullPath() throws Exception {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Path must not be null");
new PathResource((Path) null);
}
@Test
public void nullPathString() throws Exception {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Path must not be null");
new PathResource((String) null);
}
@Test
public void nullUri() throws Exception {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("URI must not be null");
new PathResource((URI) null);
}
@Test
public void createFromPath() throws Exception {
Path path = Paths.get(TEST_FILE);
PathResource resource = new PathResource(path);
assertThat(resource.getPath(), equalTo(TEST_FILE));
}
@Test
public void createFromString() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.getPath(), equalTo(TEST_FILE));
}
@Test
public void createFromUri() throws Exception {
File file = new File(TEST_FILE);
PathResource resource = new PathResource(file.toURI());
assertThat(resource.getPath(), equalTo(file.getAbsoluteFile().toString()));
}
@Test
public void getPathForFile() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.getPath(), equalTo(TEST_FILE));
}
@Test
public void getPathForDir() throws Exception {
PathResource resource = new PathResource(TEST_DIR);
assertThat(resource.getPath(), equalTo(TEST_DIR));
}
@Test
public void fileExists() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.exists(), equalTo(true));
}
@Test
public void dirExists() throws Exception {
PathResource resource = new PathResource(TEST_DIR);
assertThat(resource.exists(), equalTo(true));
}
@Test
public void fileDoesNotExist() throws Exception {
PathResource resource = new PathResource(NON_EXISTING_FILE);
assertThat(resource.exists(), equalTo(false));
}
@Test
public void fileIsReadable() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.isReadable(), equalTo(true));
}
@Test
public void doesNotExistIsNotReadable() throws Exception {
PathResource resource = new PathResource(NON_EXISTING_FILE);
assertThat(resource.isReadable(), equalTo(false));
}
@Test
public void directoryIsNotReadable() throws Exception {
PathResource resource = new PathResource(TEST_DIR);
assertThat(resource.isReadable(), equalTo(false));
}
@Test
public void getInputStream() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
assertThat(bytes.length, greaterThan(0));
}
@Test
public void getInputStreamForDir() throws Exception {
PathResource resource = new PathResource(TEST_DIR);
thrown.expect(FileNotFoundException.class);
resource.getInputStream();
}
@Test
public void getInputStreamDoesNotExist() throws Exception {
PathResource resource = new PathResource(NON_EXISTING_FILE);
thrown.expect(FileNotFoundException.class);
resource.getInputStream();
}
@Test
public void getUrl() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
assertTrue(resource.getURL().toString().endsWith(TEST_FILE));
}
@Test
public void getUri() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
assertTrue(resource.getURI().toString().endsWith(TEST_FILE));
}
@Test
public void getFile() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
File file = new File(TEST_FILE);
assertThat(resource.getFile().getAbsoluteFile(), equalTo(file.getAbsoluteFile()));
}
@Test
public void getFileUnsupported() throws Exception {
Path path = mock(Path.class);
given(path.normalize()).willReturn(path);
given(path.toFile()).willThrow(new UnsupportedOperationException());
PathResource resource = new PathResource(path);
thrown.expect(FileNotFoundException.class);
resource.getFile();
}
@Test
public void contentLength() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
File file = new File(TEST_FILE);
assertThat(resource.contentLength(), equalTo(file.length()));
}
@Test
public void contentLengthForDirectory() throws Exception {
PathResource resource = new PathResource(TEST_DIR);
File file = new File(TEST_DIR);
assertThat(resource.contentLength(), equalTo(file.length()));
}
@Test
public void lastModified() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
File file = new File(TEST_FILE);
assertThat(resource.lastModified(), equalTo(file.lastModified()));
}
@Test
public void createRelativeFromDir() throws Exception {
Resource resource = new PathResource(TEST_DIR).createRelative("example.properties");
assertThat(resource, equalTo((Resource) new PathResource(TEST_FILE)));
}
@Test
public void createRelativeFromFile() throws Exception {
Resource resource = new PathResource(TEST_FILE).createRelative("../example.properties");
assertThat(resource, equalTo((Resource) new PathResource(TEST_FILE)));
}
@Test
public void filename() throws Exception {
Resource resource = new PathResource(TEST_FILE);
assertThat(resource.getFilename(), equalTo("example.properties"));
}
@Test
public void description() throws Exception {
Resource resource = new PathResource(TEST_FILE);
assertThat(resource.getDescription(), containsString("path ["));
assertThat(resource.getDescription(), containsString(TEST_FILE));
}
@Test
public void fileIsWritable() throws Exception {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.isWritable(), equalTo(true));
}
@Test
public void directoryIsNotWritable() throws Exception {
PathResource resource = new PathResource(TEST_DIR);
assertThat(resource.isWritable(), equalTo(false));
}
@Test
public void outputStream() throws Exception {
PathResource resource = new PathResource(temporaryFolder.newFile("test").toPath());
FileCopyUtils.copy("test".getBytes(), resource.getOutputStream());
assertThat(resource.contentLength(), equalTo(4L));
}
@Test
public void doesNotExistOutputStream() throws Exception {
File file = temporaryFolder.newFile("test");
file.delete();
PathResource resource = new PathResource(file.toPath());
FileCopyUtils.copy("test".getBytes(), resource.getOutputStream());
assertThat(resource.contentLength(), equalTo(4L));
}
@Test
public void directoryOutputStream() throws Exception {
PathResource resource = new PathResource(TEST_DIR);
thrown.expect(FileNotFoundException.class);
resource.getOutputStream();
}
}