sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1795682 - in /sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata: ./ sql/
Date Sat, 20 May 2017 23:36:44 GMT
Author: desruisseaux
Date: Sat May 20 23:36:44 2017
New Revision: 1795682

URL: http://svn.apache.org/viewvc?rev=1795682&view=rev
Log:
First draft of a caching mechanism in the metadata objects created from database.
Actually the main intend is not that much to do caching, but rather to leverage the code that
compute property value from some other property values
(this computation part will be completed in a next commit). The main use case is supporting
the transition from ISO 19115:2003 to ISO 19115:2014.

Added:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/IndexMap.java
      - copied, changed from r1795677, sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/NameMap.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/LookupInfo.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java

Copied: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/IndexMap.java
(from r1795677, sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/NameMap.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/IndexMap.java?p2=sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/IndexMap.java&p1=sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/NameMap.java&r1=1795677&r2=1795682&rev=1795682&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/NameMap.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/IndexMap.java
[UTF-8] Sat May 20 23:36:44 2017
@@ -22,32 +22,25 @@ import java.util.NoSuchElementException;
 
 
 /**
- * Map of property names for a given implementation class. This map is read-only.
+ * Map of property indices for a given implementation class. This map is read-only.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.3
+ * @version 0.8
  *
- * @see MetadataStandard#asNameMap(Class, KeyNamePolicy, KeyNamePolicy)
+ * @see MetadataStandard#asIndexMap(Class, KeyNamePolicy)
  *
- * @since 0.3
+ * @since 0.8
  * @module
  */
-final class NameMap extends PropertyMap<String> {
-    /**
-     * Determines the string representation of values in this map.
-     */
-    final KeyNamePolicy valuePolicy;
-
+final class IndexMap extends PropertyMap<Integer> {
     /**
      * Creates a name map for the specified accessor.
      *
-     * @param accessor     the accessor to use for the metadata.
-     * @param keyPolicy    determines the string representation of keys in the map.
-     * @param valuePolicy  determines the string representation of values in this map.
+     * @param accessor   the accessor to use for the metadata.
+     * @param keyPolicy  determines the string representation of keys in the map.
      */
-    NameMap(final PropertyAccessor accessor, final KeyNamePolicy keyPolicy, final KeyNamePolicy
valuePolicy) {
+    IndexMap(final PropertyAccessor accessor, final KeyNamePolicy keyPolicy) {
         super(accessor, keyPolicy);
-        this.valuePolicy = valuePolicy;
     }
 
     /**
@@ -55,9 +48,10 @@ final class NameMap extends PropertyMap<
      * if this map contains no mapping for the key.
      */
     @Override
-    public String get(final Object key) {
+    public Integer get(final Object key) {
         if (key instanceof String) {
-            return accessor.name(accessor.indexOf((String) key, false), valuePolicy);
+            final int i = accessor.indexOf((String) key, false);
+            if (i >= 0) return i;
         }
         return null;
     }
@@ -66,16 +60,16 @@ final class NameMap extends PropertyMap<
      * Returns an iterator over the entries contained in this map.
      */
     @Override
-    final Iterator<Map.Entry<String,String>> iterator() {
+    final Iterator<Map.Entry<String,Integer>> iterator() {
         return new Iter() {
             @Override
-            public Map.Entry<String,String> next() {
-                final String value = accessor.name(index, valuePolicy);
-                if (value == null) {
-                    // PropertyAccessor.name(int) never return null if the index is valid.
-                    throw new NoSuchElementException();
+            public Map.Entry<String,Integer> next() {
+                final int i = index++;
+                final String name = accessor.name(i, keyPolicy);
+                if (name != null) {
+                    return new SimpleImmutableEntry<>(name, i);
                 }
-                return new SimpleImmutableEntry<>(accessor.name(index++, keyPolicy),
value);
+                throw new NoSuchElementException();
             }
         };
     }

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java?rev=1795682&r1=1795681&r2=1795682&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
[UTF-8] Sat May 20 23:36:44 2017
@@ -590,8 +590,11 @@ public class MetadataStandard implements
 
     /**
      * Returns the implementation class for the given interface, or {@code null} if none.
-     * The default implementation returns {@code null} in every cases. Subclasses shall
-     * override this method in order to map GeoAPI interfaces to their implementation.
+     * If non-null, the returned class must have a public no-argument constructor and the
+     * metadata instance created by that constructor must be initially empty (no default
value).
+     *
+     * <p>The default implementation returns {@code null} in every cases. Subclasses
shall
+     * override this method in order to map GeoAPI interfaces to their implementation.</p>
      *
      * @param  <T>   the compile-time {@code type}.
      * @param  type  the interface, typically from the {@code org.opengis.metadata} package.
@@ -645,14 +648,14 @@ public class MetadataStandard implements
     }
 
     /**
-     * Returns the type of all properties, or their declaring type, defined in the given
-     * metadata type. The keys in the returned map are the same than the keys in the above
-     * {@linkplain #asNameMap name map}. The values are determined by the {@code valuePolicy}
-     * argument, which can be {@linkplain TypeValuePolicy#ELEMENT_TYPE element type} or the
+     * Returns the type of all properties, or their declaring type, defined in the given
metadata type.
+     * The keys in the returned map are the same than the keys in the above {@linkplain #asNameMap
name map}.
+     * The values are determined by the {@code valuePolicy} argument, which can be
+     * {@linkplain TypeValuePolicy#ELEMENT_TYPE element type} or the
      * {@linkplain TypeValuePolicy#DECLARING_INTERFACE declaring interface} among others.
      *
-     * <p><b>Example:</b> the following code prints the
-     * {@link org.opengis.util.InternationalString} class name:</p>
+     * <div class="note"><b>Example:</b>
+     * the following code prints the {@link org.opengis.util.InternationalString} class name:
      *
      * {@preformat java
      *   MetadataStandard  standard = MetadataStandard.ISO_19115;
@@ -660,6 +663,7 @@ public class MetadataStandard implements
      *   Class<?> value = types.get("alternateTitle");
      *   System.out.println(value);                       // class org.opengis.util.InternationalString
      * }
+     * </div>
      *
      * @param  type         the interface or implementation class of a metadata.
      * @param  keyPolicy    determines the string representation of map keys.
@@ -708,7 +712,7 @@ public class MetadataStandard implements
      * </ul>
      *
      * <div class="note"><b>Note:</b>
-     * The rational for implementing {@code CheckedContainer} is to consider each {@code
ExtendedElementInformation}
+     * the rational for implementing {@code CheckedContainer} is to consider each {@code
ExtendedElementInformation}
      * instance as the set of all possible values for the property. If the information had
a {@code contains(E)} method,
      * it would return {@code true} if the given value is valid for that property.</div>
      *
@@ -726,8 +730,8 @@ public class MetadataStandard implements
      *
      * @see org.apache.sis.metadata.iso.DefaultExtendedElementInformation
      */
-    public Map<String,ExtendedElementInformation> asInformationMap(Class<?> type,
-            final KeyNamePolicy keyPolicy) throws ClassCastException
+    public Map<String,ExtendedElementInformation> asInformationMap(Class<?> type,
final KeyNamePolicy keyPolicy)
+            throws ClassCastException
     {
         ensureNonNull("type",     type);
         ensureNonNull("keyNames", keyPolicy);
@@ -739,6 +743,35 @@ public class MetadataStandard implements
     }
 
     /**
+     * Returns indices for all properties defined in the given metadata type.
+     * The keys in the returned map are the same than the keys in the above {@linkplain #asNameMap
name map}.
+     * The values are arbitrary indices numbered from 0 inclusive to <var>n</var>
exclusive, where <var>n</var>
+     * is the number of properties declared in the given metadata type.
+     *
+     * <p>Property indices may be used as an alternative to property names by some
applications doing their own storage.
+     * Such index usages are fine for temporary storage during the Java Virtual Machine lifetime,
but indices should not
+     * be used in permanent storage. The indices are stable as long as the metadata implementation
does not change,
+     * but may change when the implementation is upgraded to a newer version.</p>
+     *
+     * @param  type       the interface or implementation class of a metadata.
+     * @param  keyPolicy  determines the string representation of map keys.
+     * @return indices of all properties defined by the given metadata type.
+     * @throws ClassCastException if the specified interface or implementation class does
+     *         not extend or implement a metadata interface of the expected package.
+     */
+    public Map<String,Integer> asIndexMap(Class<?> type, final KeyNamePolicy
keyPolicy)
+            throws ClassCastException
+    {
+        ensureNonNull("type",      type);
+        ensureNonNull("keyPolicy", keyPolicy);
+        final Class<?> implementation = getImplementation(type);
+        if (implementation != null) {
+            type = implementation;
+        }
+        return new IndexMap(getAccessor(new CacheKey(type), true), keyPolicy);
+    }
+
+    /**
      * Returns a view of the specified metadata object as a {@link Map}.
      * The map is backed by the metadata object using Java reflection, so changes in the
      * underlying metadata object are immediately reflected in the map and conversely.

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java?rev=1795682&r1=1795681&r2=1795682&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java
[UTF-8] Sat May 20 23:36:44 2017
@@ -37,11 +37,7 @@ final class ObjectPair {
     /**
      * The set of objects currently in process of being compared.
      */
-    static final ThreadLocal<Set<ObjectPair>> CURRENT = new ThreadLocal<Set<ObjectPair>>()
{
-        @Override protected Set<ObjectPair> initialValue() {
-            return new HashSet<>();
-        }
-    };
+    static final ThreadLocal<Set<ObjectPair>> CURRENT = ThreadLocal.withInitial(HashSet::new);
 
     /**
      * The pair of objects in process of being compared.

Modified: 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=1795682&r1=1795681&r2=1795682&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
[UTF-8] Sat May 20 23:36:44 2017
@@ -20,9 +20,14 @@ import java.lang.reflect.InvocationHandl
 import java.lang.reflect.Method;
 import java.sql.SQLException;
 import java.util.Collection;
+import java.util.Map;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.metadata.KeyNamePolicy;
+import org.apache.sis.metadata.ValueExistencePolicy;
+import org.apache.sis.internal.system.Semaphores;
 
 
 /**
@@ -35,6 +40,16 @@ import org.apache.sis.util.collection.Ba
  * 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.
  *
+ * <p>There is usually a one-to-one correspondence between invoked methods and the
columns to be read, but not always.
+ * Some method invocations may actually trig a computation using the values of other columns.
This happen for example
+ * when invoking a deprecated method which computes its value from non-deprecated methods.
Such situations happen in
+ * the transition from ISO 19115:2003 to ISO 19115:2014 and may happen again in the future
as standards are revised.
+ * The algorithms are encoded in implementation classes like the ones in {@link org.apache.sis.metadata.iso}
packages,
+ * and access to those implementation classes is enabled by the {@link #cache} field (which,
consequently, is more than
+ * only a cache).</p>
+ *
+ * <p>Instance of this class shall be thread-safe.</p>
+ *
  * @author  Touraïvane (IRD)
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 0.8
@@ -57,8 +72,35 @@ final class Dispatcher implements Invoca
     /**
      * Index in the {@code CachedStatement} cache array where to search first. This is only
a hint for increasing
      * the chances to find quickly a {@code CachedStatement} instance for the right type
and identifier.
+     *
+     * <div class="note"><b>Design note:</b>
+     * this field is declared in this {@code Dispatcher} class instead than {@link CachedStatement}
because we need
+     * it before a {@code CachedStatement} instance can be found. Furthermore two {@code
Dispatcher} instances may
+     * have different {@code preferredIndex} values even if their {@link CachedStatement#type}
value is the same,
+     * since their {@link #identifier} values are different.</div>
+     */
+    byte preferredIndex;
+
+    /**
+     * The metadata instance where to store the property (column) values, or {@code null}
if not yet created.
+     * For ISO 19115, this is an instance of one of the classes defined in {@link org.apache.sis.metadata.iso}
+     * package or sub-packages. The intend is not only to cache the property values, but
also to leverage
+     * implementations that compute automatically some property values from other properties.
+     * The main usage is computing the value of a deprecated property from the values of
non-deprecated ones,
+     * e.g. for transition from ISO 19115:2003 to ISO 19115:2014.
      */
-    int preferredIndex;
+    private transient volatile Object cache;
+
+    /**
+     * A bitmask of properties having null values. Cached for avoiding to query the database
many times.
+     * Bit indices are given by {@link LookupInfo#asIndexMap(MetadataStandard)}. If a metadata
contains
+     * more than 64 properties, no "null value" information will be stored for the extra
properties.
+     * No damage will happen except more database accesses than needed.
+     *
+     * <p>We do not need to synchronize this field because it is only an optimization.
It is okay if a bit
+     * is wrongly zero; the only consequence is that it will cause one more database access
than needed.</p>
+     */
+    private transient long nullValues;
 
     /**
      * Creates a new metadata handler.
@@ -104,27 +146,84 @@ final class Dispatcher implements Invoca
                 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;
+                if (n != 0 || !source.standard.isMetadata(type)) {
+                    throw new BackingStoreException(Errors.format(Errors.Keys.UnsupportedOperation_1,
+                                Classes.getShortName(type) + '.' + name));
+                }
+                break;
+            }
+        }
+        /*
+         * The invoked method is a method from the metadata interface.
+         * Consequently, the information should exist in the database.
+         * First, we will check the cache. If the value is not present, we will query the
database and
+         * fetch the cache again (because the class that implement the cache may perform
some computation).
+         */
+        Object value = null;
+        final LookupInfo info = source.getLookupInfo(type);
+        final long nullBit = 1L << info.asIndexMap(source.standard).get(method.getName());
    // Okay even if overflow.
+        /*
+         * The NULL_COLLECTION semaphore prevents creation of new empty collections by getter
methods
+         * (a consequence of lazy instantiation). The intend is to avoid creation of unnecessary
objects
+         * for all unused properties. Users should not see behavioral difference.
+         */
+        if ((nullValues & nullBit) == 0) try {
+            final boolean allowNull = Semaphores.queryAndSet(Semaphores.NULL_COLLECTION);
+            try {
+                Object cache = this.cache;
+                if (cache != null) {
+                    synchronized (cache) {
+                        value = method.invoke(cache);
+                    }
+                }
+                if (value == null) {
+                    info.setMetadataType(type);     // Precaution in case method.invoke(cache)
fetched other metadata.
+                    value = source.readColumn(info, method, this);
+                    if (value != null) {
+                        if (cache == null) {
+                            final Class<?> impl = source.standard.getImplementation(type);
+                            if (impl == null) {
+                                return value;
+                            }
+                            this.cache = cache = impl.newInstance();
+                            /*
+                             * We do not use AtomicReference because it is okay if the cache
is instantiated twice.
+                             * It would cause us to query the database twice, but we should
get the same information.
+                             */
+                        }
+                        final Map<String, Object> map = source.standard.asValueMap(cache,
type,
+                                    KeyNamePolicy.METHOD_NAME, ValueExistencePolicy.ALL);
+                        synchronized (cache) {
+                            value = map.putIfAbsent(method.getName(), value);
+                            if (value == null) {
+                                value = method.invoke(cache);
+                            }
                         }
                     }
-                    throw new BackingStoreException(Errors.format(Errors.Keys.DatabaseError_2,
returnType, identifier), e);
                 }
+            } finally {
+                if (!allowNull) {
+                    Semaphores.clear(Semaphores.NULL_COLLECTION);
+                }
+            }
+        } catch (ReflectiveOperationException | 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);
+        }
+        if (value == null) {
+            nullValues |= nullBit;
+            final Class<?> returnType = method.getReturnType();
+            if (Collection.class.isAssignableFrom(returnType)) {
+                value = CollectionsExt.empty(returnType);
             }
         }
-        throw new BackingStoreException(Errors.format(Errors.Keys.UnsupportedOperation_1,
type + "." + name));
+        return value;
     }
 
     /**

Added: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/LookupInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/LookupInfo.java?rev=1795682&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/LookupInfo.java
(added)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/LookupInfo.java
[UTF-8] Sat May 20 23:36:44 2017
@@ -0,0 +1,143 @@
+/*
+ * 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.util.Map;
+import org.apache.sis.util.ObjectConverter;
+import org.apache.sis.util.ObjectConverters;
+import org.apache.sis.util.UnconvertibleObjectException;
+import org.apache.sis.metadata.KeyNamePolicy;
+import org.apache.sis.metadata.MetadataStandard;
+
+
+/**
+ * Information about the last used metadata type. Those information are cached on the assumption
+ * that the same maps will be used more than once before to move to another metadata object.
+ *
+ * <p>Each thread shall have its own {@code LastUsedInfo} instance.
+ * Consequently there is no need for synchronization in this class.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ * @since   0.8
+ * @module
+ */
+final class LookupInfo {
+    /**
+     * The type of metadata objects for which the {@link #names} and {@link #indices} maps
are built.
+     * Must be the interface type when such interface exists. This is mapped to the table
name in the database.
+     */
+    private Class<?> type;
+
+    /**
+     * The last "method name to column name" map returned by {@link #asNameMap(MetadataStandard)}.
+     * Cached on assumption that the same map will be used more than once before to move
to another metadata object.
+     */
+    private Map<String,String> names;
+
+    /**
+     * The last used "method name to property indices" map returned by {@link #asIndexMap(MetadataStandard)}.
+     * Cached on assumption that the same map will be used more than once before to move
to another metadata object.
+     */
+    private Map<String,Integer> indices;
+
+    /**
+     * The last converter used. This field exists only for performance purposes, on
+     * the assumption that the last used converter has good chances to be used again.
+     */
+    private ObjectConverter<?,?> converter;
+
+    /**
+     * Creates a new cache.
+     */
+    LookupInfo() {
+    }
+
+    /**
+     * Returns the type of metadata objects for which {@link #asNameMap(MetadataStandard)}
or
+     * {@link #asIndexMap(MetadataStandard)} will return "name to name" or "name to index"
mappings.
+     */
+    final Class<?> getMetadataType() {
+        return type;
+    }
+
+    /**
+     * Sets the type of metadata objects for which {@link #asNameMap(MetadataStandard)} or
+     * {@link #asIndexMap(MetadataStandard)} will return "name to name" or "name to index"
mappings.
+     *
+     * @param  t  the type of metadata object for which to get column names or property indices.
+     */
+    final void setMetadataType(final Class<?> t) {
+        if (type != t) {
+            type    = t;
+            names   = null;
+            indices = null;
+        }
+    }
+
+    /**
+     * Maps method names to the name of columns in the database table corresponding to the
current
+     * {@linkplain #getMetadataType() metadata type}. The values in the returned map must
be the
+     * same than the keys in the map returned by {@link MetadataSource#asValueMap(Object)}.
+     *
+     * @throws ClassCastException if the metadata object type does not extend a metadata
interface
+     *         of the expected package.
+     */
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    final Map<String,String> asNameMap(final MetadataStandard standard) {
+        if (names == null) {
+            names = standard.asNameMap(type, KeyNamePolicy.METHOD_NAME, MetadataSource.NAME_POLICY);
+        }
+        return names;
+    }
+
+    /**
+     * Maps method names to property indices for the current {@linkplain #getMetadataType()
metadata type}.
+     *
+     * @return a map from method names to property indices.
+     * @throws ClassCastException if the metadata object type does not extend a metadata
interface
+     *         of the expected package.
+     */
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    final Map<String,Integer> asIndexMap(final MetadataStandard standard) {
+        if (indices == null) {
+            indices = standard.asIndexMap(type, KeyNamePolicy.METHOD_NAME);
+        }
+        return indices;
+    }
+
+    /**
+     * Converts the specified non-metadata value into an object of the expected type.
+     * The expected value is an instance of a class outside the metadata package, for example
+     * {@link String}, {@link org.opengis.util.InternationalString}, {@link java.net.URI},
<i>etc.</i>
+     *
+     * @throws UnconvertibleObjectException if the value can not be converter.
+     */
+    @SuppressWarnings({"unchecked","rawtypes"})
+    final Object convert(final Class<?> targetType, Object value) {
+        final Class<?> sourceType = value.getClass();
+        if (!targetType.isAssignableFrom(sourceType)) {
+            if (converter == null || !converter.getSourceClass().isAssignableFrom(sourceType)
||
+                                     !targetType.isAssignableFrom(converter.getTargetClass()))
+            {
+                converter = ObjectConverters.find(sourceType, targetType);
+            }
+            value = ((ObjectConverter) converter).apply(value);
+        }
+        return value;
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java?rev=1795682&r1=1795681&r2=1795682&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java
[UTF-8] Sat May 20 23:36:44 2017
@@ -19,6 +19,8 @@ package org.apache.sis.metadata.sql;
 
 /**
  * Interface for metadata that are implemented by a proxy class.
+ * Instances of this interface are created by calls to {@code Proxy.newProxyInstance(…)}
+ * in {@link MetadataSource#lookup(Class, String)}.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.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=1795682&r1=1795681&r2=1795682&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] Sat May 20 23:36:44 2017
@@ -65,8 +65,6 @@ import org.apache.sis.util.logging.Warni
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.util.ObjectConverter;
-import org.apache.sis.util.ObjectConverters;
 import org.apache.sis.util.UnconvertibleObjectException;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.Classes;
@@ -113,6 +111,12 @@ import org.apache.sis.util.iso.Types;
  */
 public class MetadataSource implements AutoCloseable {
     /**
+     * The policy for column names. We use UML identifiers (e.g. {@code "title"}) as defined
by the metadata standard
+     * (typically ISO 19115).
+     */
+    static final KeyNamePolicy NAME_POLICY = KeyNamePolicy.UML_IDENTIFIER;
+
+    /**
      * The column name used for the identifiers. We do not quote this identifier;
      * we will let the database uses its own lower-case / upper-case convention.
      */
@@ -258,28 +262,15 @@ public class MetadataSource implements A
     private final WeakValueHashMap<CacheKey,Object> pool;
 
     /**
-     * The last converter used. This field exists only for performance purposes, on
-     * the assumption that the last used converter has good chances to be used again.
-     *
-     * @see #getValue(Class, Method, Dispatcher)
-     */
-    private transient volatile ObjectConverter<?,?> lastConverter;
-
-    /**
-     * The last "method name to column name" map returned by {@link #asNameMap(Class)}.
-     * Cached on assumption that the same map will be used more than once before to move
to another metadata object.
+     * 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.
      */
-    private transient Map<String,String> lastNameMap;
-
-    /**
-     * The {@code type} argument in the last call to {@link #asNameMap(Class)}.
-     */
-    private transient Class<?> lastNameMapType;
+    private final ThreadLocal<LookupInfo> lastUsed;
 
     /**
      * Where to report the warnings. This is not necessarily a logger, since users can register
listeners.
      *
-     * @see #getValue(Class, Method, Dispatcher)
+     * @see #readColumn(LookupInfo, Method, Dispatcher)
      */
     private final WarningListeners<MetadataSource> listeners;
 
@@ -356,7 +347,7 @@ public class MetadataSource implements A
         ClassLoader classloader;
         Integer maxStatements;
 
-        catalog       = Containers.property(properties, "catalog",       String.class);;
+        catalog       = Containers.property(properties, "catalog",       String.class);
         classloader   = Containers.property(properties, "classloader",   ClassLoader.class);
         maxStatements = Containers.property(properties, "maxStatements", Integer.class);
         if (classloader == null) {
@@ -365,7 +356,7 @@ public class MetadataSource implements A
         if (maxStatements == null) {
             maxStatements = 10;               // Default value, may change in any future
Apache SIS version.
         } else {
-            ArgumentChecks.ensureBetween("maxStatements", 2, 100, maxStatements);       //
Arbitrary limits.
+            ArgumentChecks.ensureBetween("maxStatements", 2, 0xFF, maxStatements);   // Unsigned
byte range.
         }
         this.standard     = standard;
         this.dataSource   = dataSource;
@@ -376,6 +367,7 @@ public class MetadataSource implements A
         this.tableColumns = new HashMap<>();
         this.pool         = new WeakValueHashMap<>(CacheKey.class);
         this.listeners    = new WarningListeners<>(this);
+        this.lastUsed     = ThreadLocal.withInitial(LookupInfo::new);
     }
 
     /**
@@ -401,6 +393,7 @@ public class MetadataSource implements A
         tableColumns = new HashMap<>();
         classloader  = source.classloader;
         pool         = source.pool;
+        lastUsed     = source.lastUsed;
         listeners    = new WarningListeners<>(this, source.listeners);
     }
 
@@ -571,25 +564,6 @@ public class MetadataSource implements A
     }
 
     /**
-     * Maps method names to the name of columns in the table for the given metadata. The
values in the
-     * returned map must be the same than the keys in the map returned by {@link #asValueMap(Object)}.
-     *
-     * @param  type  the type of metadata object for which to get column names.
-     * @return a map from method names to column names.
-     * @throws ClassCastException if the metadata object type does not extend a metadata
interface
-     *         of the expected package.
-     */
-    @SuppressWarnings("ReturnOfCollectionOrArrayField")
-    private Map<String,String> asNameMap(final Class<?> type) throws ClassCastException
{
-        assert Thread.holdsLock(this);
-        if (type != lastNameMapType) {
-            lastNameMapType = type;
-            lastNameMap = standard.asNameMap(type, KeyNamePolicy.METHOD_NAME, KeyNamePolicy.UML_IDENTIFIER);
-        }
-        return lastNameMap;
-    }
-
-    /**
      * 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 {@code MetadataSource}
implementation
      * assumes so.
@@ -600,7 +574,7 @@ public class MetadataSource implements A
      *         of the expected package.
      */
     final Map<String,Object> asValueMap(final Object metadata) throws ClassCastException
{
-        return standard.asValueMap(metadata, null, KeyNamePolicy.UML_IDENTIFIER, ValueExistencePolicy.ALL);
+        return standard.asValueMap(metadata, null, NAME_POLICY, ValueExistencePolicy.ALL);
     }
 
     /**
@@ -830,22 +804,37 @@ public class MetadataSource implements A
     }
 
     /**
+     * Gets the {@link LookupInfo} instance for call to the {@link #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 {@link LookupInfo#setMetadataType(Class)} is invoked again).
+     *
+     * @param  type  the interface class. This is mapped to the table name in the database.
+     */
+    final LookupInfo getLookupInfo(final Class<?> type) {
+        final LookupInfo info = lastUsed.get();
+        info.setMetadataType(type);
+        return info;
+    }
+
+    /**
      * Invoked by {@link MetadataProxy} for fetching an attribute value from a table.
      *
-     * @param  type      the interface class. This is mapped to the table name in the database.
+     * @param  info      the interface type (together with cached information).
+     *                   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 was not found or can not be converted to
the expected type.
      */
-    final Object getValue(Class<?> type, final Method method, final Dispatcher toSearch)
+    final Object readColumn(final LookupInfo info, final Method method, final Dispatcher
toSearch)
             throws SQLException, MetadataStoreException
     {
         /*
          * If the identifier is prefixed with a table name as in "{CI_Organisation}identifier",
          * the name between bracket is a subtype of the given 'type' argument.
          */
+        Class<?> type = info.getMetadataType();
         if (toSearch.identifier.charAt(0) == TYPE_OPEN) {
             final int i = toSearch.identifier.indexOf(TYPE_CLOSE);
             if (i >= 0) {
@@ -860,11 +849,10 @@ public class MetadataSource implements A
         final Class<?> elementType    = wantCollection ? Classes.boundOfParameterizedProperty(method)
: returnType;
         final boolean  isMetadata     = standard.isMetadata(elementType);
         final String   tableName      = getTableName(type);
-        final String   columnName;
+        final String   columnName     = info.asNameMap(standard).get(method.getName());
         final boolean  isArray;
         Object value;
         synchronized (this) {
-            columnName = asNameMap(type).get(method.getName());
             if (!getExistingColumns(tableName).contains(columnName)) {
                 value   = null;
                 isArray = false;
@@ -874,7 +862,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.
                  */
-                CachedStatement result = take(type, toSearch.preferredIndex);
+                CachedStatement result = take(type, Byte.toUnsignedInt(toSearch.preferredIndex));
                 if (result == null) {
                     final SQLBuilder helper = helper();
                     final String query = helper.clear().append("SELECT * FROM ")
@@ -889,7 +877,7 @@ public class MetadataSource implements A
                     value = array.getArray();
                     array.free();
                 }
-                toSearch.preferredIndex = recycle(result, toSearch.preferredIndex);
+                toSearch.preferredIndex = (byte) recycle(result, Byte.toUnsignedInt(toSearch.preferredIndex));
             }
         }
         /*
@@ -904,7 +892,7 @@ public class MetadataSource implements A
                     if (isMetadata) {
                         element = lookup(elementType, element.toString());
                     } else try {
-                        element = convert(elementType, element);
+                        element = info.convert(elementType, element);
                     } catch (UnconvertibleObjectException e) {
                         throw new MetadataStoreException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3,
                                 columnName + '[' + i + ']', elementType, element.getClass()),
e);
@@ -929,7 +917,7 @@ public class MetadataSource implements A
             if (isMetadata) {
                 value = lookup(elementType, value.toString());
             } else try {
-                value = convert(elementType, value);
+                value = info.convert(elementType, value);
             } catch (UnconvertibleObjectException e) {
                 throw new MetadataStoreException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3,
                         columnName, elementType, value.getClass()), e);
@@ -944,28 +932,6 @@ public class MetadataSource implements A
         }
         return value;
     }
-
-    /**
-     * Converts the specified non-metadata value into an object of the expected type.
-     * The expected value is an instance of a class outside the metadata package, for example
-     * {@link String}, {@link org.opengis.util.InternationalString}, {@link java.net.URI},
<i>etc.</i>
-     *
-     * @throws UnconvertibleObjectException if the value can not be converter.
-     */
-    @SuppressWarnings({"unchecked","rawtypes"})
-    private Object convert(final Class<?> targetType, Object value) throws UnconvertibleObjectException
{
-        final Class<?> sourceType = value.getClass();
-        if (!targetType.isAssignableFrom(sourceType)) {
-            ObjectConverter converter = lastConverter;
-            if (converter == null || !converter.getSourceClass().isAssignableFrom(sourceType)
||
-                                     !targetType.isAssignableFrom(converter.getTargetClass()))
-            {
-                lastConverter = converter = ObjectConverters.find(sourceType, targetType);
-            }
-            value = converter.apply(value);
-        }
-        return value;
-    }
 
     /**
      * Returns the code of the given type and name. This method is defined for avoiding the
warning message

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java?rev=1795682&r1=1795681&r2=1795682&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
[UTF-8] Sat May 20 23:36:44 2017
@@ -286,8 +286,8 @@ public class MetadataWriter extends Meta
         for (final String column : asSingletons.keySet()) {
             if (!columns.contains(column)) {
                 if (colTypes == null) {
-                    colTypes  = standard.asTypeMap(implementationType, KeyNamePolicy.UML_IDENTIFIER,
TypeValuePolicy.ELEMENT_TYPE);
-                    colTables = standard.asTypeMap(implementationType, KeyNamePolicy.UML_IDENTIFIER,
TypeValuePolicy.DECLARING_INTERFACE);
+                    colTypes  = standard.asTypeMap(implementationType, NAME_POLICY, TypeValuePolicy.ELEMENT_TYPE);
+                    colTables = standard.asTypeMap(implementationType, NAME_POLICY, TypeValuePolicy.DECLARING_INTERFACE);
                 }
                 /*
                  * We have found a column to add. Check if the column actually needs to be
added to the parent table

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java?rev=1795682&r1=1795681&r2=1795682&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java
[UTF-8] Sat May 20 23:36:44 2017
@@ -33,13 +33,13 @@
  *   <li>{@link org.opengis.util.InternationalString} are stored only for the default
locale.</li>
  *   <li>Cyclic graph (<var>A</var> references <var>B</var>
which reference <var>A</var>) are not supported,
  *       unless foreigner key constraints are manually disabled for the columns which contain
the cyclic references.</li>
- *   <li>Metadata that are sub-interface of other metadata (for example
- *       {@link org.opengis.metadata.extent.GeographicDescription} which extends
- *       {@link org.opengis.metadata.extent.GeographicExtent}) can be stored only
- *       in databases supporting <cite>table inheritance</cite>, like
- *       <a href="http://www.postgresql.org">PostgreSQL</a>.</li>
  * </ul>
  *
+ * If the database supports <cite>table inheritance</cite> (like <a href="http://www.postgresql.org">PostgreSQL</a>),
+ * then this package will leverage that feature for the storage of metadata that are sub-interface
of other metadata
+ * (for example {@link org.opengis.metadata.extent.GeographicDescription} which extends
+ * {@link org.opengis.metadata.extent.GeographicExtent}).
+ *
  * @author  Touraïvane (IRD)
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 0.8



Mime
View raw message