sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1510624 - in /sis/branches/JDK7: core/sis-utility/src/main/java/org/apache/sis/util/ core/sis-utility/src/main/java/org/apache/sis/util/resources/ storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/ storage/sis-storage/src/main...
Date Mon, 05 Aug 2013 17:47:23 GMT
Author: desruisseaux
Date: Mon Aug  5 17:47:23 2013
New Revision: 1510624

URL: http://svn.apache.org/r1510624
Log:
Prefetch more bytes when the ByteBuffer doesn't contain enough bytes for allowing us to recognize
the format.
We try to prefetch more bytes only if necessary in order to avoid latency on network connections.

Added:
    sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/DripByteChannel.java
  (with props)
Modified:
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Static.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java
    sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java
    sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
    sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
    sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/ProbeResult.java
    sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
    sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Static.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Static.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Static.java [UTF-8]
(original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Static.java [UTF-8]
Mon Aug  5 17:47:23 2013
@@ -58,6 +58,8 @@ package org.apache.sis.util;
  * <tr><th colspan="2" class="hsep">Input / Output (including CRS, XML, images)</th></tr>
  * <tr><td>{@link org.apache.sis.io.IO}</td>
  *     <td>Methods working on {@link Appendable} instances.</td></tr>
+ * <tr><td>{@link org.apache.sis.storage.DataStores}</td>
+ *     <td>Read or write geospatial data in various backends.</td></tr>
  * <tr><td>{@link org.apache.sis.xml.XML}</td>
  *     <td>Marshal or unmarshal ISO 19115 objects.</td></tr>
  * <tr><td>{@link org.apache.sis.xml.Namespaces}</td>

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -99,6 +99,11 @@ public final class Errors extends Indexe
         public static final int CanNotParseFile_2 = 79;
 
         /**
+         * Can not read “{0}”.
+         */
+        public static final int CanNotRead_1 = 108;
+
+        /**
          * Can not set a value for property “{0}”.
          */
         public static final int CanNotSetPropertyValue_1 = 75;

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] Mon Aug  5 17:47:23 2013
@@ -31,6 +31,7 @@ CanNotComputeDerivative         = Can no
 CanNotInstantiate_1             = Can not instantiate an object of type \u2018{0}\u2019.
 CanNotOpen_1                    = Can not open \u201c{0}\u201d.
 CanNotParseFile_2               = Can not parse \u201c{1}\u201d as a file in the {0} format.
+CanNotRead_1                    = Can not read \u201c{0}\u201d.
 CanNotSetPropertyValue_1        = Can not set a value for property \u201c{0}\u201d.
 ClassNotFinal_1                 = Class \u2018{0}\u2019 is not final.
 CloneNotSupported_1             = Can not clone an object of type \u2018{0}\u2019.

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] Mon Aug  5 17:47:23 2013
@@ -21,6 +21,7 @@ CanNotComputeDerivative         = La d\u
 CanNotInstantiate_1             = Ne peut pas cr\u00e9er un objet de type \u2018{0}\u2019.
 CanNotOpen_1                    = Ne peut pas ouvrir \u201c{0}\u201d.
 CanNotParseFile_2               = Ne peut pas lire \u201c{1}\u201d comme un fichier au format
{0}.
+CanNotRead_1                    = Ne peut pas lire \u201c{0}\u201d.
 CanNotSetPropertyValue_1        = Ne peut pas d\u00e9finir une valeur pour la propri\u00e9t\u00e9
