sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1770532 - in /sis/branches/JDK8/core: sis-metadata/src/main/java/org/apache/sis/metadata/sql/ sis-utility/src/main/java/org/apache/sis/util/collection/ sis-utility/src/main/java/org/apache/sis/util/resources/
Date Sun, 20 Nov 2016 01:13:35 GMT
Author: desruisseaux
Date: Sun Nov 20 01:13:35 2016
New Revision: 1770532

URL: http://svn.apache.org/viewvc?rev=1770532&view=rev
Log:
Complete the port of MetadataSource, pending a temporary hack in the lookup method to be removed
after we moved format information into the database.

Added:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties

Added: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java?rev=1770532&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
(added)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
[UTF-8] Sun Nov 20 01:13:35 2016
@@ -0,0 +1,145 @@
+/*
+ * 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.metadata.sql;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.sql.SQLException;
+import java.util.Collection;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.collection.BackingStoreException;
+
+
+/**
+ * The handler for metadata proxy that implement (indirectly) metadata interfaces like
+ * {@link org.opengis.metadata.Metadata}, {@link org.opengis.metadata.citation.Citation},
+ * <i>etc</i>.
+ *
+ * Any call to a method in a metadata interface is redirected toward the {@link #invoke}
method.
+ * This method uses reflection in order to find the caller's method and class name. The class
+ * name is translated into a table name, and the method name is translated into a column
name.
+ * Then the information is fetched in the underlying metadata database.
+ *
+ * @author  Touraïvane (IRD)
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+final class Dispatcher implements InvocationHandler {
+    /**
+     * The identifier used in order to locate the record for this metadata entity in the
database.
+     * This is usually the primary key in the table which contains this entity.
+     */
+    final String identifier;
+
+    /**
+     * The connection to the database. All metadata handlers created from a single database
+     * should share the same source.
+     */
+    private final MetadataSource source;
+
+    /**
+     * Index in the {@code MetadataResult} cache array where to search first. This is only
a hint for increasing
+     * the chances to find quickly a {@code MetadataResult} instance for the right type and
identifier.
+     */
+    int preferredIndex;
+
+    /**
+     * Creates a new metadata handler.
+     *
+     * @param identifier  the identifier used in order to locate the record for this metadata
entity in the database.
+     *                    This is usually the primary key in the table which contains this
entity.
+     * @param source      the connection to the table which contains this entity.
+     */
+    public Dispatcher(final String identifier, final MetadataSource source) {
+        this.identifier = identifier;
+        this.source     = source;
+        preferredIndex  = -1;
+    }
+
+    /**
+     * Invoked when any method from a metadata interface is invoked.
+     *
+     * @param  proxy   the object on which the method is invoked.
+     * @param  method  the method invoked.
+     * @param  args    the argument given to the method.
+     * @return the value to be returned from the public method invoked by the method.
+     */
+    @Override
+    public Object invoke(final Object proxy, final Method method, final Object[] args) {
+        final Class<?> type = method.getDeclaringClass();
+        final String   name = method.getName();
+        final int      n    = args.length;
+        switch (name) {
+            case "toString": {
+                if (n != 0) break;
+                return toString(type);
+            }
+            case "hashCode": {
+                if (n != 0) break;
+                return System.identityHashCode(proxy);
+            }
+            case "equals": {
+                if (n != 1) break;
+                return proxy == args[0];
+            }
+            case "identifier": {
+                if (n != 1) break;
+                return (args[0] == source) ? identifier : null;
+            }
+            default: {
+                if (n != 0) break;
+                if (!source.standard.isMetadata(type)) break;
+                /*
+                 * The invoked method is a method from the metadata interface.
+                 * Consequently, the information should exist in the database.
+                 */
+                try {
+                    return source.getValue(type, method, this);
+                } catch (SQLException | MetadataStoreException e) {
+                    Class<?> returnType = method.getReturnType();
+                    if (Collection.class.isAssignableFrom(returnType)) {
+                        final Class<?> elementType = Classes.boundOfParameterizedProperty(method);
+                        if (elementType != null) {
+                            returnType = elementType;
+                        }
+                    }
+                    throw new BackingStoreException(Errors.format(Errors.Keys.DatabaseError_2,
returnType, identifier), e);
+                }
+            }
+        }
+        throw new BackingStoreException(Errors.format(Errors.Keys.UnsupportedOperation_1,
type + "." + name));
+    }
+
+    /**
+     * Returns a string representation of a metadata of the given type.
+     */
+    private String toString(final Class<?> type) {
+        return Classes.getShortName(type) + "[id=“" + identifier + "”]";
+    }
+
+    /**
+     * Returns a string representation of this handler.
+     * This is mostly for debugging purpose.
+     */
+    @Override
+    public String toString() {
+        return toString(getClass());
+    }
+}

