sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1777533 - in /sis/branches/JDK8/storage: sis-storage/src/main/java/org/apache/sis/storage/ sis-xmlstore/src/main/java/org/apache/sis/internal/xml/ sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/
Date Fri, 06 Jan 2017 06:00:16 GMT
Author: desruisseaux
Date: Fri Jan  6 06:00:15 2017
New Revision: 1777533

URL: http://svn.apache.org/viewvc?rev=1777533&view=rev
Log:
Tests multiple readings from the same XML data store.

Added:
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java
      - copied, changed from r1777268, sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedStorageException.java
Modified:
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamIO.java
    sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java

Copied: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java
(from r1777268, sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedStorageException.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java?p2=sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java&p1=sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedStorageException.java&r1=1777268&r2=1777533&rev=1777533&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedStorageException.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java
[UTF-8] Fri Jan  6 06:00:15 2017
@@ -17,32 +17,33 @@
 package org.apache.sis.storage;
 
 import java.util.Locale;
-import java.nio.file.OpenOption;
-import org.apache.sis.util.Classes;
 import org.apache.sis.internal.storage.Resources;
-import org.apache.sis.internal.storage.IOUtilities;
 
 
 /**
- * Thrown when no {@link DataStoreProvider} is found for a given storage object.
- * May also be thrown if a {@code DataStore} is instantiated directly (without {@code DataStoreProvider}
- * for verifying the input) but the data store can not handle the given input or output object.
+ * Thrown when an operation would require to move the cursor back, but the underlying storage
does not allow that.
+ * For example this exception is thrown if the user wants to read the same data a second
time, but the underlying
+ * {@linkplain java.nio.channels.ReadableByteChannel} is not
+ * {@linkplain java.nio.channels.SeekableByteChannel seekable}.
+ *
+ * <p>This exception typically does not depend on the {@link DataStore} implementation,
but rather on the
+ * {@link StorageConnector} value given to the data store.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @since   0.4
+ * @since   0.8
  * @version 0.8
  * @module
  */
-public class UnsupportedStorageException extends DataStoreException {
+public class ForwardOnlyStorageException extends DataStoreException {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = -8754573140979570187L;
+    private static final long serialVersionUID = 5750925701319201321L;
 
     /**
      * Creates an exception with no cause and no details message.
      */
-    public UnsupportedStorageException() {
+    public ForwardOnlyStorageException() {
         super();
     }
 
@@ -51,57 +52,16 @@ public class UnsupportedStorageException
      *
      * @param message  the detail message.
      */
-    public UnsupportedStorageException(final String message) {
+    public ForwardOnlyStorageException(final String message) {
         super(message);
     }
 
     /**
-     * Creates an exception with the specified cause and no details message.
-     *
-     * @param cause  the cause for this exception.
-     */
-    public UnsupportedStorageException(final Throwable cause) {
-        super(cause);
-    }
-
-    /**
-     * Creates an exception with the specified details message and cause.
-     *
-     * @param message  the detail message.
-     * @param cause    the cause for this exception.
-     */
-    public UnsupportedStorageException(final String message, final Throwable cause) {
-        super(message, cause);
-    }
-
-    /**
-     * Creates a new exception which will format a localized message in the given locale.
-     *
-     * @param locale      the locale for the message to be returned by {@link #getLocalizedMessage()}.
-     * @param key         one of {@link Resources.Keys} constants.
-     * @param parameters  parameters to use for formatting the messages.
-     */
-    UnsupportedStorageException(final Locale locale, final short key, final Object... parameters)
{
-        super(locale, key, parameters);
-    }
-
-    /**
-     * Creates a localized exception for an invalid input or output object given to a data
store.
-     * Arguments given to this constructor are hints for building an error message.
+     * Creates a localized exception with a default message saying that the stream is read-once.
      *
      * @param locale   the locale of the message to be returned by {@link #getLocalizedMessage()},
or {@code null}.
-     * @param format   short name or abbreviation of the data format (e.g. "CSV", "GML",
"WKT", <i>etc</i>).
-     * @param storage  the invalid input or output object. This is typically {@link StorageConnector#getStorage()}.
-     * @param options  the option used for opening the file, or {@code null} or empty if
unknown.
-     *                 This method looks in particular for {@link java.nio.file.StandardOpenOption#READ}
and
-     *                 {@code WRITE} options for inferring if the data store was to be used
as a reader or as a writer.
-     *                 Those options can be obtained by {@code StorageConnector.getOption(OptionKey.OPEN_OPTIONS)}.
-     *
-     * @since 0.8
      */
-    public UnsupportedStorageException(final Locale locale, final String format, final Object
storage, final OpenOption... options) {
-        super(locale, IOUtilities.isWrite(options) ? Resources.Keys.IllegalOutputTypeForWriter_2
-                                                   : Resources.Keys.IllegalInputTypeForReader_2,
-                      format, Classes.getClass(storage));
+    public ForwardOnlyStorageException(final Locale locale) {
+        super(locale, Resources.Keys.StreamIsReadOnce);
     }
 }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java?rev=1777533&r1=1777532&r2=1777533&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java
[UTF-8] Fri Jan  6 06:00:15 2017
@@ -42,6 +42,7 @@ import org.apache.sis.internal.storage.F
 import org.apache.sis.internal.storage.Markable;
 import org.apache.sis.internal.util.AbstractMap;
 import org.apache.sis.storage.DataStoreClosedException;
+import org.apache.sis.storage.ForwardOnlyStorageException;
 import org.apache.sis.storage.UnsupportedStorageException;
 import org.apache.sis.util.logging.WarningListener;
 
@@ -204,6 +205,7 @@ public abstract class StaxDataStore exte
         if (stream == null && storage instanceof AutoCloseable) {
             stream = (AutoCloseable) storage;
         }
+        channelFactory = connector.getStorageAs(ChannelFactory.class);  // Must be last before
'closeAllExcept'.
         connector.closeAllExcept(stream);
         /*
          * If possible, remember the position where data begin in the stream in order to
allow reading
@@ -221,7 +223,6 @@ public abstract class StaxDataStore exte
         } else {
             streamPosition = -1;
         }
-        channelFactory = connector.getStorageAs(ChannelFactory.class);              // Must
be last.
     }
 
     /**
@@ -369,32 +370,34 @@ public abstract class StaxDataStore exte
          * by InputType, then maybe that storage was a Path, File or URL, in which case the
constructor
          * should have opened an InputStream for it. If not, then this was an unsupported
storage type.
          */
+        AutoCloseable opened = stream;
         InputType type = storageToReader;
         if (type == null) {
-            type = InputType.STREAM;
-            if ((input = stream) == null) {
-                throw new UnsupportedStorageException(getLocale(), getFormatName(), storage,
StandardOpenOption.READ);
+            if (opened == null) {
+                throw new UnsupportedStorageException(getLocale(), getFormatName(), input,
StandardOpenOption.READ);
             }
+            input = opened;
+            type  = InputType.STREAM;
         }
         /*
          * If the stream has already been used by a previous read operation, then we need
to rewind
          * it to the start position determined at construction time. It the stream does not
support
          * mark, then we can not re-read the data.
          */
-reset:  switch (state) {
-            default: {
-                throw new AssertionError(state);
-            }
+        switch (state) {
+            default: throw new AssertionError(state);
             case READY: break;                  // Stream already at the data start; nothing
to do.
             case FINISHED: {
                 if (streamPosition >= 0) {
-                    final Markable m = (Markable) input;
+                    final Markable m = (Markable) opened;
                     long p;
-                    while ((p = m.getStreamPosition()) >= streamPosition) {
-                        if (p == streamPosition) {
-                            break reset;
-                        }
+                    do {
                         m.reset();
+                        p = m.getStreamPosition();
+                    } while (p > streamPosition);
+                    if (p == streamPosition) {
+                        m.mark();
+                        break;
                     }
                 }
                 // Failed to reset the stream - fallthrough.
@@ -402,21 +405,20 @@ reset:  switch (state) {
             /*
              * If the input stream is in use, or if we finished to use it but were unable
to reset its position,
              * then we need to create a new input stream (except if the input was a DOM in
memory, which we can
-             * share).
+             * share). The 'target' StaxStreamReader will be in charge of closing that stream.
              */
             case IN_USE: {
                 if (type == InputType.NODE) break;
                 if (channelFactory == null) {
-                    throw new DataStoreException("Can not read twice.");
+                    throw new ForwardOnlyStorageException(getLocale());
                 }
-                final InputStream in = channelFactory.inputStream();
-                final XMLStreamReader reader = InputType.STREAM.create(this, in);
-                target.stream = in;
-                return reader;
+                input = opened = channelFactory.inputStream();
+                type  = InputType.STREAM;
+                break;
             }
         }
         final XMLStreamReader reader = type.create(this, input);
-        target.stream = stream;
+        target.stream = opened;
         state = IN_USE;
         return reader;
     }
@@ -464,7 +466,7 @@ reset:  switch (state) {
      * @return whether the caller should invoke {@code finished.close()}.
      */
     final synchronized boolean canClose(final AutoCloseable finished) {
-        if (finished != null && stream == finished) {
+        if (finished == stream) {
             state = FINISHED;
             return false;
         }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamIO.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamIO.java?rev=1777533&r1=1777532&r2=1777533&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamIO.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamIO.java
[UTF-8] Fri Jan  6 06:00:15 2017
@@ -98,7 +98,7 @@ abstract class StaxStreamIO implements A
     public void close() throws Exception {
         final AutoCloseable s = stream;
         stream = null;
-        if (owner.canClose(s)) {
+        if (s != null && owner.canClose(s)) {
             s.close();
         }
     }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java?rev=1777533&r1=1777532&r2=1777533&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java
[UTF-8] Fri Jan  6 06:00:15 2017
@@ -39,6 +39,7 @@ import static org.apache.sis.test.TestUt
 import static org.apache.sis.test.TestUtilities.getSingleton;
 
 // Branch-dependent imports
+import java.util.stream.Stream;
 import org.opengis.feature.Feature;
 import org.opengis.metadata.content.FeatureTypeInfo;
 
@@ -213,7 +214,7 @@ public final strictfp class ReaderTest e
         assertStringEquals("Route",    it.next().getFeatureTypeName().tip());
         assertStringEquals("Track",    it.next().getFeatureTypeName().tip());
         assertStringEquals("WayPoint", it.next().getFeatureTypeName().tip());
-        assertFalse(it.hasNext());
+        assertFalse("hasNext", it.hasNext());
     }
 
     /**
@@ -227,11 +228,13 @@ public final strictfp class ReaderTest e
         try (final Store reader = create("1.0/waypoint.xml")) {
             verifyAlmostEmptyMetadata((Metadata) reader.getMetadata());
             assertEquals("version", Store.V1_0, reader.getVersion());
-            final Iterator<Feature> it = reader.getFeatures().iterator();
-            verifyPoint(it.next(), 0, false);
-            verifyPoint(it.next(), 1, false);
-            verifyPoint(it.next(), 2, false);
-            assertFalse(it.hasNext());
+            try (final Stream<Feature> features = reader.getFeatures()) {
+                final Iterator<Feature> it = features.iterator();
+                verifyPoint(it.next(), 0, false);
+                verifyPoint(it.next(), 1, false);
+                verifyPoint(it.next(), 2, false);
+                assertFalse("hasNext", it.hasNext());
+            }
         }
     }
 
@@ -246,11 +249,13 @@ public final strictfp class ReaderTest e
         try (final Store reader = create("1.1/waypoint.xml")) {
             verifyAlmostEmptyMetadata((Metadata) reader.getMetadata());
             assertEquals("version", Store.V1_1, reader.getVersion());
-            final Iterator<Feature> it = reader.getFeatures().iterator();
-            verifyPoint(it.next(), 0, true);
-            verifyPoint(it.next(), 1, true);
-            verifyPoint(it.next(), 2, true);
-            assertFalse(it.hasNext());
+            try (final Stream<Feature> features = reader.getFeatures()) {
+                final Iterator<Feature> it = features.iterator();
+                verifyPoint(it.next(), 0, true);
+                verifyPoint(it.next(), 1, true);
+                verifyPoint(it.next(), 2, true);
+                assertFalse("hasNext", it.hasNext());
+            }
         }
     }
 
@@ -265,10 +270,12 @@ public final strictfp class ReaderTest e
         try (final Store reader = create("1.0/route.xml")) {
             verifyAlmostEmptyMetadata((Metadata) reader.getMetadata());
             assertEquals("version", Store.V1_0, reader.getVersion());
-            final Iterator<Feature> it = reader.getFeatures().iterator();
-            verifyRoute(it.next(), false, 1);
-            verifyEmpty(it.next(), "rtept");
-            assertFalse(it.hasNext());
+            try (final Stream<Feature> features = reader.getFeatures()) {
+                final Iterator<Feature> it = features.iterator();
+                verifyRoute(it.next(), false, 1);
+                verifyEmpty(it.next(), "rtept");
+                assertFalse("hasNext", it.hasNext());
+            }
         }
     }
 
@@ -283,10 +290,20 @@ public final strictfp class ReaderTest e
         try (final Store reader = create("1.1/route.xml")) {
             verifyAlmostEmptyMetadata((Metadata) reader.getMetadata());
             assertEquals("version", Store.V1_1, reader.getVersion());
-            final Iterator<Feature> it = reader.getFeatures().iterator();
+            verifyRoute110(reader);
+        }
+    }
+
+    /**
+     * Verifies the routes of GPX {@code "1.1/route.xml"} test file.
+     * This verification is shared by {@link #testRoute110()} and {@link #testSequentialReads()}.
+     */
+    private static void verifyRoute110(final Store reader) throws DataStoreException {
+        try (final Stream<Feature> features = reader.getFeatures()) {
+            final Iterator<Feature> it = features.iterator();
             verifyRoute(it.next(), true, 3);
             verifyEmpty(it.next(), "rtept");
-            assertFalse(it.hasNext());
+            assertFalse("hasNext", it.hasNext());
         }
     }
 
@@ -360,10 +377,12 @@ public final strictfp class ReaderTest e
         try (final Store reader = create("1.0/track.xml")) {
             verifyAlmostEmptyMetadata((Metadata) reader.getMetadata());
             assertEquals("version", Store.V1_0, reader.getVersion());
-            final Iterator<Feature> it = reader.getFeatures().iterator();
-            verifyTrack(it.next(), false, 1);
-            verifyEmpty(it.next(), "trkseg");
-            assertFalse(it.hasNext());
+            try (final Stream<Feature> features = reader.getFeatures()) {
+                final Iterator<Feature> it = features.iterator();
+                verifyTrack(it.next(), false, 1);
+                verifyEmpty(it.next(), "trkseg");
+                assertFalse("hasNext", it.hasNext());
+            }
         }
     }
 
@@ -378,10 +397,12 @@ public final strictfp class ReaderTest e
         try (final Store reader = create("1.1/track.xml")) {
             verifyAlmostEmptyMetadata((Metadata) reader.getMetadata());
             assertEquals("version", Store.V1_1, reader.getVersion());
-            final Iterator<Feature> it = reader.getFeatures().iterator();
-            verifyTrack(it.next(), true, 3);
-            verifyEmpty(it.next(), "trkseg");
-            assertFalse(it.hasNext());
+            try (final Stream<Feature> features = reader.getFeatures()) {
+                final Iterator<Feature> it = features.iterator();
+                verifyTrack(it.next(), true, 3);
+                verifyEmpty(it.next(), "trkseg");
+                assertFalse("hasNext", it.hasNext());
+            }
         }
     }
 
@@ -529,4 +550,72 @@ public final strictfp class ReaderTest e
             }
         }
     }
+
+    /**
+     * Creates a data store for the {@code "1.1/route.xml"} test files using its URL instead
than the input stream.
+     * Using the URL makes easier for the data store to read the same data more than once.
+     */
+    private static Store createFromURL() throws DataStoreException {
+        return new Store(provider, new StorageConnector(ReaderTest.class.getResource("1.1/route.xml")));
+    }
+
+    /**
+     * Tests reading the same data more than once. For this test, the XML resource needs
to be obtained
+     * as a URL instead than as an input stream.  But Apache SIS implementation will still
tries to use
+     * mark/reset instead than re-opening a new stream if it can.
+     *
+     * @throws DataStoreException if reader failed to be created or failed at reading.
+     */
+    @Test
+    @DependsOnMethod("testRoute110")
+    public void testSequentialReads() throws DataStoreException {
+        final Metadata md;
+        try (final Store reader = createFromURL()) {
+            verifyRoute110(reader);
+            /*
+             * Ask for metadata only after a first read, for testing the way the store manages
readers.
+             * The new 'getFeatures()' call should reuse the reader created by 'getMetadata()'
- this
+             * can be verified by stepping in the code with a debugger.
+             */
+            md = (Metadata) reader.getMetadata();
+            verifyRoute110(reader);
+            /*
+             * One more check.
+             */
+            assertSame("metadata", md, reader.getMetadata());
+            verifyRoute110(reader);
+        }
+        verifyAlmostEmptyMetadata(md);
+    }
+
+    /**
+     * Tests reading the same data more than once without waiting
+     * that the previous iterator finishes its iteration.
+     *
+     * @throws DataStoreException if reader failed to be created or failed at reading.
+     */
+    @Test
+    @DependsOnMethod("testSequentialReads")
+    public void testConcurrentReads() throws DataStoreException {
+        try (final Store reader = createFromURL()) {
+            final Stream<Feature>   f1 = reader.getFeatures();
+            final Iterator<Feature> i1 = f1.iterator();
+            verifyRoute(i1.next(), true, 3);
+            final Stream<Feature>   f2 = reader.getFeatures();
+            final Iterator<Feature> i2 = f2.iterator();
+            verifyEmpty(i1.next(), "rtept");
+            final Stream<Feature>   f3 = reader.getFeatures();
+            final Iterator<Feature> i3 = f3.iterator();
+            verifyRoute(i2.next(), true, 3);
+            verifyRoute(i3.next(), true, 3);
+            verifyEmpty(i3.next(), "rtept");
+            verifyEmpty(i2.next(), "rtept");
+            assertFalse("hasNext", i3.hasNext());
+            assertFalse("hasNext", i1.hasNext());
+            assertFalse("hasNext", i2.hasNext());
+            f2.close();
+            f1.close();
+            f3.close();
+        }
+    }
 }



Mime
View raw message