\u201c{0}\u201d.
 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.

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -116,7 +116,7 @@ public class NetcdfStoreProvider extends
         final ByteBuffer buffer = storage.getStorageAs(ByteBuffer.class);
         if (buffer != null) {
             if (buffer.remaining() < Integer.SIZE / Byte.SIZE) {
-                return ProbeResult.UNDETERMINED;
+                return ProbeResult.INSUFFICIENT_BYTES;
             }
             final int header = buffer.getInt(buffer.position());
             if ((header & 0xFFFFFF00) == ChannelDecoder.MAGIC_NUMBER) {
@@ -131,7 +131,7 @@ public class NetcdfStoreProvider extends
             ensureInitialized();
             final Method method = canOpenFromPath;
             if (method != null) try {
-                return ((Boolean) method.invoke(null, path)) ? ProbeResult.SUPPORTED : ProbeResult.UNKNOWN_FORMAT;
+                return ((Boolean) method.invoke(null, path)) ? ProbeResult.SUPPORTED : ProbeResult.UNSUPPORTED_STORAGE;
             } catch (IllegalAccessException e) {
                 throw new AssertionError(e); // Should never happen, since the method is
public.
             } catch (InvocationTargetException e) {
@@ -152,7 +152,7 @@ public class NetcdfStoreProvider extends
                 return ProbeResult.SUPPORTED;
             }
         }
-        return ProbeResult.UNKNOWN_FORMAT;
+        return ProbeResult.UNSUPPORTED_STORAGE;
     }
 
     /**

Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -59,7 +59,7 @@ import java.nio.channels.SeekableByteCha
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-3.07)
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public class ChannelDataInput {
@@ -150,6 +150,34 @@ public class ChannelDataInput {
     }
 
     /**
+     * Tries to read more bytes from the channel without changing the buffer position.
+     * This method returns a negative number if the buffer is already full or if the channel
reached the
+     * <cite>end of stream</cite>. Otherwise this method reads an arbitrary amount
of bytes not greater
+     * than the space available in the buffer, and returns the amount bytes actually read.
+     *
+     * @return The number of bytes read, or -2 if the buffer is full, or -1 on <cite>end
of stream</cite>.
+     * @throws IOException If an error occurred while reading the bytes.
+     *
+     * @since 0.4
+     */
+    public final int prefetch() throws IOException {
+        final int limit    = buffer.limit();
+        final int capacity = buffer.capacity();
+        if (limit == capacity) {
+            return -2;
+        }
+        final int position = buffer.position();
+        buffer.limit(capacity).position(limit);
+        int c = channel.read(buffer);
+        while (c == 0) {
+            onEmptyChannelBuffer();
+            c = channel.read(buffer);
+        }
+        buffer.limit(buffer.position()).position(position);
+        return c;
+    }
+
+    /**
      * Returns {@code true} if the buffer or the channel has at least one byte remaining.
      * If the {@linkplain #buffer} has no remaining bytes, then this method will attempts
      * to read at least one byte from the {@linkplain #channel}. If no bytes can be read
@@ -708,8 +736,9 @@ public class ChannelDataInput {
                     }
                     onEmptyChannelBuffer();
                 }
+                buffer.flip();
             } while (p > buffer.limit());
-            buffer.flip().position((int) p);
+            buffer.position((int) p);
         } else {
             /*
              * Requested position is before the current buffer limits

Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -67,7 +67,7 @@ public abstract class DataStoreProvider 
      * <ul>
      *   <li>{@link ProbeResult#SUPPORTED} if the {@code DataStore}s created by this
provider
      *       can open the given storage.</li>
-     *   <li>{@link ProbeResult#UNKNOWN_STORAGE} if the given storage does not appear
to be in a format
+     *   <li>{@link ProbeResult#UNSUPPORTED_STORAGE} if the given storage does not
appear to be in a format
      *       supported by this {@code DataStoreProvider}.</li>
      * </ul>
      *
@@ -90,18 +90,18 @@ public abstract class DataStoreProvider 
      *         if (buffer == null) {
      *             // If StorageConnector can not provide a ByteBuffer, then the storage
is
      *             // probably not a File, URL, URI, InputStream neither a ReadableChannel.
-     *             return ProbeResult.UNKNOWN_STORAGE;
+     *             return ProbeResult.UNSUPPORTED_STORAGE;
      *         }
      *         if (buffer.remaining() < Integer.SIZE / Byte.SIZE) {
      *             // If the buffer does not contain enough bytes for the integer type, this
is not
      *             // necessarily because the file is truncated. It may be because the data
were not
      *             // yet available at the time this method has been invoked.
-     *             return ProbeResult.UNDETERMINED;
+     *             return ProbeResult.INSUFFICIENT_BYTES;
      *         }
      *         if (buffer.getInt(buffer.position()) != MAGIC_NUMBER) {
      *             // We used ByteBuffer.getInt(int) instead than ByteBuffer.getInt() above
      *             // in order to keep the buffer position unchanged after this method call.
-     *             return ProbeResult.UNKNOWN_FORMAT;
+     *             return ProbeResult.UNSUPPORTED_STORAGE;
      *         }
      *         return ProbeResult.SUPPORTED;
      *     }

Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -16,6 +16,9 @@
  */
 package org.apache.sis.storage;
 
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Iterator;
 import java.util.ServiceLoader;
 import org.apache.sis.util.ThreadSafe;
 import org.apache.sis.util.ArgumentChecks;