Propchange: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java?rev=1770532&r1=1770531&r2=1770532&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
[UTF-8] Sun Nov 20 01:13:35 2016
@@ -23,7 +23,6 @@ import java.util.Set;
 import java.util.HashSet;
 import java.util.TreeSet;
 import java.util.SortedSet;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -33,6 +32,7 @@ import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.lang.reflect.Array;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import javax.sql.DataSource;
 import java.sql.Connection;
 import java.sql.Statement;
@@ -52,6 +52,8 @@ import org.apache.sis.internal.system.Sy
 import org.apache.sis.internal.metadata.sql.Initializer;
 import org.apache.sis.internal.metadata.sql.SQLBuilder;
 import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.util.collection.CodeListSet;
 import org.apache.sis.util.collection.WeakValueHashMap;
 import org.apache.sis.util.resources.Errors;
@@ -127,7 +129,7 @@ public class MetadataSource implements A
      * The previously created objects.
      * Used in order to share existing instances for the same interface and primary key.
      */
-    private final WeakValueHashMap<CacheKey,Object> cache;
+    private final WeakValueHashMap<CacheKey,Object> pool;
 
     /**
      * The last converter used.
@@ -197,7 +199,7 @@ public class MetadataSource implements A
         this.schema   = schema;
         this.tables   = new HashMap<>();
         statements    = new ResultPool(dataSource, this);
-        cache         = new WeakValueHashMap<>(CacheKey.class);
+        pool          = new WeakValueHashMap<>(CacheKey.class);
         loader        = getClass().getClassLoader();
     }
 
@@ -215,7 +217,7 @@ public class MetadataSource implements A
         loader     = source.loader;
         tables     = new HashMap<>();
         statements = new ResultPool(source.statements);
-        cache      = new WeakValueHashMap<>(CacheKey.class);
+        pool       = new WeakValueHashMap<>(CacheKey.class);
     }
 
     /**
@@ -458,15 +460,25 @@ public class MetadataSource implements A
     }
 
     /**
-     * Temporary place-holder for a method to be developed later.
+     * Returns an implementation of the specified metadata interface filled with the data
referenced
+     * by the specified identifier. Alternatively, this method can also returns a {@link
CodeList} element.
+     *
+     * @param  <T>         the parameterized type of the {@code type} argument.
+     * @param  type        the interface to implement (e.g. {@link org.opengis.metadata.citation.Citation}),
+     *                     or the {@link CodeList} value.
+     * @param  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.
+     * @return an implementation of the required interface, or the code list element.
+     * @throws MetadataStoreException if a SQL query failed.
      */
     public <T> T lookup(final Class<T> type, String identifier) throws MetadataStoreException
{
+        ArgumentChecks.ensureNonNull("type", type);
+        ArgumentChecks.ensureNonNull("identifier", identifier);
+        /*
+         * TODO: temporary hack until we ported the following information to the database.
+         */
         if (type == Format.class) {
             final DefaultCitation spec = new DefaultCitation();
-            /*
-             * TODO: move the following hard-coded values in a database
-             * after we ported the org.apache.sis.metadata.sql package.
-             */
             String title = null;
             switch (identifier) {
                 case "GeoTIFF": title = "GeoTIFF Coverage Encoding Profile"; break;
@@ -483,22 +495,38 @@ public class MetadataSource implements A
             format.setFormatSpecificationCitation(spec);
             return (T) format;
         }
-        return null;
+        /*
+         * IMPLEMENTATION NOTE: This method must not invoke any method which may access 'statements'.
+         * It is not allowed to acquire the lock on 'statements' neither.
+         */
+        Object value;
+        if (CodeList.class.isAssignableFrom(type)) {
+            value = getCodeList(type, identifier);
+        } else {
+            final CacheKey key = new CacheKey(type, identifier);
+            synchronized (pool) {
+                value = pool.get(key);
+                if (value == null) {
+                    value = Proxy.newProxyInstance(loader,
+                            new Class<?>[] {type, MetadataProxy.class}, new Dispatcher(identifier,
this));
+                    pool.put(key, value);
+                }
+            }
+        }
+        return type.cast(value);
     }
 
     /**
      * Returns an attribute from a table.
      *
-     * @param  type            the interface class. This is mapped to the table name in the
database.
-     * @param  method          the method invoked. This is mapped to the column name in the
database.
-     * @param  identifier      the primary key of the record to search for.
-     * @param  preferredIndex  index in the cache array where to search first. This is only
a hint for increasing
-     *         the chances to find quickly a {@code MetadataResult} instance for the right
type and identifier.
+     * @param  type      the interface class. This is mapped to the table name in the database.
+     * @param  method    the method invoked. This is mapped to the column name in the database.
+     * @param  toSearch  contains the identifier and preferred index of the record to search.
      * @return the value of the requested attribute.
      * @throws SQLException if the SQL query failed.
      * @throws MetadataStoreException if a value can not be converted to the expected type.
      */
-    final Object getValue(final Class<?> type, final Method method, final String identifier,
int preferredIndex)
+    final Object getValue(final Class<?> type, final Method method, final Dispatcher
toSearch)
             throws SQLException, MetadataStoreException
     {
         final Class<?> returnType     = method.getReturnType();
@@ -524,7 +552,7 @@ public class MetadataSource implements A
                  * Note that the usage of 'result' must stay inside this synchronized block
                  * because we can not assume that JDBC connections are thread-safe.
                  */
-                MetadataResult result = statements.take(type, preferredIndex);
+                MetadataResult result = statements.take(type, toSearch.preferredIndex);
                 if (result == null) {
                     final SQLBuilder helper = statements.helper();
                     final String query = helper.clear().append("SELECT * FROM ")
@@ -532,14 +560,14 @@ public class MetadataSource implements A
                             .append(ID_COLUMN).append("=?").toString();
                     result = new MetadataResult(type, connection.prepareStatement(query),
statements.listeners);
                 }
-                value = result.getValue(identifier, columnName);
+                value = result.getValue(toSearch.identifier, columnName);
                 isArray = (value instanceof java.sql.Array);
                 if (isArray) {
                     final java.sql.Array array = (java.sql.Array) value;
                     value = array.getArray();
                     array.free();
                 }
-                preferredIndex = statements.recycle(result, preferredIndex);
+                toSearch.preferredIndex = statements.recycle(result, toSearch.preferredIndex);
             }
         }
         /*
@@ -564,7 +592,7 @@ public class MetadataSource implements A
             }
             value = values;             // Now a Java array.
             if (wantCollection) {
-                value = specialize(Arrays.asList(values), returnType, elementType);
+                value = specialize(UnmodifiableArrayList.wrap(values), returnType, elementType);
             }
         }
         /*
@@ -574,7 +602,11 @@ public class MetadataSource implements A
         if (value == null) {
             if (wantCollection) {
                 if (Set.class.isAssignableFrom(returnType)) {
-                    return Collections.EMPTY_SET;
+                    if (SortedSet.class.isAssignableFrom(returnType)) {
+                        return Collections.emptySortedSet();
+                    } else {
+                        return Collections.EMPTY_SET;
+                    }
                 } else {
                     return Collections.EMPTY_LIST;
                 }
@@ -644,11 +676,11 @@ public class MetadataSource implements A
         if (!returnType.isAssignableFrom(Set.class)) {
             return collection;
         }
-        final Set<E> s;
+        final Set<E> enumeration;
         if (CodeList.class.isAssignableFrom(elementType)) {
-            s = new CodeListSet<>((Class) elementType);
+            enumeration = new CodeListSet<>((Class) elementType);
         } else if (Enum.class.isAssignableFrom(elementType)) {
-            s = EnumSet.noneOf((Class) elementType);
+            enumeration = EnumSet.noneOf((Class) elementType);
         } else {
             /*
              * If 'returnType' is Collection.class, do not copy into a Set since a List
@@ -656,17 +688,25 @@ public class MetadataSource implements A
              */
             if (Set.class.isAssignableFrom(returnType)) {
                 if (SortedSet.class.isAssignableFrom(returnType)) {
-                    collection = new TreeSet<>(collection);
+                    if (collection.isEmpty()) {
+                        collection = Collections.emptySortedSet();
+                    } else {
+                        collection = Collections.unmodifiableSortedSet(new TreeSet<>(collection));
+                    }
                 } else {
-                    collection = new LinkedHashSet<>(collection);
+                    switch (collection.size()) {
+                        case 0:  collection = Collections.emptySet(); break;
+                        case 1:  collection = Collections.singleton(CollectionsExt.first(collection));
break;
+                        default: collection = Collections.unmodifiableSet(new LinkedHashSet<>(collection));
break;
+                    }
                 }
             }
             return collection;
         }
         for (final Object e : collection) {
-            s.add(elementType.cast(e));
+            enumeration.add(elementType.cast(e));
         }
