Class StorageConnector

java.lang.Object
org.apache.sis.storage.StorageConnector
All Implemented Interfaces:
Serializable

public class StorageConnector extends Object implements Serializable
Information for creating a connection to a DataStore in read and/or write mode. StorageConnector wraps an input Object, which can be any of the following types: The getStorageAs(Class) method provides the storage as an object of the given type, opening the input stream if necessary. This class tries to open the stream only once - subsequent invocation of getStorageAs(…) may return the same input stream.

This class is used only for discovery of a DataStore implementation capable to handle the input. Once a suitable DataStore has been found, the StorageConnector instance is typically discarded since each data store implementation will use their own input/output objects.

Limitations

This class is not thread-safe. Not only StorageConnector should be used by a single thread, but the objects returned by getStorageAs(Class) should also be used by the same thread.

Instances of this class are serializable if the storage object given at construction time is serializable.

Since:
0.3
Version:
1.3
See Also:
  • Field Details

    • serialVersionUID

      private static final long serialVersionUID
      For cross-version compatibility.
      See Also:
    • DEFAULT_BUFFER_SIZE

      static final int DEFAULT_BUFFER_SIZE
      The default size of the ByteBuffer to be created. Users can override this value by providing a value for OptionKey.BYTE_BUFFER.

      This buffer capacity is also used as read-ahead limit for mark operations. The rational is to allow as many bytes as contained in buffers of default size. For increasing the chances to meet that goal, this size should be the same than BufferedInputStream default buffer size.

      See Also:
    • MINIMAL_BUFFER_SIZE

      static final int MINIMAL_BUFFER_SIZE
      The minimal size of the ByteBuffer to be created. This size is used only for temporary buffers that are unlikely to be used for the actual reading process.
      See Also:
    • CASCADE_ON_CLOSE

      private static final byte CASCADE_ON_CLOSE
      A flag for addView(…, view, source, flags) telling that after closing the view, we also need to close the source. This flag should be set when the view is an ImageInputStream because Java I/O FileCacheImageInputStream.close() does not close the underlying stream. For most other kinds of view, this flag should not be set.
      See Also:
    • CASCADE_ON_RESET

      private static final byte CASCADE_ON_RESET
      A flag for addView(…, view, source, flags) telling that before resetting the view, we need to reset the source first. This flag should can be unset if any change in the position of view is immediately reflected in the position of source, and vice-versa.
      See Also:
    • CLEAR_ON_RESET

      private static final byte CLEAR_ON_RESET
      A flag for addView(…, view, source, flags) telling that view cannot be reset, so it should be set to null instead. This implies that a new view of the same type will be recreated next time it will be requested.

      When this flag is set, the CASCADE_ON_RESET should usually be set at the same time.

      See Also:
    • OPENERS

      private static final Map<Class<?>,StorageConnector.Opener<?>> OPENERS
      List of types recognized by getStorageAs(Class), associated to the methods for opening stream of those types. This map shall contain every types documented in getStorageAs(Class) javadoc. null values means to use ObjectConverters for that particular type.
    • storage

      final Object storage
      The input/output object given at construction time.
      See Also:
    • name

      private transient String name
      A name for the input/output object, or null if none. This field is initialized only when first needed.
    • extension

      private transient String extension
      The filename extension, or null if none. This field is initialized only when first needed.
    • options

      private Map<OptionKey<?>,Object> options
      The options, created only when first needed.
      See Also:
    • views

      private transient Map<Class<?>,StorageConnector.Coupled> views
      Views of storage as instances of types different than the type of the object given to the constructor. The null reference can appear in various places:
      • A non-existent entry (equivalent to an entry associated to the null value) means that the value has not yet been computed.
      • A valid entry with StorageConnector.Coupled.view set to null means the value has been computed and we have determined that getStorageAs(Class) shall return null for that type.
      • By convention, the null key is associated to the storage value.
      An empty map means that this StorageConnector has been closed.
      See Also:
  • Constructor Details

    • StorageConnector

      public StorageConnector(Object storage)
      Creates a new data store connection wrapping the given input/output object. The object can be of any type, but the class javadoc lists the most typical ones.
      Parameters:
      storage - the input/output object as a URL, file, image input stream, etc..
  • Method Details

    • add

      private static <S> void add(Class<S> type, StorageConnector.Opener<S> op)
      Helper method for OPENERS static initialization.
    • getOption

      public <T> T getOption(OptionKey<T> key)
      Returns the option value for the given key, or null if none.
      Type Parameters:
      T - the type of option value.
      Parameters:
      key - the option for which to get the value.
      Returns:
      the current value for the given option, or null if none.
    • setOption

      public <T> void setOption(OptionKey<T> key, T value)
      Sets the option value for the given key. The default implementation recognizes the following options:
      Type Parameters:
      T - the type of option value.
      Parameters:
      key - the option for which to set the value.
      value - the new value for the given option, or null for removing the value.
    • getStorage

      public Object getStorage() throws DataStoreException
      Returns the input/output object given at construction time. The object can be of any type, but the class javadoc lists the most typical ones.
      Returns:
      the input/output object as a URL, file, image input stream, etc..
      Throws:
      DataStoreException - if the storage object has already been used and cannot be reused.
      See Also:
    • getStorageName

      public String getStorageName()
      Returns a short name of the input/output object. For example if the storage is a file, this method returns the filename without the path (but including the file extension). The default implementation performs the following choices based on the type of the storage object:
      • For Path, File, URI or URL instances, this method uses dedicated API like Path.getFileName().
      • For CharSequence instances, this method gets a string representation of the storage object and returns the part after the last '/' character or platform-dependent name separator.
      • For instances of unknown type, this method builds a string representation using the class name. Note that the string representation of unknown types may change in any future SIS version.
      Returns:
      a short name of the storage object.
    • getFileExtension

      public String getFileExtension()
      Returns the filename extension of the input/output object. The default implementation performs the following choices based on the type of the storage object:
      • For Path, File, URI, URL or CharSequence instances, this method returns the string after the last '.' character in the filename, provided that the '.' is not the first filename character. This may be an empty string if the filename has no extension, but never null.
      • For instances of unknown type, this method returns null.
      Returns:
      the filename extension, or an empty string if none, or null if the storage is an object of unknown type.
    • isSupportedType

      static boolean isSupportedType(Class<?> type)
      Returns true if the given type is one of the types supported by StorageConnector. The list of supported types is hard-coded and may change in any future version.
    • getStorageAs

      public <S> S getStorageAs(Class<S> type) throws IllegalArgumentException, DataStoreException
      Returns the storage as a view of the given type if possible, or null otherwise. The default implementation accepts the following types:
      • String:
        • If the storage object is an instance of the Path, File, URL, URI or CharSequence types, returns the string representation of their path.
        • Otherwise this method returns null.
      • Path, URI, URL, File:
        • If the storage object is an instance of the Path, File, URL, URI or CharSequence types and that type can be converted to the requested type, returned the conversion result.
        • Otherwise this method returns null.
      • ByteBuffer:
        • If the storage object can be obtained as described in bullet 2 of the DataInput section below, then this method returns the associated byte buffer.
        • Otherwise this method returns null.
      • DataInput:
      • ImageInputStream:
        • If the above DataInput can be created and casted to ImageInputStream, returns it.
        • Otherwise this method returns null.
      • InputStream:
        • If the storage object is already an instance of InputStream, then it is returned unchanged.
        • Otherwise if the above ImageInputStream can be created, returns a wrapper around that stream.
        • Otherwise this method returns null.
      • Reader:
        • If the storage object is already an instance of Reader, then it is returned unchanged.
        • Otherwise if the above InputStream can be created, returns an InputStreamReader using the encoding specified by OptionKey.ENCODING if any, or using the system default encoding otherwise.
        • Otherwise this method returns null.
      • Connection:
        • If the storage object is already an instance of Connection, then it is returned unchanged.
        • Otherwise if the storage is an instance of DataSource, then a connection is obtained when first needed and returned.
        • Otherwise this method returns null.
      • Any other types:
        • If the storage given at construction time is already an instance of the requested type, returns it as-is.
        • Otherwise this method throws IllegalArgumentException.

      Usage for probing operations

      Multiple invocations of this method on the same StorageConnector instance will try to return the same instance on a best effort basis. Consequently, implementations of DataStoreProvider.probeContent(StorageConnector) methods shall not close the stream or database connection returned by this method. In addition, those probeContent(StorageConnector) methods are responsible for restoring the stream or byte buffer to its original position on return. For an easier and safer way to ensure that the storage position is not modified, see DataStoreProvider.probeContent(StorageConnector, Class, Prober).
      Type Parameters:
      S - the compile-time type of the type argument (the source or storage type).
      Parameters:
      type - the desired type as one of ByteBuffer, DataInput, Connection class or other types supported by StorageConnector subclasses.
      Returns:
      the storage as a view of the given type, or null if the given type is one of the supported types listed in javadoc but no view can be created for the source given at construction time.
      Throws:
      IllegalArgumentException - if the given type argument is not one of the supported types listed in this javadoc or in subclass javadoc.
      IllegalStateException - if this StorageConnector has been closed.
      DataStoreException - if an error occurred while opening a stream or database connection.
      See Also:
    • reset

      private boolean reset(StorageConnector.Coupled c) throws DataStoreException
      Resets the given view. If the view is an instance of InputStream, ReadableByteChannel or other objects that may be affected by views operations, this method will reset the storage position. The view must have been previously marked by InputStream.mark(int) or equivalent method.

      This method is not a substitute for the requirement that users leave the getStorageAs(Class) return value in the same state as they found it. This method is only for handling the cases where using a view has an indirect impact on another view.

      Rational: DataStoreProvider.probeContent(StorageConnector) contract requires that implementers reset the input stream themselves. However if ChannelDataInput or InputStreamReader has been used, then the user performed a call to ChannelData.reset() (for instance), which did not reset the underlying input stream. So we need to perform the missing InputStream.reset() here, then synchronize the ChannelDataInput position accordingly.
      Parameters:
      c - container of the view to reset, or null if none.
      Returns:
      true if the given view, after reset, is valid. Note that StorageConnector.Coupled.view may be null and valid.
      Throws:
      DataStoreException
    • reset

      private void reset() throws DataStoreException
      Resets the root storage object.
      Throws:
      DataStoreException - if the storage cannot be reset.
    • createChannelDataInput

      private ChannelDataInput createChannelDataInput(boolean asImageInputStream) throws IOException, DataStoreException
      Creates a view for the input as a ChannelDataInput if possible. This is also a starting point for createDataInput() and createByteBuffer(). This method is one of the OPENERS methods and should be invoked at most once per StorageConnector instance.
      Parameters:
      asImageInputStream - whether the ChannelDataInput needs to be ChannelImageInputStream subclass.
      Throws:
      IOException - if an error occurred while opening a channel for the input.
      DataStoreException
      See Also:
    • createDataInput

      private DataInput createDataInput() throws IOException, DataStoreException
      Creates a view for the input as a DataInput if possible. This method performs the choice documented in the getStorageAs(Class) method for the DataInput case. Opening the data input may imply creating a ByteBuffer, in which case the buffer will be stored under the ByteBuffer.class key together with the DataInput.class case.

      This method is one of the OPENERS methods and should be invoked at most once per StorageConnector instance.

      Throws:
      IOException - if an error occurred while opening a stream for the input.
      DataStoreException
      See Also:
    • getChannelBuffer

      private ByteBuffer getChannelBuffer(ChannelFactory factory)
      Returns or allocate a buffer for use with the ChannelDataInput or ChannelDataOutput. If the user did not specified a buffer, this method may allocate a direct buffer for better leveraging of ChannelDataInput, which tries hard to transfer data in the most direct way between buffers and arrays. By contrast creating a heap buffer may imply the use of a temporary direct buffer cached by the JDK itself (in JDK internal implementation).
      Parameters:
      factory - the factory which will be used for creating the readable or writable channel.
      Returns:
      the byte buffer to use with ChannelDataInput or ChannelDataOutput.
    • createByteBuffer

      private ByteBuffer createByteBuffer() throws IOException, DataStoreException
      Creates a ByteBuffer from the ChannelDataInput if possible, or from the ImageInputStream otherwise. The buffer will be initialized with an arbitrary amount of bytes read from the input. If this amount is not sufficient, it can be increased by a call to prefetch().

      This method is one of the OPENERS methods and should be invoked at most once per StorageConnector instance.

      Throws:
      IOException - if an error occurred while opening a stream for the input.
      DataStoreException
    • prefetch

      final boolean prefetch() throws DataStoreException
      Transfers more bytes from the DataInput to the ByteBuffer, if possible. This method returns true on success, or false if input is not a readable channel or stream, we have reached the end of stream, or the buffer is full.

      This method is invoked when the amount of bytes in the buffer appears to be insufficient for DataStoreProvider.probeContent(StorageConnector) purpose.

      Returns:
      true on success.
      Throws:
      DataStoreException - if an error occurred while reading more bytes.
    • createImageInputStream

      private ImageInputStream createImageInputStream() throws DataStoreException
      Creates an ImageInputStream from the DataInput if possible. This method simply casts DataInput if such cast is allowed. Since createDataInput() instantiates ChannelImageInputStream, this cast is usually possible.

      This method is one of the OPENERS methods and should be invoked at most once per StorageConnector instance.

      Throws:
      DataStoreException
    • createInputStream

      private InputStream createInputStream() throws IOException, DataStoreException
      Creates an input stream from ReadableByteChannel if possible, or from ImageInputStream otherwise.

      This method is one of the OPENERS methods and should be invoked at most once per StorageConnector instance.

      Throws:
      IOException
      DataStoreException
      See Also:
    • createReader

      private Reader createReader() throws IOException, DataStoreException
      Creates a character reader if possible.

      This method is one of the OPENERS methods and should be invoked at most once per StorageConnector instance.

      Throws:
      IOException
      DataStoreException
    • createConnection

      private Connection createConnection() throws SQLException
      Creates a database connection if possible.

      This method is one of the OPENERS methods and should be invoked at most once per StorageConnector instance.

      Throws:
      SQLException
    • createString

      private String createString()
      Returns the storage as a path if possible, or null otherwise.

      This method is one of the OPENERS methods and should be invoked at most once per StorageConnector instance.

    • addView

      private <S> void addView(Class<S> type, S view)
      Adds the given view in the cache, without dependencies.
      Type Parameters:
      S - the compile-time type of the type argument.
      Parameters:
      type - the view type.
      view - the view, or null if none.
    • createChannelDataOutput

      private ChannelDataOutput createChannelDataOutput() throws IOException, DataStoreException
      Creates a view for the storage as a ChannelDataOutput if possible. This code is a partial copy of createDataInput() adapted for output.
      Throws:
      IOException - if an error occurred while opening a channel for the output.
      DataStoreException
      See Also:
    • createDataOutput

      private DataOutput createDataOutput() throws IOException, DataStoreException
      Creates a view for the output as a DataOutput if possible. This code is a copy of createDataInput() adapted for output.
      Throws:
      IOException - if an error occurred while opening a stream for the output.
      DataStoreException
      See Also:
    • createOutputStream

      private OutputStream createOutputStream() throws IOException, DataStoreException
      Creates an output stream from WritableByteChannel if possible, or from ImageOutputStream otherwise. This code is a partial copy of createInputStream() adapted for output.
      Throws:
      IOException
      DataStoreException
      See Also:
    • addView

      private <S> void addView(Class<S> type, S view, Class<?> source, byte cascade)
      Adds the given view in the cache together with information about its dependency. For example, InputStreamReader is a wrapper for a InputStream: read operations from the latter may change position of the former, and closing the latter also close the former.
      Type Parameters:
      S - the compile-time type of the type argument.
      Parameters:
      type - the view type.
      view - the view, or null if none.
      source - the type of input that view is wrapping, or null for storage.
      cascade - bitwise combination of CASCADE_ON_CLOSE, CASCADE_ON_RESET or CLEAR_ON_RESET.
    • getView

      private StorageConnector.Coupled getView(Class<?> type)
      Returns the view for the given type from the cache. This method does not reset the view.
      Parameters:
      type - the view type, or null for the storage container.
      Returns:
      information associated to the given type. May be null if the view has never been requested before. StorageConnector.Coupled.view may be null if the view has been requested and we determined that none can be created.
    • commit

      public <S> S commit(Class<S> type, String format) throws IllegalArgumentException, DataStoreException
      Returns the storage as a view of the given type and closes all other views. Invoking this method is equivalent to invoking getStorageAs(Class) followed by closeAllExcept(Object) except that the latter method is always invoked (in a way similar to "try with resource") and that this method never returns null.
      Type Parameters:
      S - the compile-time type of the type argument (the source or storage type).
      Parameters:
      type - the desired type as one of the types documented in getStorageAs(Class) (example: ByteBuffer, DataInput, Connection).
      format - short name or abbreviation of the data format (e.g. "CSV", "GML", "WKT", etc). Used for information purpose in error messages if needed.
      Returns:
      the storage as a view of the given type. Never null.
      Throws:
      IllegalArgumentException - if the given type argument is not one of the supported types.
      IllegalStateException - if this StorageConnector has been closed.
      DataStoreException - if an error occurred while opening a stream or database connection.
      Since:
      1.2
      See Also:
    • closeAllExcept

      public void closeAllExcept(Object view) throws DataStoreException
      Closes all streams and connections created by this StorageConnector except the given view. This method closes all objects created by the getStorageAs(Class) method except the given view. If view is null, then this method closes everything including the storage if it is closeable.

      This method is invoked when a suitable DataStore has been found - in which case the view used by the data store is given in argument to this method - or when no suitable DataStore has been found - in which case the view argument is null.

      This StorageConnector instance shall not be used anymore after invocation of this method.

      Parameters:
      view - the view to leave open, or null if none.
      Throws:
      DataStoreException - if an error occurred while closing the stream or database connection.
      See Also:
    • unwrap

      @Workaround(library="JDK", version="18") private static IOException unwrap(IIOException e)
      Returns the cause of given exception if it exists, or the exception itself otherwise. This method is invoked in the catch block of a try block invoking ImageIO.createImageInputStream(Object) or ImageIO.createImageOutputStream(Object).

      Rational

      As of Java 18, above-cited methods systematically catch all IOExceptions and wrap them in an IIOException with "Cannot create cache file!" error message. This is conform to Image I/O specification but misleading if the stream provider throws an 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 Also:
    • toString

      public String toString()
      Returns a string representation of this StorageConnector for debugging purpose. This string representation is for diagnostic and may change in any future version.
      Overrides:
      toString in class Object
      Returns:
      a string representation of this StorageConnector for debugging purpose.