@@ -89,22 +92,77 @@ final class DataStoreRegistry {
         } else {
             connector = new StorageConnector(storage);
         }
+        DataStoreProvider provider = null;
+        List<DataStoreProvider> deferred = null;
         try {
-            DataStoreProvider provider = null;
+            /*
+             * TODO: we may have thread contention here.
+             * We are waiting to see if this is a problem in practice.
+             */
             synchronized (loader) {
 search:         for (final DataStoreProvider candidate : loader) {
                     switch (candidate.canOpen(connector)) {
+                        /*
+                         * Stop at the first provider claiming to be able to read the storage.
+                         * We will exit the synchronized block before to open the storage.
+                         */
                         case SUPPORTED: {
                             provider = candidate;
+                            deferred = null;
                             break search;
                         }
+                        /*
+                         * If a provider doesn't have enough bytes for answering the question,
+                         * try again after this loop with more bytes in the buffer, unless
we
+                         * found an other provider.
+                         */
+                        case INSUFFICIENT_BYTES: {
+                            if (deferred == null) {
+                                deferred = new LinkedList<>();
+                            }
+                            deferred.add(candidate);
+                            break;
+                        }
+                        /*
+                         * If a provider doesn't know whether it can open the given storage,
+                         * we will try it only if we find no provider retuning SUPPORTED.
+                         *
+                         * TODO: What to do if we find more than one provider here? We can
not invoke
+                         *       provider.open(connector) in a try … catch block because
it may leave
+                         *       the StorageConnector in an invalid state in case of failure.
+                         */
                         case UNDETERMINED: {
-                            // TODO: not enough information.
+                            provider = candidate;
                             break;
                         }
                     }
                 }
             }
+            /*
+             * If any provider did not had enough bytes for answering the 'canOpen(…)'
question,
+             * get more bytes and try again. We try to prefetch more bytes only if we have
no choice
+             * in order to avoid latency on network connection.
+             */
+            if (deferred != null) {
+search:         while (!deferred.isEmpty() && connector.prefetch()) {
+                    for (final Iterator<DataStoreProvider> it=deferred.iterator();
it.hasNext();) {
+                        final DataStoreProvider candidate = it.next();
+                        switch (candidate.canOpen(connector)) {
+                            case SUPPORTED:          provider = candidate; break search;
+                            case UNDETERMINED:       provider = candidate; break;
+                            case INSUFFICIENT_BYTES: continue; // Will try again in next
iteration.
+                        }
+                        it.remove(); // UNSUPPORTED_* or UNDETERMINED: do not try again those
providers.
+                    }
+                }
+            }
+            /*
+             * If a provider has been found, or if a provider returned UNDETERMINED, use
that one
+             * for opening a DataStore. Note that if more than one provider returned UNDETERMINED,
+             * the selected one is arbitrary and may change in different execution. Implementors
+             * shall avoid the UNDETERMINED value as much as possible (this value should
be used
+             * only for RAW image format).
+             */
             if (provider != null) {
                 final DataStore data = provider.open(connector);
                 connector = null; // For preventing it to be closed.

Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/ProbeResult.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/ProbeResult.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/ProbeResult.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/ProbeResult.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -23,12 +23,11 @@ package org.apache.sis.storage;
  *
  * <ul>
  *   <li>{@link #SUPPORTED} indicates that the storage can be read and eventually written.</li>
- *   <li>{@link #UNDETERMINED} indicates that the provider does not have enough information
for telling
- *       whether the storage can be opened. SIS will try to use such provider last, if no
better suited
- *       provider is found.</li>
- *   <li>All other values indicate that the storage can not be opened. The actual enumeration
value gives
- *       the reason (e.g. {@linkplain #UNKNOWN_FORMAT unknown format}, or
- *       {@linkplain #UNSUPPORTED_VERSION unsupported version}).</li>
+ *   <li>{@code UNSUPPORTED_*} indicate that the storage can not be opened. The actual
enumeration value gives
+ *       the reason (e.g. unsupported format or {@linkplain #UNSUPPORTED_VERSION unsupported
version}).</li>
+ *   <li>{@link #INSUFFICIENT_BYTES} or {@link #UNDETERMINED} indicate that the provider
does not have enough
+ *       information for telling whether the storage can be opened. SIS will try to use such
provider last,
+ *       if no better suited provider is found.</li>
  * </ul>
  *
  * When a {@link DataStores#open DataStores.open(…)} method is invoked, SIS will iterate
over the list of known
@@ -52,34 +51,56 @@ public enum ProbeResult {
     SUPPORTED,
 
     /**
-     * The open capability can not be determined.
-     * This value may be returned in two kinds of situation:
+     * The {@code DataStoreProvider} does not recognize the given storage object, file format
or database schema.
+     * Examples:
      *
      * <ul>
-     *   <li>The method can not look ahead far enough in the file header,
-     *       for example because the buffer has not fetched enough bytes.</li>
-     *   <li>The {@code DataStore} could potentially open anything.
-     *       This is the case for example of the RAW image format.</li>
+     *   <li>The storage is a file while the provider expected a database connection
(or conversely).</li>
+     *   <li>The file does not contains the expected magic number.</li>
+     *   <li>The database schema does not contain the expected tables.</li>
      * </ul>
      */
-    UNDETERMINED,
+    UNSUPPORTED_STORAGE,
 
     /**
-     * The {@code DataStoreProvider} does not recognize the given storage object.
-     * For example the storage may be a file while the provider expected a database connection,
or conversely.
+     * The {@code DataStoreProvider} recognizes the given storage, but the data are structured
+     * according a file or schema version not yet supported by the current implementation.
      */
-    UNKNOWN_STORAGE,
+    UNSUPPORTED_VERSION,
 
     /**
-     * The {@code DataStoreProvider} does not recognize the file format or schema.
-     * For example the file does not contains the expected magic number,
-     * or the database schema does not contain the expected tables.
+     * The open capability can not be determined because the {@link ByteBuffer} contains
an insufficient
+     * amount of bytes. This value may be returned by {@link DataStoreProvider#canOpen(StorageConnector)}
+     * implementations similar to the following:
+     *
+     * {@preformat java
+     *     public ProbeResult canOpen(StorageConnector storage) throws DataStoreException
{
+     *         final ByteBuffer buffer = storage.getStorageAs(ByteBuffer.class);
+     *         if (buffer == null) {
+     *             return ProbeResult.UNSUPPORTED_STORAGE;
+     *         }
+     *         if (buffer.remaining() < Integer.SIZE / Byte.SIZE) {
+     *             return ProbeResult.INSUFFICIENT_BYTES;
+     *         }
+     *         // Other verifications here.
+     *     }
+     * }
+     *
+     * When some {@code DataStoreProvider} return this value, SIS will first continue the
search for a provider that
+     * can answer the {@code canOpen(…)} question using only the available bytes. Only
if no provider is found,
+     * then SIS will fetch more bytes and try again the providers that returned {@code INSUFFICIENT_BYTES}.
+     * SIS tries to work with available bytes before to ask more in order to reduce latencies
on network connections.
      */
-    UNKNOWN_FORMAT,
+    INSUFFICIENT_BYTES,
 
     /**
-     * The {@code DataStoreProvider} recognizes the given storage, but the data are structured
-     * according a file or schema version not yet supported by the current implementation.
+     * The open capability can not be determined.
+     * This value may be returned by {@code DataStore} implementations that could potentially
open anything,
+     * as for example of the RAW image format.
+     *
+     * <p><strong>This is a last resort value!</strong> {@code canOpen(…)}
implementations are strongly encouraged
+     * to return a more accurate enumeration value for allowing {@link DataStores#open(Object)}
to perform a better
+     * choice. Generally, this value should be used only by the RAW image format.</p>
      */
-    UNSUPPORTED_VERSION
+    UNDETERMINED
 }

Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -31,7 +31,6 @@ import java.sql.Connection;
 import javax.sql.DataSource;
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.Classes;
-import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.storage.IOUtilities;
@@ -82,6 +81,12 @@ public class StorageConnector implements
     static final int DEFAULT_BUFFER_SIZE = 4096;
 
     /**
+     * The minimal size of the {@link ByteBuffer} to be created. This size is used only
+     * for temporary buffers that are unlikely to be used for the actual reading process.
+     */
+    private static final int MINIMAL_BUFFER_SIZE = 256;
+
+    /**
      * The input/output object given at construction time.
      *
      * @see #getStorage()
@@ -364,6 +369,7 @@ public class StorageConnector implements
 
     /**
      * Creates a view for the input as a {@link ChannelDataInput} if possible.
+     * If the view can not be created, remember that fact in order to avoid new attempts.
      *
      * @param  asImageInputStream If the {@code ChannelDataInput} needs to be {@link ChannelImageInputStream}
subclass.
      * @throws IOException If an error occurred while opening a channel for the input.
@@ -426,14 +432,18 @@ public class StorageConnector implements
     }
 
     /**
-     * If an {@link ImageInputStream} has been created without buffer, read an arbitrary
amount of bytes now so
-     * we can provide a {@link ByteBuffer} for users who want it. We read only a small amount
of bytes because,
-     * at the contrary of the buffer created in {@link #createChannelAndBuffer(boolean)},
the buffer created
-     * here is unlikely to be used for the reading process after the recognition of the file
format.
+     * Creates a {@link ByteBuffer} from the {@link ChannelDataInput} if possible, or from
the
+     * {@link ImageInputStream} otherwise. The buffer will be initialized with an arbitrary
amount
+     * of bytes read from the input. This amount is not sufficient, it can be increased by
a call
+     * to {@link #prefetch()}.
      *
      * @throws IOException If an error occurred while opening a stream for the input.
      */
     private void createByteBuffer() throws IOException, DataStoreException {
+        /*
+         * First, try to create the ChannelDataInput if it does not already exists.
+         * If successful, this will create a ByteBuffer companion as a side effect.
+         */
         if (!views.containsKey(ChannelDataInput.class)) {
             createChannelDataInput(false);
         }
@@ -442,15 +452,22 @@ public class StorageConnector implements
         if (c != null) {
             asByteBuffer = c.buffer.asReadOnlyBuffer();
         } else {
+            /*
+             * If no ChannelDataInput has been create by the above code, get the input as
an ImageInputStream and
+             * read an arbitrary amount of bytes. Read only a small amount of bytes because,
at the contrary of the
+             * buffer created in createChannelDataInput(boolean), the buffer created here
is unlikely to be used for
+             * the reading process after the recognition of the file format.
+             */
             final ImageInputStream in = getStorageAs(ImageInputStream.class);
             if (in != null) {
                 in.mark();
-                final byte[] buffer = new byte[256];
+                final byte[] buffer = new byte[MINIMAL_BUFFER_SIZE];
                 final int n = in.read(buffer);
                 in.reset();
                 if (n >= 1) {
-                    asByteBuffer = ByteBuffer.wrap(ArraysExt.resize(buffer, n))
-                            .asReadOnlyBuffer().order(in.getByteOrder());
+                    asByteBuffer = ByteBuffer.wrap(buffer).order(in.getByteOrder());
+                    asByteBuffer.limit(n);
+                    // Can't invoke asReadOnly() because 'prefetch()' need to be able to
write in it.
                 }
             }
         }
@@ -458,6 +475,46 @@ public class StorageConnector implements
     }
 
     /**
+     * Transfers more bytes from the {@link DataInput} to the {@link ByteBuffer}, if possible.
+     * This method returns {@code true} on success, or {@code false} if input is not a readable
+     * channel or stream, we have reached the end of stream, or the buffer is full.
+     *
+     * <p>This method is invoked when the amount of bytes in the buffer appears to
be insufficient
+     * for {@link DataStoreProvider#canOpen(StorageConnector)} purpose.</p>
+     *
+     * @return {@code true} on success.
+     * @throws DataStoreException If an error occurred while reading more bytes.
+     */
+    final boolean prefetch() throws DataStoreException {
+        try {
+            final ChannelDataInput c = getView(ChannelDataInput.class);
+            if (c != null) {
+                return c.prefetch() >= 0;
+            }
+            /*
+             * The above code is the usual case. The code below this point is the fallback
used when only
+             * an ImageInputStream was available. In such case, the ByteBuffer can only be
the one created
+             * by the above createByteBuffer() method, which is known to be backed by a writable
array.
+             */
+            final ImageInputStream input = getView(ImageInputStream.class);
+            if (input != null) {
+                final ByteBuffer buffer = getView(ByteBuffer.class);
+                if (buffer != null) {
+                    final int p = buffer.limit();
+                    final int n = input.read(buffer.array(), p, buffer.capacity() - p);
+                    if (n >= 0) {
+                        buffer.limit(p + n);
+                        return true;
+                    }
+                }
+            }
+        } catch (IOException e) {
+            throw new DataStoreException(Errors.format(Errors.Keys.CanNotRead_1, getStorageName()),
e);
+        }
+        return false;
+    }
+
+    /**
      * Creates a storage view of the given type if possible, or returns {@code null} otherwise.
      * This method is invoked by {@link #getStorageAs(Class)} when first needed, and the
result is cached.
      *

Modified: sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java?rev=1510624&r1=1510623&r2=1510624&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -22,7 +22,6 @@ import java.io.DataInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.nio.channels.Channels;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -36,7 +35,7 @@ import static org.junit.Assert.*;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-3.07)
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public final strictfp class ChannelDataInputTest extends TestCase {
@@ -46,6 +45,20 @@ public final strictfp class ChannelDataI
     private static final int ARRAY_MAX_SIZE = 256;
 
     /**
+     * Creates an array filled with random values.
+     *
+     * @param length The length of the array to create.
+     * @param random The random number generator to use.
+     */
+    static byte[] createRandomArray(final int length, final Random random) {
+        final byte[] array = new byte[length];
+        for (int i=0; i<length; i++) {
+            array[i] = (byte) random.nextInt(256);
+        }
+        return array;
+    }
+
+    /**
      * Fills a buffer with random data and compare the result with a standard image input
stream.
      * We allocate a small buffer for the {@code ChannelDataInput} in order to force frequent
      * interactions between the buffer and the channel.
@@ -59,7 +72,7 @@ public final strictfp class ChannelDataI
         compareStreamToBuffer(random, array.length,
                 new DataInputStream(new ByteArrayInputStream(array)),
                 new ChannelDataInput("testAllReadMethods",
-                    Channels.newChannel(new ByteArrayInputStream(array)),
+                    new DripByteChannel(array, random, 1, 1024),
                     ByteBuffer.allocate(random.nextInt(ARRAY_MAX_SIZE / 4) + (Double.SIZE
/ Byte.SIZE)), false));
     }
 
@@ -149,11 +162,12 @@ public final strictfp class ChannelDataI
      */
     @Test
     public void testReadString() throws IOException {
+        final Random random   = TestUtilities.createRandomNumberGenerator("testReadString");
         final String expected = "お元気ですか";
-        final byte[] array = expected.getBytes("UTF-8");
+        final byte[] array    = expected.getBytes("UTF-8");
         assertEquals(expected.length()*3, array.length); // Sanity check.
         final ChannelDataInput input = new ChannelDataInput("testReadString",
-                Channels.newChannel(new ByteArrayInputStream(array)),
+                new DripByteChannel(array, random, 1, 32),
                 ByteBuffer.allocate(array.length + 4), false);
         assertEquals(expected, input.readString(array.length, "UTF-8"));
         assertFalse(input.buffer.hasRemaining());
@@ -173,7 +187,7 @@ public final strictfp class ChannelDataI
         length -= (Long.SIZE / Byte.SIZE); // Safety against buffer underflow.
         final ByteBuffer buffer = ByteBuffer.wrap(array);
         final ChannelDataInput input = new ChannelDataInput("testSeekOnForwardOnlyChannel",
-                Channels.newChannel(new ByteArrayInputStream(array)),
+                new DripByteChannel(array, random, 1, 2048),
                 ByteBuffer.allocate(random.nextInt(64) + 16), false);
         int position = 0;
         while (position < length) {
@@ -185,16 +199,46 @@ public final strictfp class ChannelDataI
     }
 
     /**
-     * Creates an array filled with random values.
+     * Tests {@link ChannelDataInput#prefetch()}.
      *
-     * @param length The length of the array to create.
-     * @param random The random number generator to use.
+     * @throws IOException Should never happen.
      */
-    static byte[] createRandomArray(final int length, final Random random) {
-        final byte[] array = new byte[length];
-        for (int i=0; i<length; i++) {
-            array[i] = (byte) random.nextInt(256);
+    @Test
+    public void testPrefetch() throws IOException {
+        final Random     random = TestUtilities.createRandomNumberGenerator("testPrefetch");
+        final int        length = random.nextInt(256) + 128;
+        final byte[]     array  = createRandomArray(length, random);
+        final ByteBuffer buffer = ByteBuffer.allocate(random.nextInt(64) + 16);
+        final ChannelDataInput input = new ChannelDataInput("testPrefetch",
+                new DripByteChannel(array, random, 1, 64), buffer, false);
+        int position = 0;
+        while (position != length) {
+            if (random.nextBoolean()) {
+                assertEquals(array[position++], input.readByte());
+            }
+            /*
+             * Prefetch a random amount of bytes and verifies the buffer status.
+             */
+            final int p = buffer.position();
+            final int m = buffer.limit();
+            final int n = input.prefetch();
+            assertEquals("Position shall be unchanged.", p, buffer.position());
+            final int limit = buffer.limit();
+            if (n >= 0) {
+                // Usual case.
+                assertTrue("Limit shall be increased.", limit > m);
+            } else {
+                // Buffer is full or channel reached the end of stream.
+                assertEquals("Limit shall be unchanged", m, limit);
+            }
+            /*
+             * Compare the buffer content with the original data array. The comparison starts
+             * from the buffer begining, in order to ensure that previous data are unchanged.
+             */
+            final int offset = position - buffer.position();
+            for (int i=0; i<limit; i++) {
+                assertEquals(array[offset + i], buffer.get(i));
+            }
         }
-        return array;
     }
 }

Added: sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/DripByteChannel.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/DripByteChannel.java?rev=1510624&view=auto
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/DripByteChannel.java
(added)
+++ sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/DripByteChannel.java
[UTF-8] Mon Aug  5 17:47:23 2013
@@ -0,0 +1,114 @@
+/*
+ * 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.internal.storage;
+
+import java.util.Random;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.ClosedChannelException;
+
+import static java.lang.StrictMath.min;
+
+
+/**
+ * A {@link ReadableByteChannel} with a {@code read} methods that do not return all available
bytes.
+ * This class is used for simulating a socket connection where some data may not be immediately
available
+ * from the socket's input buffer.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.4
+ * @version 0.4
+ * @module
+ */
+public final strictfp class DripByteChannel implements ReadableByteChannel {
+    /**
+     * The data to provide.
+     */
+    private final byte[] data;
+
+    /**
+     * The random number generator to use for determining how many bytes to return.
+     */
+    private final Random random;
+
+    /**
+     * Minimal (inclusive) and maximal (exclusive) amount of bytes to read.
+     */
+    private final int lower, upper;
+
+    /**
+     * Current position in the data array.
+     */
+    private int position;
+
+    /**
+     * Sets to {@code true} after {@link #close()} has been invoked.
+     */
+    private boolean isClosed;
+
+    /**
+     * Creates a new {@code DripByteChannel} wrapping the given data.
+     *
+     * @param data    The data to provide.
+     * @param random  The random number generator to use for determining how many bytes to
return.
+     * @param lower   Minimal amount of bytes to read, inclusive.
+     * @param upper   Maximal amount of bytes to read, exclusive.
+     */
+    public DripByteChannel(final byte[] data, final Random random, final int lower, final
int upper) {
+        this.data    = data;
+        this.random  = random;
+        this.lower   = lower;
+        this.upper   = upper;
+    }
+
+    /**
+     * Reads a random number of bytes from the data array.
+     *
+     * @param buffer The buffer where to copy the bytes.
+     */
+    @Override
+    public int read(final ByteBuffer buffer) throws IOException {
+        if (isClosed) {
+            throw new ClosedChannelException();
+        }
+        final int remaining = data.length - position;
+        if (remaining == 0) {
+            return -1;
+        }
+        final int n = min(random.nextInt(upper - lower) + lower, min(remaining, buffer.remaining()));
+        buffer.put(data, position, n);
+        position += n;
+        return n;
+    }
+
+    /**
+     * Returns {@code true} if the channel has not yet been closed.
+     */
+    @Override
+    public boolean isOpen() {
+        return !isClosed;
+    }
+
+    /**
+     * Closes the channel.
+     */
+    @Override
+    public void close() {
+        isClosed = true;
+    }
+}

Propchange: sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/DripByteChannel.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/DripByteChannel.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8



Mime
View raw message