-        return s;
+        return Collections.unmodifiableSet(enumeration);
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java?rev=1770532&r1=1770531&r2=1770532&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java
[UTF-8] Sun Nov 20 01:13:35 2016
@@ -21,10 +21,10 @@ import java.sql.SQLException;
 
 
 /**
- * Thrown to indicate that an operation could not complete because of a failure in the backing
- * store (a file or a database). This exception is thrown by collection implementations that
are
- * not allowed to throw checked exceptions. This exception usually has an {@link IOException}
or
- * a {@link SQLException} as its {@linkplain #getCause() cause}.
+ * Thrown to indicate that an operation could not complete because of a failure in the backing
store
+ * (a file or a database). This exception is thrown by implementations of API (collection,
streams,
+ * <i>etc.</i> that are not allowed to throw checked exceptions.
+ * This exception usually has an {@link IOException} or a {@link SQLException} as its {@linkplain
#getCause() cause}.
  *
  * <p>This method provides a {@link #unwrapOrRethrow(Class)} convenience method which
can be used
  * for re-throwing the cause as in the example below. This allows client code to behave as
if a

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1770532&r1=1770531&r2=1770532&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] Sun Nov 20 01:13:35 2016
@@ -166,7 +166,7 @@ public final class Errors extends Indexe
         public static final short ClosedReader_1 = 19;
 
         /**
-         * Database error while creating a ‘{0}’ object for code “{1}”.
+         * Database error while creating a ‘{0}’ object for the “{1}” identifier.
          */
         public static final short DatabaseError_2 = 20;
 

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1770532&r1=1770531&r2=1770532&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] Sun Nov 20 01:13:35 2016
@@ -44,7 +44,7 @@ CircularReference                 = Circ
 ClassNotFinal_1                   = Class \u2018{0}\u2019 is not final.
 CloneNotSupported_1               = Can not clone an object of type \u2018{0}\u2019.
 ClosedReader_1                    = This {0} reader is closed.
