Details
-
Task
-
Status: Open
-
Major
-
Resolution: Unresolved
-
0.8, 1.0, 1.1, 1.2
-
None
-
None
Description
The org.apache.sis.storage package contains a Resource interface which is the root of all resources loaded by a data store (FeatureSet, Aggregate, etc.). The resources do not tell us which data store created them. In some case we need this information, for example in order to fetch the parameters used for opening the data store.
We could add getOriginatingStore() method in Resource interface, but not all resources are produced by a data store (a resource could be computed by a model for example). We could use an Optional<DataStore> return type, but Optional is not convenient with objects making extensive use of checked exceptions, because lambda functions are difficult to use in that context.
A an alternative would be to define the getOriginatingStore() method in a StoreResource sub-interface, to be implemented only by resources produced by data stores. It would be the only method of that interface.
Use case
The only use case that has been given up to now is for restoring a resource between sessions with the following steps:
- Save the information provided by DataStore#getOpenParameters().
- Save the Resource.getIdentifier().
With those two information, a resource can be re-fetched later. Those steps implies that getOriginatingStore() should be valid only on resources opened directly on the data store. Otherwise (e.g. if the resource is the result of a subset), above steps do not provide sufficient information.
Another use case would be to check if a resource belong to a data store that we have just closed (or moved, or whatever). That use case would require a different contract: data stores returned even if the resource is the result of an operation, and data stores returned as Set<DataStore> instead of Optional<DataStore>.
So we have two use cases with contradictory requirements. Only the first use case is used in practice at the time of writing this issue.
Summary of alternatives
StoreResource sub-interface
Advantages
- Avoid polluting the Resource API.
Inconvenient
- It is very easy to forget to implement it.
- It is more difficult to implement by wrappers (need to prepare many sub-classes for different combination of implemented interfaces).
- Adding a type inflates the API in a more intrusive way than adding a method.
- Type safety provided by the use of an interface is not useful in practice for this case.
Resource.getOriginatingStore() returning Optional<DataStore>
Advantages
- Easier for wrappers to implement.
- Less risk to forget to implement this method (compiler reminds us).
Inconvenient
- Most Optional methods are unusable because of checked exceptions.
- This method make little sense in DataStore (which is itself a Resource).
Resource.getOpenParameters() returning Optional<ParameterValueGroup>
Advantages
- DataStore already has this method.
- Could be used for saving more kind of resources than just the ones produces directly by the data store.
Inconvenient
- Limit the use case to saving the parameters.
Resource.getSources() returning Set<Resource>
With callers having to check themselves if the resources are DataStore instances. For finding the root DataStore, we can iterate recursively in the sources until we find a DataStore with no source (i.e. getSources() return an empty collection).
Advantages
- Flexible, addresses more use cases than just the above-cited ones.
- Familiar pattern for developers.
Inconvenient
Still ambiguous, because a DataStore is also a Resource. For example the CSV data store implements FeatureSet interface. If we invoke FeatureSet.query(Query) method on it, we get a new FeatureSet instance which has the CSV DataStore as its sole source. It looks the same as a FeatureSet instance which would be a component of an Aggregate. We do not have a fully reliable way to tell us whether a given FeatureSet is the result of a subset or the component of an aggregate. The distinction between the two is relevant if we want to save the parameters needed for re-opening the FeatureSet later.