This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 08331faee8372ef19ce2e5787abe1390b8fdd01c Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Tue Sep 20 14:36:56 2022 +0200 Workaround a misleading error message from ImageIO. --- .../sis/internal/storage/image/FormatFinder.java | 34 ++++++++++++++++++-- .../org/apache/sis/storage/StorageConnector.java | 36 ++++++++++++++++++++-- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFinder.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFinder.java index d4f7f83837..72a63613bf 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFinder.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFinder.java @@ -26,6 +26,7 @@ import java.nio.file.Files; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.ImageWriter; +import javax.imageio.IIOException; import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageWriterSpi; import javax.imageio.stream.ImageInputStream; @@ -34,6 +35,7 @@ import javax.imageio.stream.FileImageOutputStream; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.DataStoreException; import org.apache.sis.internal.storage.io.IOUtilities; +import org.apache.sis.util.Workaround; /** @@ -42,7 +44,7 @@ import org.apache.sis.internal.storage.io.IOUtilities; * It also helps to choose which {@link WorldFileStore} subclass to instantiate. * * @author Martin Desruisseaux (Geomatys) - * @version 1.2 + * @version 1.3 * @since 1.2 * @module */ @@ -212,7 +214,7 @@ final class FormatFinder implements AutoCloseable { ImageInputStream stream = null; for (final Map.Entry<ImageReaderSpi,Boolean> entry : deferred.entrySet()) { if (entry.getValue()) { - if (stream == null) { + if (stream == null) try { if (isWritable) { // ImageOutputStream is both read and write. stream = ImageIO.createImageOutputStream(storage); @@ -221,6 +223,8 @@ final class FormatFinder implements AutoCloseable { stream = ImageIO.createImageInputStream(storage); if (stream == null) break; } + } catch (IIOException e) { + throw unwrap(e); } final ImageReaderSpi p = entry.getKey(); if (p.canDecodeInput(stream)) { @@ -259,9 +263,11 @@ final class FormatFinder implements AutoCloseable { final File file = connector.getStorageAs(File.class); if (file != null) { stream = new FileImageOutputStream(file); - } else { + } else try { stream = ImageIO.createImageOutputStream(storage); if (stream == null) break; + } catch (IIOException e) { + throw unwrap(e); } } final ImageWriterSpi p = entry.getKey(); @@ -277,6 +283,28 @@ final class FormatFinder implements AutoCloseable { return writer; } + /** + * Returns the cause of given exception if it exists, or the exception itself otherwise. + * This method is invoked in the {@code catch} block of a {@code try} block invoking + * {@link ImageIO#createImageInputStream(Object)} or + * {@link ImageIO#createImageOutputStream(Object)}. + * + * <h4>Rational</h4> + * As of Java 18, above-cited methods systematically catch all {@link IOException}s and wrap + * them in an {@link IIOException} with <cite>"Can't create cache file!"</cite> error message. + * This is conform to Image I/O specification but misleading if the stream provider throws an + * {@link IOException} for another reason. Even when the failure is really caused by a problem + * with cache file, we want to propagate the original exception to user because its message + * may tell that there is no space left on device or no write permission. + * + * @see org.apache.sis.storage.StorageConnector#unwrap(IIOException) + */ + @Workaround(library = "JDK", version = "18") + private static IOException unwrap(final IIOException e) { + final Throwable cause = e.getCause(); + return (cause instanceof IOException) ? (IOException) cause : e; + } + /** * Closes all unused resources. Keep open only the find of objects needed by the image reader or writer. * This method must be invoked after by {@link WorldFileStore} construction. diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java index 5560158acd..9ba807bbb7 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java @@ -38,12 +38,14 @@ import java.nio.channels.SeekableByteChannel; import java.nio.file.NoSuchFileException; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; +import javax.imageio.IIOException; import javax.imageio.ImageIO; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.sis.util.Debug; import org.apache.sis.util.Classes; +import org.apache.sis.util.Workaround; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.ObjectConverters; import org.apache.sis.util.resources.Errors; @@ -1037,7 +1039,11 @@ public class StorageConnector implements Serializable { views.put(DataInput.class, c); // Share the same Coupled instance. } else { reset(); - asDataInput = ImageIO.createImageInputStream(storage); + try { + asDataInput = ImageIO.createImageInputStream(storage); + } catch (IIOException e) { + throw unwrap(e); + } addView(DataInput.class, asDataInput, null, (byte) (CASCADE_ON_RESET | CASCADE_ON_CLOSE)); /* * Note: Java Image I/O wrappers for Input/OutputStream do NOT close the underlying streams. @@ -1361,7 +1367,11 @@ public class StorageConnector implements Serializable { views.put(DataOutput.class, c); // Share the same Coupled instance. } else { reset(); - asDataOutput = ImageIO.createImageOutputStream(storage); + try { + asDataOutput = ImageIO.createImageOutputStream(storage); + } catch (IIOException e) { + throw unwrap(e); + } addView(DataOutput.class, asDataOutput, null, (byte) (CASCADE_ON_RESET | CASCADE_ON_CLOSE)); /* * Note: Java Image I/O wrappers for Input/OutputStream do NOT close the underlying streams. @@ -1598,6 +1608,28 @@ public class StorageConnector implements Serializable { } } + /** + * Returns the cause of given exception if it exists, or the exception itself otherwise. + * This method is invoked in the {@code catch} block of a {@code try} block invoking + * {@link ImageIO#createImageInputStream(Object)} or + * {@link ImageIO#createImageOutputStream(Object)}. + * + * <h4>Rational</h4> + * As of Java 18, above-cited methods systematically catch all {@link IOException}s and wrap + * them in an {@link IIOException} with <cite>"Can't create cache file!"</cite> error message. + * This is conform to Image I/O specification but misleading if the stream provider throws an + * {@link IOException} for another reason. Even when the failure is really caused by a problem + * with cache file, we want to propagate the original exception to user because its message + * may tell that there is no space left on device or no write permission. + * + * @see org.apache.sis.internal.storage.image.FormatFinder#unwrap(IIOException) + */ + @Workaround(library = "JDK", version = "18") + private static IOException unwrap(final IIOException e) { + final Throwable cause = e.getCause(); + return (cause instanceof IOException) ? (IOException) cause : e; + } + /** * Returns a string representation of this {@code StorageConnector} for debugging purpose. * This string representation is for diagnostic and may change in any future version.