Skip to content

Commit

Permalink
Merge pull request #538 from dbmdz/file-refactor
Browse files Browse the repository at this point in the history
commons-file: Add new FileSystemResourceIOException
  • Loading branch information
datazuul authored Jan 31, 2022
2 parents 9201637 + b3e220e commit 39682b6
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 32 deletions.
2 changes: 1 addition & 1 deletion dc-commons-file/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<name>DigitalCollections: Commons File</name>
<artifactId>dc-commons-file</artifactId>
<version>5.1.1-SNAPSHOT</version>
<version>5.2.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.digitalcollections.commons.file.backend;

import de.digitalcollections.model.exception.ResourceIOException;

/**
* A subtype of ResourceIOException to indicate that an honest-to-god actual IOException (as in
* harddisk broken, network share unavailable or a cosmic ray hit the CPU in just the right way for
* the syscall to fail.)
*/
public class FileSystemResourceIOException extends ResourceIOException {

public FileSystemResourceIOException(Throwable ex) {
super(ex);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.digitalcollections.commons.file.backend.api;

import de.digitalcollections.commons.file.backend.FileSystemResourceIOException;
import de.digitalcollections.model.exception.ResourceIOException;
import de.digitalcollections.model.exception.ResourceNotFoundException;
import de.digitalcollections.model.file.MimeType;
Expand Down Expand Up @@ -46,6 +47,8 @@ void assertReadability(FileResource resource)
* @param identifier identifier of FileResource, used to lookup URI for FileResource
* @param mimeType mimetype of the FileResource
* @return FileResource implementation matching mimetype and URI resolved using identifier.
* @throws FileSystemResourceIOException if there was a raw disk I/O error while locating the
* resource
* @throws ResourceIOException thrown if no URI can be resolved for FileResource with given
* mimetype and identifier
* @throws ResourceNotFoundException thrown if FileResource at resolved URI does not exist
Expand All @@ -56,6 +59,8 @@ FileResource find(String identifier, MimeType mimeType)
/**
* @param resourceUri URI for accessing FileResource data
* @return InputStream for reading FileResource data
* @throws FileSystemResourceIOException if there was a raw disk I/O error while locating the
* resource
* @throws ResourceIOException thrown if an IOExcpetion appears at reading FileResource data
* @throws ResourceNotFoundException thrown if FileResource at resolved URI does not exist
*/
Expand All @@ -64,6 +69,8 @@ FileResource find(String identifier, MimeType mimeType)
/**
* @param resource FileResource containing URI for accessing FileResource data
* @return InputStream for reading FileResource data
* @throws FileSystemResourceIOException if there was a raw disk I/O error while locating the
* resource
* @throws ResourceIOException thrown if an IOExcpetion appears at reading FileResource data
* @throws ResourceNotFoundException thrown if FileResource at resolved URI does not exist
*/
Expand All @@ -73,6 +80,8 @@ InputStream getInputStream(FileResource resource)
/**
* @param resource FileResource containing URI for accessing FileResource data
* @return Reader for InputStream of FileResource data
* @throws FileSystemResourceIOException if there was a raw disk I/O error while locating the
* resource
* @throws ResourceIOException thrown if an IOExcpetion appears at reading FileResource data
* @throws ResourceNotFoundException thrown if FileResource at resolved URI does not exist
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.digitalcollections.commons.file.backend.impl;

import de.digitalcollections.commons.file.backend.FileSystemResourceIOException;
import de.digitalcollections.commons.file.backend.api.FileResourceRepository;
import de.digitalcollections.commons.file.backend.api.IdentifierToFileResourceUriResolver;
import de.digitalcollections.model.exception.ResourceIOException;
Expand All @@ -19,6 +20,7 @@
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystemException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -38,6 +40,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Repository;
Expand Down Expand Up @@ -84,11 +87,10 @@ public void assertReadability(FileResource resource)
if (is.available() <= 0) {
throw new ResourceIOException("Cannot read " + resource.getFilename() + ": Empty file");
}
} catch (ResourceIOException e) {
throw new ResourceIOException(
"Cannot read " + resource.getFilename() + ": " + e.getMessage());
} catch (ResourceNotFoundException e) {
throw e;
} catch (FileSystemException e) {
throw new FileSystemResourceIOException(e);
} catch (Exception e) {
throw new ResourceIOException(
"Cannot read " + resource.getFilename() + ": " + e.getMessage());
Expand Down Expand Up @@ -173,6 +175,8 @@ private List<URI> expandWildcardFilenames(List<URI> candidates) throws ResourceI
continue;
} catch (MalformedURLException ex) {
throw new ResourceIOException("Invalid URL " + candidate.toString(), ex);
} catch (FileSystemException e) {
throw new FileSystemResourceIOException(e);
} catch (IOException ex) {
throw new ResourceIOException(
"Invalid file system access for " + candidate.toString(), ex);
Expand Down Expand Up @@ -205,36 +209,48 @@ public FileResource find(String identifier, MimeType mimeType)
}
final List<URI> expandedCandidates = expandWildcardFilenames(candidates);

URI uriCandidate;
if (overriddenDirectoryStream != null) {
// for testability
uriCandidate = expandedCandidates.get(0);
resource.setUri(uriCandidate);
resource.setUri(expandedCandidates.get(0));
} else {
uriCandidate =
expandedCandidates.stream()
.filter(
u ->
(resourceLoader.getResource(u.toString()).isReadable()
|| u.toString().startsWith("http")))
.findFirst()
.orElseThrow(
() ->
new ResourceIOException(
"Could not resolve identifier "
+ identifier
+ " with MIME type "
+ mimeType.getTypeName()
+ " to a readable Resource. Attempted URIs were "
+ candidates));
resource.setUri(uriCandidate);
for (URI u : expandedCandidates) {
if (u.getScheme().startsWith("http")) {
resource.setUri(u);
break;
}
Resource res = resourceLoader.getResource(u.toString());
if (res.isReadable()) {
resource.setUri(u);
break;
} else if (res instanceof FileSystemResource) {
// For resources from the file system 'unreadable' can mean a whole number of things.
// We'd like to differentiate between "there's a path with that name, but it's not
// readable" and "we can't tell if that path is not readable due to an IO exception".
// The former is something that should make us continue looking and the latter should
// result in an error. Getting the size gives us an IOException if it's not possible,
// contrary to `isReadable()` which just returns `false`.
try {
res.contentLength();
} catch (FileSystemException fsExc) {
// Can't determine file size due to low-level IO exception, exit with an error
throw new FileSystemResourceIOException(fsExc);
} catch (IOException e) {
// Other reasons, keep looking for matching candidates
}
}
}
if (resource.getUri() == null) {
throw new ResourceIOException(
"Could not resolve identifier "
+ identifier
+ " with MIME type "
+ mimeType.getTypeName()
+ " to a readable Resource. Attempted URIs were "
+ candidates);
}

// test if resource exists
Resource springResource = resourceLoader.getResource(uriCandidate.toString());
if (!uriCandidate.getScheme().startsWith("http") && !springResource.exists()) {
throw new ResourceNotFoundException(
"Resource not found at location '" + uriCandidate.toString() + "'");
}
Resource springResource = resourceLoader.getResource(resource.getUri().toString());
long lastModified = getLastModified(springResource);
if (lastModified != 0) {
// lastmodified by code in java.io.File#lastModified (is also used in Spring's
Expand Down Expand Up @@ -274,16 +290,24 @@ public InputStream getInputStream(URI resourceUri)
throw new ResourceNotFoundException("Resource not found at location '" + location + "'");
}
return resource.getInputStream();
} catch (FileSystemException e) {
throw new FileSystemResourceIOException(e);
} catch (IOException e) {
throw new ResourceIOException(e);
}
}

protected long getLastModified(Resource springResource) {
protected long getLastModified(Resource springResource) throws FileSystemResourceIOException {
try {
return springResource.lastModified();
} catch (FileNotFoundException e) {
LOGGER.warn("Resource " + springResource.toString() + " does not exist.");
LOGGER.warn("Resource " + springResource + " does not exist.");
} catch (FileSystemException e) {
LOGGER.error(
String.format(
"I/O error while trying to read timestamp from filesystem for %s", springResource),
e);
throw new FileSystemResourceIOException(e);
} catch (IOException ex) {
LOGGER.warn("Can not get lastModified for resource " + springResource.toString(), ex);
}
Expand All @@ -296,10 +320,12 @@ public Reader getReader(FileResource resource)
return new InputStreamReader(this.getInputStream(resource));
}

private long getSize(Resource springResource) {
private long getSize(Resource springResource) throws FileSystemResourceIOException {
try {
long length = springResource.contentLength();
return length;
} catch (FileSystemException e) {
throw new FileSystemResourceIOException(e);
} catch (IOException ex) {
LOGGER.warn("Can not get size for resource " + springResource.toString(), ex);
}
Expand Down

0 comments on commit 39682b6

Please sign in to comment.