-DatabaseError_2                   = Database error while creating a \u2018{0}\u2019 object
for code \u201c{1}\u201d.
+DatabaseError_2                   = Database error while creating a \u2018{0}\u2019 object
for the \u201c{1}\u201d identifier.
 DeadThread_1                      = Thread \u201c{0}\u201d is dead.
 DisposedInstanceOf_1              = This instance of \u2018{0}\u2019 has been disposed.
 DuplicatedElement_1               = Element \u201c{0}\u201d is duplicated.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1770532&r1=1770531&r2=1770532&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] Sun Nov 20 01:13:35 2016
@@ -41,7 +41,7 @@ CircularReference                 = R\u0
 ClassNotFinal_1                   = La classe \u2018{0}\u2019 n\u2019est pas finale.
 CloneNotSupported_1               = Un objet de type \u2018{0}\u2019 ne peut pas \u00eatre
clon\u00e9.
 ClosedReader_1                    = Ce lecteur {0} est ferm\u00e9.
-DatabaseError_2                   = Erreur de base de donn\u00e9es lors de la cr\u00e9ation
d\u2019un objet \u2018{0}\u2019 pour le code \u00ab\u202f{1}\u202f\u00bb.
+DatabaseError_2                   = Erreur de base de donn\u00e9es lors de la cr\u00e9ation
d\u2019un objet \u2018{0}\u2019 pour l\u2019identifiant \u00ab\u202f{1}\u202f\u00bb.
 DeadThread_1                      = La t\u00e2che \u00ab\u202f{0}\u202f\u00bb est morte.
 DisposedInstanceOf_1              = Cette instance de \u2018{0}\u2019 a \u00e9t\u00e9 dispos\u00e9e.
 DuplicatedElement_1               = L\u2019\u00e9lement \u00ab\u202f{0}\u202f\u00bb est dupliqu\u00e9.



Mime
View raw message