You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2021/09/03 16:52:30 UTC
[sis] 03/05: Landsat data store use GeoTIFF data store for
providing components.
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 d585a5b3fe11c09999c40926a447001687965a9c
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Sep 3 18:37:41 2021 +0200
Landsat data store use GeoTIFF data store for providing components.
---
storage/sis-earth-observation/pom.xml | 5 +
.../apache/sis/storage/earthobservation/Band.java | 7 +-
.../sis/storage/earthobservation/BandData.java | 131 ++++++++++++
.../storage/earthobservation/LandsatReader.java | 3 +-
.../sis/storage/earthobservation/LandsatStore.java | 82 +++++++-
.../org/apache/sis/storage/geotiff/DataCube.java | 11 +-
.../sis/storage/geotiff/ImageFileDirectory.java | 9 +-
.../sis/internal/storage/GridResourceWrapper.java | 220 +++++++++++++++++++++
8 files changed, 460 insertions(+), 8 deletions(-)
diff --git a/storage/sis-earth-observation/pom.xml b/storage/sis-earth-observation/pom.xml
index 0b8a1cf..f224bf9 100644
--- a/storage/sis-earth-observation/pom.xml
+++ b/storage/sis-earth-observation/pom.xml
@@ -125,6 +125,11 @@
<artifactId>sis-storage</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.sis.storage</groupId>
+ <artifactId>sis-geotiff</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/Band.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/Band.java
index 44e65a7..4210320 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/Band.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/Band.java
@@ -16,6 +16,9 @@
*/
package org.apache.sis.storage.earthobservation;
+import org.opengis.util.InternationalString;
+import org.apache.sis.util.iso.SimpleInternationalString;
+
/**
* Names of Landsat bands.
@@ -50,7 +53,7 @@ enum Band {
/**
* Name of Landsat band.
*/
- final String name;
+ final InternationalString name;
/**
* Peak response wavelength for the Landsat band, in nanometres.
@@ -62,7 +65,7 @@ enum Band {
*/
private Band(final BandGroup group, final String name, final short wavelength) {
this.group = group;
- this.name = name;
+ this.name = new SimpleInternationalString(name);
this.wavelength = wavelength;
}
}
diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/BandData.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/BandData.java
new file mode 100644
index 0000000..804f1ab
--- /dev/null
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/BandData.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sis.storage.earthobservation;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+import org.opengis.util.LocalName;
+import org.opengis.util.GenericName;
+import org.opengis.metadata.Metadata;
+import org.opengis.metadata.identification.Identification;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.geotiff.GeoTiffStore;
+import org.apache.sis.internal.storage.GridResourceWrapper;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.metadata.iso.DefaultMetadata;
+
+
+/**
+ * A band in a Landsat data set. Each band is represented by a separated GeoTIFF file.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+final class BandData extends GridResourceWrapper {
+ /**
+ * The data store that contains this band.
+ * Also the object on which to perform synchronization locks.
+ */
+ private final LandsatStore parent;
+
+ /**
+ * The band for which this instance provides data.
+ */
+ private final Band band;
+
+ /**
+ * Identifier of the band for which this instance provides data.
+ *
+ * @see #getIdentifier()
+ */
+ private final LocalName identifier;
+
+ /**
+ * Filename of the file to read for band data.
+ * This is relative to {@link LandsatStore#directory}.
+ */
+ private final String filename;
+
+ /**
+ * The metadata, created when first needed.
+ */
+ private Metadata metadata;
+
+ /**
+ * Creates a new resource for the band identified by the given identifier.
+ */
+ BandData(final LandsatStore parent, final Band band, final LocalName identifier, final String filename) {
+ this.parent = parent;
+ this.band = band;
+ this.identifier = identifier;
+ this.filename = filename;
+ }
+
+ /**
+ * Creates the GeoTIFF reader and get the first image from it.
+ */
+ @Override
+ protected GridCoverageResource createSource() throws DataStoreException {
+ final Path file;
+ if (parent.directory != null) {
+ file = parent.directory.resolve(filename);
+ } else {
+ file = Paths.get(filename);
+ }
+ return new GeoTiffStore(parent, parent.getProvider(), new StorageConnector(file), true).components().get(0);
+ }
+
+ /**
+ * Returns the resource persistent identifier. The name is the {@link Band#name()}
+ * and the scope (namespace) is the name of the directory that contains this band.
+ */
+ @Override
+ public Optional<GenericName> getIdentifier() throws DataStoreException {
+ return Optional.of(identifier);
+ }
+
+ /**
+ * Returns information about this resource.
+ */
+ @Override
+ public synchronized Metadata getMetadata() throws DataStoreException {
+ if (metadata == null) {
+ /*
+ * The GeoTIFF store implementation is known to create instances of `DefaultMetadata`,
+ * and this instance is kept modifiable because the `hidden` argument specified to the
+ * `GeoTiffStore` constructor was `true`. This is an undocumented feature that may be
+ * modified in future SIS versions.
+ */
+ final DefaultMetadata md = (DefaultMetadata) source().getMetadata();
+ for (final Identification id : md.getIdentificationInfo()) {
+ final DefaultCitation c = (DefaultCitation) id.getCitation();
+ if (c != null) {
+ c.setTitle(band.name);
+ break;
+ }
+ }
+ md.transitionTo(DefaultMetadata.State.FINAL);
+ metadata = md;
+ }
+ return metadata;
+ }
+}
diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatReader.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatReader.java
index d914cb0..7691f70 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatReader.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatReader.java
@@ -60,7 +60,6 @@ import org.apache.sis.util.Characters;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Vocabulary;
-import org.apache.sis.util.iso.SimpleInternationalString;
import org.apache.sis.internal.referencing.GeodeticObjectBuilder;
import org.apache.sis.internal.referencing.ReferencingFactoryContainer;
import org.apache.sis.internal.referencing.provider.PolarStereographicB;
@@ -804,7 +803,7 @@ final class LandsatReader extends MetadataBuilder {
DefaultBand band = bands.get(bk);
if (band == null) {
band = new DefaultBand();
- band.setDescription(new SimpleInternationalString(bk.name));
+ band.setDescription(bk.name);
band.setPeakResponse((double) bk.wavelength);
band.setBoundUnits(Units.NANOMETRE);
bands.put(bk, band);
diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
index 992afed..f550865 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
@@ -16,6 +16,8 @@
*/
package org.apache.sis.storage.earthobservation;
+import java.util.Map;
+import java.util.List;
import java.util.Optional;
import java.io.Reader;
import java.io.BufferedReader;
@@ -26,10 +28,15 @@ import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
-import org.opengis.metadata.Metadata;
+import org.opengis.util.NameSpace;
+import org.opengis.util.LocalName;
import org.opengis.util.GenericName;
+import org.opengis.util.NameFactory;
import org.opengis.util.FactoryException;
+import org.opengis.metadata.Metadata;
import org.opengis.parameter.ParameterValueGroup;
+import org.apache.sis.storage.Aggregate;
+import org.apache.sis.storage.GridCoverageResource;
import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.DataStoreClosedException;
@@ -40,6 +47,8 @@ import org.apache.sis.storage.event.StoreEvent;
import org.apache.sis.storage.event.StoreListener;
import org.apache.sis.storage.event.WarningEvent;
import org.apache.sis.internal.storage.URIDataStore;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
import org.apache.sis.setup.OptionKey;
@@ -76,7 +85,7 @@ import org.apache.sis.setup.OptionKey;
* @since 0.8
* @module
*/
-public class LandsatStore extends DataStore {
+public class LandsatStore extends DataStore implements Aggregate {
/**
* The reader, or {@code null} if closed.
*/
@@ -103,6 +112,12 @@ public class LandsatStore extends DataStore {
private GenericName identifier;
/**
+ * The array of images for each Landsat band, or {@code null} if not yet created.
+ * This array is created together with {@linkplain #metadata} and is unmodifiable.
+ */
+ private BandData[] components;
+
+ /**
* Creates a new Landsat store from the given file, URL, stream or character reader.
* This constructor invokes {@link StorageConnector#closeAllExcept(Object)},
* keeping open only the needed resource.
@@ -142,6 +157,16 @@ public class LandsatStore extends DataStore {
}
/**
+ * Returns the name of the directory that contains this data set.
+ * The directory may not exist, for example if the data are read from a ZIP file.
+ * The returned name can be used in user interfaces or in error messages.
+ */
+ @Override
+ public final String getDisplayName() {
+ return (directory != null) ? directory.getFileName().toString() : super.getDisplayName();
+ }
+
+ /**
* Returns the parameters used to open this Landsat data store.
* The parameters are described by {@link LandsatStoreProvider#getOpenParameters()} and contains at least
* a parameter named {@value org.apache.sis.storage.DataStoreProvider#LOCATION} with a {@link URI} value.
@@ -177,6 +202,7 @@ public class LandsatStore extends DataStore {
/**
* Parses the main Landsat text file.
+ * Also creates the array of components, but without loading GeoTIFF data yet.
*/
private void loadMetadata() throws DataStoreException {
if (source == null) {
@@ -187,6 +213,20 @@ public class LandsatStore extends DataStore {
final LandsatReader parser = new LandsatReader(getDisplayName(), listeners);
parser.read(reader);
metadata = parser.getMetadata();
+ /*
+ * Create the array of components. The resource identifier is the band name.
+ * The namespace of each identifier is the name of the data set directory.
+ */
+ final String name = getDisplayName();
+ final NameFactory factory = DefaultFactories.forBuildin(NameFactory.class);
+ final NameSpace scope = (name != null) ? factory.createNameSpace(factory.createLocalName(null, name), null) : null;
+ int i = 0;
+ components = new BandData[parser.resources.size()];
+ for (final Map.Entry<Band,String> entry : parser.resources.entrySet()) {
+ final Band band = entry.getKey();
+ final LocalName id = factory.createLocalName(scope, band.name());
+ components[i++] = new BandData(this, band, id, entry.getValue());
+ }
} catch (IOException e) {
throw new DataStoreException(e);
} catch (FactoryException e) {
@@ -211,6 +251,23 @@ public class LandsatStore extends DataStore {
}
/**
+ * Returns the resources for each Landsat band.
+ *
+ * @return all bands that are components of this aggregate. Never {@code null}.
+ * @throws DataStoreException if an error occurred while fetching the components.
+ *
+ * @since 1.1
+ */
+ @Override
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public synchronized List<GridCoverageResource> components() throws DataStoreException {
+ if (components == null) {
+ loadMetadata();
+ }
+ return UnmodifiableArrayList.wrap(components);
+ }
+
+ /**
* Registers a listener to notify when the specified kind of event occurs in this data store.
* The current implementation of this data store can emit only {@link WarningEvent}s;
* any listener specified for another kind of events will be ignored.
@@ -231,5 +288,26 @@ public class LandsatStore extends DataStore {
@Override
public synchronized void close() throws DataStoreException {
metadata = null;
+ final BandData[] bands = components;
+ if (bands != null) {
+ components = null;
+ DataStoreException error = null;
+ for (int i=0; i<bands.length; i++) {
+ final BandData band = bands[i];
+ if (band != null) {
+ bands[i] = null;
+ try {
+ band.closeDataStore();
+ } catch (DataStoreException e) {
+ if (error == null) {
+ error = e;
+ } else {
+ error.addSuppressed(e);
+ }
+ }
+ }
+ }
+ if (error != null) throw error;
+ }
}
}
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
index bcbaa7a..ee3eb9a 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
@@ -30,6 +30,7 @@ import org.apache.sis.internal.geotiff.Predictor;
import org.apache.sis.internal.geotiff.Compression;
import org.apache.sis.internal.storage.TiledGridResource;
import org.apache.sis.internal.storage.ResourceOnFileSystem;
+import org.apache.sis.internal.storage.StoreResource;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.math.Vector;
@@ -45,7 +46,7 @@ import org.apache.sis.math.Vector;
* @since 1.1
* @module
*/
-abstract class DataCube extends TiledGridResource implements ResourceOnFileSystem {
+abstract class DataCube extends TiledGridResource implements ResourceOnFileSystem, StoreResource {
/**
* The GeoTIFF reader which contain this {@code DataCube}.
* Used for fetching information like the input channel and where to report warnings.
@@ -63,6 +64,14 @@ abstract class DataCube extends TiledGridResource implements ResourceOnFileSyste
}
/**
+ * Returns the data store that produced this resource.
+ */
+ @Override
+ public final DataStore getOriginator() {
+ return reader.store;
+ }
+
+ /**
* Returns the object on which to perform all synchronizations for thread-safety.
*/
@Override
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
index c73899c..7ce5bc1 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
@@ -1375,7 +1375,14 @@ final class ImageFileDirectory extends DataCube {
referencing.completeMetadata(metadata); // Must be after `getGridGeometry()`.
}
metadata.addTitleOrIdentifier(identifier.toString(), MetadataBuilder.Scope.RESOURCE);
- final Metadata md = metadata.build(true);
+ /*
+ * Undocumented feature: if `GeoTiffStore` is not used as a standalone reader but is instead
+ * used as a component of a larger data store (e.g. Landsat), do not freeze the metadata.
+ * This is needed for allowing `LandsatStore` to modify those metadata before to return them
+ * to the user. We may revert this undocumented feature in the future if we provide a better
+ * way to perform metadata amendment.
+ */
+ final Metadata md = metadata.build(!reader.store.hidden);
metadata = null;
return md;
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GridResourceWrapper.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GridResourceWrapper.java
new file mode 100644
index 0000000..fc42043
--- /dev/null
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GridResourceWrapper.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sis.internal.storage;
+
+import java.util.List;
+import java.util.Optional;
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.Query;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.opengis.geometry.Envelope;
+import org.opengis.metadata.Metadata;
+import org.opengis.util.GenericName;
+
+
+/**
+ * A grid coverage resource which is a wrapper around another grid coverage resource.
+ * Wrappers can be used for delaying data loading, modifying the identifier, completing metadata, <i>etc</i>.
+ * The wrapped resource is created only when first needed.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public abstract class GridResourceWrapper implements GridCoverageResource {
+ /**
+ * The coverage resource instance which provides the data.
+ * This is initially {@code null} and created when first needed.
+ */
+ private GridCoverageResource source;
+
+ /**
+ * Creates a new wrapper.
+ */
+ protected GridResourceWrapper() {
+ }
+
+ /**
+ * Creates the resource on which to delegate operations.
+ * This method is invoked in a synchronized block when first needed and the result is cached.
+ *
+ * @return the resource on which to delegate operations.
+ * @throws DataStoreException if the resource can not be created.
+ */
+ protected abstract GridCoverageResource createSource() throws DataStoreException;
+
+ /**
+ * Returns the potentially cached source.
+ * This method invokes {@link #createSource()} when first needed and caches the result.
+ *
+ * @return the resource on which to delegate operations.
+ * @throws DataStoreException if the resource can not be created.
+ */
+ protected final synchronized GridCoverageResource source() throws DataStoreException {
+ if (source == null) {
+ source = createSource();
+ }
+ return source;
+ }
+
+ /**
+ * Returns the resource persistent identifier.
+ * The default implementation delegates to the source.
+ *
+ * @return a persistent identifier unique within the data store, or absent if this resource has no such identifier.
+ * @throws DataStoreException if an error occurred while fetching the identifier.
+ */
+ @Override
+ public Optional<GenericName> getIdentifier() throws DataStoreException {
+ return source().getIdentifier();
+ }
+
+ /**
+ * Returns information about this resource.
+ * The default implementation delegates to the source.
+ *
+ * @return information about this resource. Should not be {@code null}.
+ * @throws DataStoreException if an error occurred while reading the metadata.
+ */
+ @Override
+ public Metadata getMetadata() throws DataStoreException {
+ return source().getMetadata();
+ }
+
+ /**
+ * Returns the spatiotemporal extent of this resource in its most natural coordinate reference system.
+ * This is not necessarily the smallest bounding box encompassing all data.
+ * The default implementation delegates to the source.
+ *
+ * @return the spatiotemporal resource extent. May be absent if none or too costly to compute.
+ * @throws DataStoreException if an error occurred while reading or computing the envelope.
+ */
+ @Override
+ public Optional<Envelope> getEnvelope() throws DataStoreException {
+ return source().getEnvelope();
+ }
+
+ /**
+ * Returns the valid extent of grid coordinates together with the conversion from those grid
+ * coordinates to real world coordinates. The default implementation delegates to the source.
+ *
+ * @return extent of grid coordinates together with their mapping to "real world" coordinates.
+ * @throws DataStoreException if an error occurred while reading definitions from the underlying data store.
+ */
+ @Override
+ public GridGeometry getGridGeometry() throws DataStoreException {
+ return source().getGridGeometry();
+ }
+
+ /**
+ * Returns the ranges of sample values together with the conversion from samples to real values.
+ * The default implementation delegates to the source.
+ *
+ * @return ranges of sample values together with their mapping to "real values".
+ * @throws DataStoreException if an error occurred while reading definitions from the underlying data store.
+ */
+ @Override
+ public List<SampleDimension> getSampleDimensions() throws DataStoreException {
+ return source().getSampleDimensions();
+ }
+
+ /**
+ * Loads a subset of the grid coverage represented by this resource.
+ * The default implementation delegates to the source.
+ *
+ * @param domain desired grid extent and resolution, or {@code null} for reading the whole domain.
+ * @param range 0-based indices of sample dimensions to read, or {@code null} or an empty sequence for reading them all.
+ * @return the grid coverage for the specified domain and range.
+ * @throws DataStoreException if an error occurred while reading the grid coverage data.
+ */
+ @Override
+ public GridCoverage read(GridGeometry domain, int... range) throws DataStoreException {
+ return source().read(domain, range);
+ }
+
+ /**
+ * Requests a subset of the coverage.
+ * The default implementation delegates to the source.
+ *
+ * @param query definition of domain (grid extent) and range (sample dimensions) filtering applied at reading time.
+ * @return resulting coverage resource (never {@code null}).
+ * @throws DataStoreException if an error occurred while processing the query.
+ */
+ @Override
+ public GridCoverageResource subset(final Query query) throws DataStoreException {
+ return source().subset(query);
+ }
+
+ /**
+ * Registers a listener to notify when the specified kind of event occurs in this resource or in children.
+ * The default implementation delegates to the source.
+ *
+ * @param <T> compile-time value of the {@code eventType} argument.
+ * @param listener listener to notify about events.
+ * @param eventType type of {@link StoreEvent} to listen (can not be {@code null}).
+ */
+ @Override
+ public <T extends StoreEvent> void addListener(Class<T> eventType, StoreListener<? super T> listener) {
+ final GridCoverageResource source;
+ try {
+ source = source();
+ } catch (DataStoreException e) {
+ throw new BackingStoreException(e);
+ }
+ source.addListener(eventType, listener);
+ }
+
+ /**
+ * Unregisters a listener previously added to this resource for the given type of events.
+ * The default implementation delegates to the source.
+ *
+ * @param <T> compile-time value of the {@code eventType} argument.
+ * @param listener listener to stop notifying about events.
+ * @param eventType type of {@link StoreEvent} which were listened (can not be {@code null}).
+ */
+ @Override
+ public <T extends StoreEvent> void removeListener(Class<T> eventType, StoreListener<? super T> listener) {
+ final GridCoverageResource source;
+ synchronized (this) {
+ source = this.source; // No need to invoke the `source()` method here.
+ }
+ if (source != null) {
+ source.removeListener(eventType, listener);
+ }
+ }
+
+ /**
+ * Closes the data store associated to the resource, then discards the resource.
+ * This method does not verify if the data store is still used by other resources.
+ *
+ * @throws DataStoreException if an error occurred while closing the data store.
+ */
+ public final void closeDataStore() throws DataStoreException {
+ final GridCoverageResource s = source;
+ source = null;
+ if (s instanceof StoreResource) {
+ ((StoreResource) s).getOriginator().close();
+ }
+ }
+}