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 dab1256806e44d60f0a8c4c892f1e8b1a1cb1855 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sat Sep 14 17:19:59 2024 +0200 Better `probeContent` implementation, including MIME type. `GDALStore.close()` needs to close child components too. --- .../org/apache/sis/storage/gdal/GDALStore.java | 33 ++++++++++++++++++- .../apache/sis/storage/gdal/GDALStoreProvider.java | 14 ++++---- .../main/org/apache/sis/storage/gdal/Opener.java | 38 +++++++++++++++++++++- .../org/apache/sis/storage/gdal/GDALStoreTest.java | 24 ++++++++++++-- 4 files changed, 98 insertions(+), 11 deletions(-) diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java index 6182b45554..8122818c66 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java @@ -490,8 +490,36 @@ public class GDALStore extends DataStore implements Aggregate, ResourceOnFileSys } } try (Flush _ = new Flush()) { + closeRecursively(); + } + } + + /** + * Closes all child components (if any) recursively, then closes this data store. + */ + private void closeRecursively() { + RuntimeException error = null; + try { + listeners.close(); + } catch (RuntimeException e) { + error = e; // Should not happen even if a listener threw an exception, but we want to be safe. + } finally { try { - listeners.close(); + final List<? extends Resource> children; + synchronized (this) { + children = components; + components = null; + } + if (children != null) { + for (Resource child : children) { + if (child instanceof Subdataset) try { + ((GDALStore) child).closeRecursively(); + } catch (RuntimeException e) { + if (error == null) error = e; + else error.addSuppressed(e); + } + } + } } finally { synchronized (this) { handle = null; @@ -499,5 +527,8 @@ public class GDALStore extends DataStore implements Aggregate, ResourceOnFileSys } } } + if (error != null) { + throw error; + } } } diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java index 885c44efaa..d1f6c053cc 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java @@ -16,6 +16,7 @@ */ package org.apache.sis.storage.gdal; +import java.net.URI; import java.util.List; import java.util.Optional; import java.util.logging.Logger; @@ -279,18 +280,17 @@ public class GDALStoreProvider extends DataStoreProvider { * @return a {@linkplain ProbeResult#isSupported() supported} status with the MIME type * if the given storage seems to be readable by {@code GDALStore} instances. * @throws DataStoreException if an I/O error occurred. - * - * @todo Need to check whether GDAL library has been found. - * @todo Need better check. Does GDAL provides a probe API? */ @Override public ProbeResult probeContent(final StorageConnector connector) throws DataStoreException { - final Path path = connector.getStorageAs(Path.class); - if (path != null) { + final URI url = connector.getStorageAs(URI.class); + if (url != null) { final GDAL gdal = tryGDAL("probeContent").orElse(null); if (gdal != null) { - final String mimeType = null; // TODO - return new ProbeResult(true, mimeType, null); + try (Opener p = Opener.read(this, Opener.toURL(url, connector.getStorageAs(Path.class)))) { + String mimeType = p.getMetadataItem(gdal, "DMD_MIMETYPE"); + return new ProbeResult(true, mimeType, null); + } } } return ProbeResult.UNSUPPORTED_STORAGE; diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java index 297ab3c950..af3dc8e7cf 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java @@ -24,6 +24,7 @@ import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import java.lang.foreign.MemorySegment; import org.apache.sis.util.privy.Constants; +import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.DataStoreException; @@ -39,7 +40,7 @@ import org.apache.sis.storage.DataStoreException; * @author Quentin Bialota (Geomatys) * @author Martin Desruisseaux (Geomatys) */ -final class Opener implements Runnable { +final class Opener implements Runnable, AutoCloseable { /** * Schemes of URLs to open with <var>GDAL</var> Virtual File Systems backed by <abbr>CURL</abbr>. */ @@ -155,6 +156,41 @@ final class Opener implements Runnable { return array; } + /** + * Returns a metadata item from the driver. + * + * @param gdal set of handles for invoking <abbr>GDAL</abbr> functions. + * @param name the key for the metadata item to fetch. + * @return the metadata given by the driver, or {@code null} if unspecified. + * @throws DataStoreException if an error occurred while invoking a <abbr>GDAL</abbr> function. + */ + final String getMetadataItem(final GDAL gdal, final String name) throws DataStoreException { + try (var arena = Arena.ofConfined()) { + final MemorySegment driver = (MemorySegment) gdal.getDatasetDriver.invokeExact(handle); + if (!GDAL.isNull(driver)) { + MemorySegment n = arena.allocateFrom(name); + MemorySegment r = (MemorySegment) gdal.getMetadataItem.invokeExact(driver, n, MemorySegment.NULL); + return GDAL.toString(r); + } + } catch (Throwable e) { + throw GDAL.propagate(e); + } finally { + ErrorHandler.throwOnFailure(null, "probeContent"); + } + return null; + } + + /** + * Closes the <abbr>GDAL</abbr> data set. This method shall be invoked + * by {@link GDALStoreProvider#probeContent(StorageConnector)} only. + * {@link GDALStore} has its own close method. + */ + @Override + public void close() throws DataStoreException { + run(); + ErrorHandler.throwOnFailure(null, "probeContent"); + } + /** * Invoked by {@link java.lang.ref.Cleaner} when the native resource is ready to be closed. * This method shall not be invoked explicitly. The {@code Cleaner} <abbr>API</abbr> ensures diff --git a/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java b/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java index 7ea4afcf5d..9353bdf4a5 100644 --- a/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java +++ b/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java @@ -29,11 +29,13 @@ import org.apache.sis.coverage.grid.GridCoverage; import org.apache.sis.coverage.grid.GridExtent; import org.apache.sis.coverage.grid.GridGeometry; import org.apache.sis.coverage.grid.PixelInCell; +import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.GridCoverageResource; import org.apache.sis.storage.Resource; import org.apache.sis.storage.DataStores; import org.apache.sis.storage.DataStoreProvider; import org.apache.sis.storage.StorageConnector; +import org.apache.sis.storage.ProbeResult; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -65,6 +67,25 @@ public final class GDALStoreTest { fail("Provider not found."); } + /** + * Returns the storage connector to the test file to use as input. + */ + private static StorageConnector input() { + return new StorageConnector(GDALStoreTest.class.getResource("test.tiff")); + } + + /** + * Tests providing the MIME type of an image. + * + * @throws DataStoreException if any error occurred. + */ + @Test + public void testProbeContent() throws DataStoreException { + final var provider = new GDALStoreProvider(); + ProbeResult result = provider.probeContent(input()); + assertEquals("image/tiff", result.getMimeType()); + } + /** * Tests reading an indexed image. The test uses a small image with 1 band and indexed color palette. * @@ -74,8 +95,7 @@ public final class GDALStoreTest { public void readIndexedImage() throws Exception { boolean foundGrid = false; boolean foundBand = false; - final var input = new StorageConnector(GDALStoreTest.class.getResource("test.tiff")); - try (GDALStore store = new GDALStore(new GDALStoreProvider(), input)) { + try (GDALStore store = new GDALStore(new GDALStoreProvider(), input())) { for (final Resource r : store.components()) { assertFalse(foundGrid); foundGrid = true;