Class MetadataSource

java.lang.Object
org.apache.sis.metadata.sql.MetadataSource
All Implemented Interfaces:
AutoCloseable
Direct Known Subclasses:
MetadataFallback, MetadataWriter

public class MetadataSource extends Object implements AutoCloseable
A connection to a metadata database in read-only mode. It can be either the database provided by Apache SIS with predefined ISO 19115 metadata, or another database specified at construction time. Metadata instances can be obtained as in the example below: where id is the primary key value for the desired record in the Format table.

Properties

The constructor expects three Java arguments (the metadata standard, the data source and the database schema) completed by an arbitrary amount of optional arguments given as a map of properties. The following keys are recognized by MetadataSource and all other entries are ignored:
Optional properties at construction time
Key Value type Description
"catalog" String The database catalog where the metadata schema is stored.
"classloader" ClassLoader The class loader to use for creating Proxy instances.
"maxStatements" Integer Maximal number of PreparedStatements that can be kept simultaneously open.

Concurrency

MetadataSource is thread-safe but is not concurrent. If concurrency is desired, multiple instances of MetadataSource can be created for the same DataSource. The MetadataSource(MetadataSource) convenience constructor can be used for this purpose.
Since:
0.8
Version:
1.2
  • Field Details

    • NAME_POLICY

      static final KeyNamePolicy NAME_POLICY
      The policy for column names. We use UML identifiers (e.g. "title") as defined by the metadata standard (typically ISO 19115).
    • ID_COLUMN

      static final String ID_COLUMN
      The column name used for the identifiers.
      See Also:
    • TIMEOUT

      private static final long TIMEOUT
      The timeout before to close a prepared statement, in nanoseconds. This is set to 2 seconds, which is a bit short but should be okay if the DataSource creates pooled connections. In case there is no connection pool, then the mechanism defined in this package will hopefully keeps the performance at a reasonable level.
      See Also:
    • EXTRA_DELAY

      private static final int EXTRA_DELAY
      An extra delay to add to the TIMEOUT in order to increase the chances to close many statements at once.
      See Also:
    • standard

      protected final MetadataStandard standard
      The metadata standard to be used for constructing the database schema.
    • dataSource

      private final DataSource dataSource
      The data source object for fetching the connection to the database. This is specified at construction time.
    • connection

      private Connection connection
      The connection to the database, or null if not yet created or if closed. This field is set to a non-null value when connection() is invoked, then closed and set to null after all cached statements have been closed.
      See Also:
    • statements

      private final CachedStatement[] statements
      A pool of prepared statements with a maximal capacity equals to the array length. The array length should be reasonably small. The array may contain null element anywhere. Inactive statements are closed after some timeout.
      Note: this array duplicates the work done by statement pools in modern JDBC drivers. Nevertheless it still useful in our case since we retain some additional JDBC resources together with the PreparedStatement, for example the ResultSet created from that statement.
      Every access to this array must be synchronized on MetadataSource.this. Execution of a prepared statement may also need to be done inside the synchronized block, because a single JDBC connection cannot be assumed thread-safe.

      Usage example:

      See Also:
    • catalog

      final String catalog
      The catalog, or null if none.
    • schema

      private String schema
      The database schema where metadata are stored, or null if none. In the metadata source provided by SIS, this is "metadata" or "METADATA", depending on the database convention regarding lower case / upper case identifiers.

      Consider this field as final. This field is modified only by install() for taking in account the lower case or upper case convention of the database engine.

    • quoteSchema

      private boolean quoteSchema
      Whether the helper should quote schemas in SQL statements. A value of false let the database engine chooses its own lower case / upper case policy. This flag is true if the schema was specified by the user, or false if using the metadata provided by SIS.

      Consider this field as final. This field is modified only by install().

      See Also:
    • helper

      private transient SQLBuilder helper
      A helper class used for constructing SQL statements. This helper is created when first needed, then kept until the connection is closed.
      See Also:
    • tableColumns

      private final Map<String,Set<String>> tableColumns
      All columns found in tables that have been queried or created up to date. Keys are table names and values are the columns defined for that table.
      See Also:
    • classloader

      private final ClassLoader classloader
      The class loader to use for creating Proxy instances.
      See Also:
    • pool

      private final WeakValueHashMap<CacheKey,Object> pool
      The objects which have been created by a previous call to lookup(Class, String). Used in order to share existing instances for the same interface and primary key.
      See Also:
    • lastUsed

      private final ThreadLocal<LookupInfo> lastUsed
      Some information about last used objects. Cached on assumption that the same information will be used more than once before to move to another metadata object.
    • logFilter

      private volatile Filter logFilter
      Where to report the warnings before to eventually log them.
      See Also:
    • isCloseScheduled

      private boolean isCloseScheduled
      Whether at least one MetadataSource.CloseTask is scheduled for execution.
      See Also:
    • instance

      private static MetadataSource instance
      The instance connected to the "jdbc/SpatialMetadata" database, created when first needed and cleared when the classpath change. May be MetadataFallback.INSTANCE if we failed to establish a connection to the database for a non-transient reason.
  • Constructor Details

    • MetadataSource

      public MetadataSource(MetadataStandard standard, DataSource dataSource, String schema, Map<String,?> properties)
      Creates a new metadata source. The metadata standard to implement (typically ISO 19115, but not necessarily) and the database source are mandatory information. All other information are optional and can be null.
      Parameters:
      standard - the metadata standard to implement.
      dataSource - the source for getting a connection to the database.
      schema - the database schema were metadata tables are stored, or null if none.
      properties - additional options, or null if none. See class javadoc for a description.
    • MetadataSource

      public MetadataSource(MetadataSource source)
      Creates a new metadata source with the same configuration than the given source. The two MetadataSource instances will share the same DataSource but will use their own Connection. This constructor is useful when concurrency is desired.

      The new MetadataSource initially contains the warning filter declared in the given source.

      Parameters:
      source - the source from which to copy the configuration.
    • MetadataSource

      MetadataSource()
      For MetadataFallback constructor only.
  • Method Details

    • getProvided

      public static MetadataSource getProvided()
      Returns the metadata source connected to the "jdbc/SpatialMetadata" database. In a default Apache SIS installation, this metadata source contains predefined records for some commonly used citations and formats among others.

      If connection to the metadata database cannot be established, then this method returns a fallback with a few hard-coded values.

      Returns:
      source of predefined metadata records from the "jdbc/SpatialMetadata" database.
    • install

      final void install() throws IOException, SQLException
      If the metadata schema does not exist in the database, creates it and inserts the predefined metadata values. The current implementation has the following restrictions:
      • Metadata standard must be MetadataStandard.ISO_19115 or compatible.
      • The schema name must be "metadata", as this is the name used unquoted in SQL scripts.
      Maintenance note: this method is invoked by reflection in non-free:sis-embedded-data module. If we make this method public in a future Apache SIS version, then we can remove the reflection code.
      Throws:
      SQLException - if an error occurred while inserting the metadata.
      IOException
    • connection

      final Connection connection() throws SQLException
      Returns the connection to the database, creating a new one if needed. This method shall be invoked inside a synchronized block wider than just the scope of this method in order to ensure that the connection is used by only one thread at time. This is also necessary for preventing the background thread to close the connection too early.

      Callers shall not close the connection returned by this method. The connection will be closed by closeExpired() after an arbitrary timeout.

      Returns:
      the connection to the database.
      Throws:
      SQLException - if an error occurred while fetching the connection.
    • schema

      final String schema()
      Returns the database schema where metadata are stored, or null if none.
    • helper

      final SQLBuilder helper() throws SQLException
      Returns a helper class for building SQL statements.
      Throws:
      SQLException
    • prepareStatement

      private CachedStatement prepareStatement(Class<?> type, String tableName, int preferredIndex) throws SQLException
      Returns a statement that can be reused for performing queries on the table for the specified interface. Callers must invoke this method in a block synchronized on this.
      Parameters:
      type - the interface for which to reuse a prepared statement.
      tableName - value of getTableName(type), or null for computing by this method.
      preferredIndex - index in the cache array where to search first. This is only a hint for increasing the chances to find quickly a CachedStatement instance for the right type and identifier.
      Throws:
      SQLException
    • recycle

      private int recycle(CachedStatement statement, int preferredIndex) throws SQLException
      Flags the given CachedStatement as available for reuse.
      Parameters:
      statement - the prepared statement to cache.
      preferredIndex - index in the cache array to use if the corresponding slot is available.
      Returns:
      index in the cache array where the result has been actually stored.
      Throws:
      SQLException
    • getTableName

      static String getTableName(Class<?> type)
      Returns the table name for the specified class. This is usually the ISO 19115 name, but we fallback on the simple class name if the ISO name is not available. The package prefix is omitted (e.g. "CI_" in "CI_Citation" since newer ISO standards tend to drop it.
    • proxy

      final String proxy(Object metadata)
      If the given metadata is a proxy generated by this MetadataSource, returns the identifier of that proxy. Such metadata do not need to be inserted again in the database.
      Parameters:
      metadata - the metadata to test.
      Returns:
      the identifier (primary key), or null if the given metadata is not a proxy.
    • asValueMap

      final Map<String,Object> asValueMap(Object metadata) throws ClassCastException
      Returns a view of the given metadata as a map. This method returns always a map using UML identifier and containing all entries including the null ones because the MetadataSource implementation assumes so.
      Parameters:
      metadata - the metadata object to view as a map.
      Returns:
      a map view over the metadata object.
      Throws:
      ClassCastException - if the metadata object does not implement a metadata interface of the expected package.
    • extractFromCollection

      static Object extractFromCollection(Object value)
      If the given value is a collection, returns the first element in that collection or null if empty.
      Parameters:
      value - the value to inspect (can be null).
      Returns:
      the given value, or its first element if the value is a collection, or null if the given value is null or an empty collection.
    • search

      public String search(Object metadata) throws MetadataStoreException
      Searches for the given metadata in the database. If such metadata is found, then its identifier (primary key) is returned. Otherwise this method returns null.
      Parameters:
      metadata - the metadata to search for.
      Returns:
      the identifier of the given metadata, or null if none.
      Throws:
      MetadataStoreException - if the metadata object does not implement a metadata interface of the expected package, or if an error occurred while searching in the database.
    • search

      final String search(String table, Set<String> columns, Map<String,Object> metadata, Statement stmt, SQLBuilder helper) throws SQLException, org.opengis.util.FactoryException
      Searches for the given metadata in the database. If such metadata is found, then its identifier (primary key) is returned. Otherwise this method returns null.
      Parameters:
      table - the table where to search.
      columns - the table columns as given by getExistingColumns(String), or null.
      metadata - a map view of the metadata to search for.
      stmt - the statement to use for executing the query.
      helper - an helper class for creating the SQL query.
      Returns:
      the identifier of the given metadata, or null if none.
      Throws:
      SQLException - if an error occurred while searching in the database.
      org.opengis.util.FactoryException
    • toStorableValue

      static Object toStorableValue(Object value) throws org.opengis.util.FactoryException
      Converts the given object to a value that can be stored in the database.
      Throws:
      org.opengis.util.FactoryException - if an error occurred while using the geodetic database.
    • getExistingColumns

      final Set<String> getExistingColumns(String table) throws SQLException
      Returns the set of all columns in a table, or an empty set if none (never null). Because each table should have at least the "ID" column, an empty set of columns will be understood as meaning that the table does not exist.

      This method returns a direct reference to the cached set. The returned set shall be modified in-place if new columns are added in the database table.

      Parameters:
      table - the name of the table for which to get the columns.
      Returns:
      the set of columns, or an empty set if the table has not yet been created.
      Throws:
      SQLException - if an error occurred while querying the database.
    • lookup

      public <T> T lookup(Class<T> type, String identifier) throws MetadataStoreException
      Returns an implementation of the specified metadata interface filled with the data referenced by the specified identifier. Alternatively, this method can also return a CodeList or Enum element.
      Type Parameters:
      T - the parameterized type of the type argument.
      Parameters:
      type - the interface to implement (e.g. Citation), or the ControlledVocabulary type (CodeList or some Enum).
      identifier - the identifier of the record for the metadata entity to be created. This is usually the primary key of the record to search for.
      Returns:
      an implementation of the required interface, or the code list element.
      Throws:
      MetadataStoreException - if a SQL query failed or if the metadata has not been found.
    • lookup

      private Object lookup(Class<?> type, String identifier, boolean verify) throws MetadataStoreException
      Implementation of public lookup(Class, String) method.

      Deferred database access

      This method may or may not query the database immediately, at implementation choice. It the database is not queried immediately, invalid identifiers may not be detected during this method invocation. Instead, an invalid identifier may be detected only when a getter method is invoked on the returned metadata object. In such case, an BackingStoreException will be thrown at getter method invocation time.
      Parameters:
      type - the interface to implement or the ControlledVocabulary type.
      identifier - the identifier of the record for the metadata entity to be created.
      verify - whether to check for record existence.
      Returns:
      an implementation of the required interface, or the code list element.
      Throws:
      MetadataStoreException - if a SQL query failed or if the metadata has not been found.
    • getLookupInfo

      final LookupInfo getLookupInfo(Class<?> type)
      Gets the LookupInfo instance for call to the readColumn(LookupInfo, Method, Dispatcher) method. The call to those two methods must be in the same thread, and no other metadata object shall be queried between the two calls (unless LookupInfo.setMetadataType(Class) is invoked again).
      Parameters:
      type - the interface class. This is mapped to the table name in the database.
    • readColumn

      final Object readColumn(LookupInfo info, Method method, Dispatcher toSearch) throws SQLException, MetadataStoreException
      Invoked by MetadataProxy for fetching an attribute value from a table. It the database table does not contains a column for the property, this method returns null. A null value may also mean that the column exists but contains an SQL NULL value.
      Parameters:
      info - the interface type (together with cached information). This is mapped to the table name in the database.
      method - the method invoked. This is mapped to the column name in the database.
      toSearch - contains the identifier and preferred index of the record to search.
      Returns:
      the value of the requested attribute, or null if none.
      Throws:
      SQLException - if the SQL query failed.
      MetadataStoreException - if a value was not found or cannot be converted to the expected type.
    • getCodeList

      static org.opengis.util.CodeList<?> getCodeList(Class<?> type, String name)
      Returns the code of the given type and name. This method is defined for avoiding the compiler warning message when the actual class is unknown (it must have been checked dynamically by the caller however).
    • specialize

      private static <E> Collection<?> specialize(Collection<?> collection, Class<?> returnType, Class<E> elementType)
      Copies the given collection into the best Set implementation if possible, or returns the given collection unchanged otherwise.
      Parameters:
      collection - the collection to copy.
      returnType - the desired collection type.
      elementType - the type of elements in the collection.
      Returns:
      the collection of a specialized type if relevant.
    • warning

      final void warning(Class<? extends MetadataSource> source, String method, LogRecord record)
      Reports a warning.
      Parameters:
      source - the source class, either MetadataSource or MetadataWriter.
      method - the method to report as the warning emitter.
      record - the warning to report.
    • setWarningFilter

      public Filter setWarningFilter(Filter filter)
      Sets a filter to be notified when a warning occurred while reading from or writing metadata. When a warning occurs, there is a choice:
      • If this metadata source has no warning filter, or if the filter returns true, then the warning is logged at Level.WARNING.
      • Otherwise the warning is not logged by this metadata source instance. The filter implementation is free to keep a reference to the given record, for example in order to display it in a graphical user interface.
      Parameters:
      filter - the filter to set, or null for removing the filter.
      Returns:
      the previous filter, or null if none.
      Since:
      1.1
    • getWarningFilter

      public Filter getWarningFilter()
      Returns the current warning filter.
      Returns:
      the current filter, or null if none.
      Since:
      1.1
    • scheduleCloseTask

      private void scheduleCloseTask()
      Schedules a task for closing the statements and the connection, if no such task is scheduled.
    • closeExpired

      final void closeExpired()
      Executed in a background thread for closing statements after their expiration time. This task will be given to the executor every time the first statement is recycled.
    • closeQuietly

      private void closeQuietly(AutoCloseable resource)
      Closes the given resource without throwing exception. In case of failure while closing the resource, the message is logged but the process continue since we are not supposed to use the resource anymore. This method is invoked from methods that cannot throw a SQL exception.
    • close

      public void close() throws MetadataStoreException
      Closes the database connection used by this object.
      Specified by:
      close in interface AutoCloseable
      Throws:
      MetadataStoreException - if an error occurred while closing the connection.