[ 
https://issues.apache.org/jira/browse/SIS-416?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Martin Desruisseaux updated SIS-416:
------------------------------------
    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.

h1. 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.

h1. Summary of alternatives
h2. {{StoreResource}} sub-interface
h3. Advantages
* Avoid polluting the {{Resource}} API.

h3. 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.

h2. {{Resource.getOriginatingStore()}} returning {{Optional<DataStore>}}
h3. Advantages
* Easier for wrappers to implement.
* Less risk to forget to implement this method (compiler reminds us).

h3. Inconvenient
* Most {{Optional}} methods are unusable because of checked exceptions.
* This method make little sense in {{DataStore}} (which is itself a 
{{Resource}}).

h2. {{Resource.getOpenParameters()}} returning {{Optional<ParameterValueGroup>}}
h3. 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.

h3. Inconvenient
* Limit the use case to saving the parameters.

h2. {{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).
h3. Advantages
* Flexible, addresses more use cases than just the above-cited ones.
* Familiar pattern for developers.

h3. 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.


  was:
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.

h1. 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.

h1. Summary of alternatives
h2. {{StoreResource}} sub-interface
h3. Advantages
* Avoid polluting the {{Resource}} API.

h3. 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.

h2. {{Resource.getOriginatingStore()}} returning {{Optional<DataStore>}}
h3. Advantages
* Easier for wrappers to implement.
* Less risk to forget to implement this method (compiler reminds us).

h3. Inconvenient
* Most {{Optional}} methods are unusable because of checked exceptions.
* This method make little sense in {{DataStore}} (which is itself a 
{{Resource}}).

h2. {{Resource.getOpenParameters()}} returning {{Optional<ParameterValueGroup>}}
h3. 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.

h3. Inconvenient
* Limit the use case to saving the parameters.

h2. {{Resource.getSources()}} returning {{Set<Resource>}}
With callers having to check themselves if the resources are {{DataStore}} 
instances.
h3. Advantages
* Flexible, addresses all use cases listed above (using recursivity if needed).



> Consider adding StoreResource interface
> ---------------------------------------
>
>                 Key: SIS-416
>                 URL: https://issues.apache.org/jira/browse/SIS-416
>             Project: Spatial Information Systems
>          Issue Type: Task
>          Components: Storage
>    Affects Versions: 0.8, 1.0, 1.1, 1.2
>            Reporter: Martin Desruisseaux
>            Assignee: Martin Desruisseaux
>            Priority: Major
>
> 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.
> h1. 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.
> h1. Summary of alternatives
> h2. {{StoreResource}} sub-interface
> h3. Advantages
> * Avoid polluting the {{Resource}} API.
> h3. 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.
> h2. {{Resource.getOriginatingStore()}} returning {{Optional<DataStore>}}
> h3. Advantages
> * Easier for wrappers to implement.
> * Less risk to forget to implement this method (compiler reminds us).
> h3. Inconvenient
> * Most {{Optional}} methods are unusable because of checked exceptions.
> * This method make little sense in {{DataStore}} (which is itself a 
> {{Resource}}).
> h2. {{Resource.getOpenParameters()}} returning 
> {{Optional<ParameterValueGroup>}}
> h3. 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.
> h3. Inconvenient
> * Limit the use case to saving the parameters.
> h2. {{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).
> h3. Advantages
> * Flexible, addresses more use cases than just the above-cited ones.
> * Familiar pattern for developers.
> h3. 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.



--
This message was sent by Atlassian Jira
(v8.20.7#820007)

Reply via email to