You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2022/01/10 00:48:08 UTC

[sis] branch geoapi-4.0 updated: Add a `Prober.orElse(…)` method for testing probers with different types.

This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 88c8bbb  Add a `Prober.orElse(…)` method for testing probers with different types.
88c8bbb is described below

commit 88c8bbbd3ae7ee867cc9311cd14c833150dfb0aa
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Jan 10 01:29:17 2022 +0100

    Add a `Prober.orElse(…)` method for testing probers with different types.
---
 .../apache/sis/internal/storage/folder/Store.java  |   2 +-
 .../sis/internal/storage/xml/AbstractProvider.java |  24 +-
 .../org/apache/sis/storage/DataStoreProvider.java  | 272 +++++++++++++++------
 .../apache/sis/storage/DataStoreProviderTest.java  |  28 +--
 4 files changed, 213 insertions(+), 113 deletions(-)

diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
index d03cbf3..fd83e34 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
@@ -190,10 +190,10 @@ class Store extends DataStore implements StoreResource, Aggregate, DirectoryStre
     private Store(final Store parent, final StorageConnector connector, final NameFactory nameFactory) throws DataStoreException {
         super(parent, parent.getProvider(), connector, false);
         originator        = parent;
-        location          = connector.getStorageAs(Path.class);
         locale            = connector.getOption(OptionKey.LOCALE);
         timezone          = connector.getOption(OptionKey.TIMEZONE);
         encoding          = connector.getOption(OptionKey.ENCODING);
+        location          = connector.commit(Path.class, StoreProvider.NAME);
         children          = parent.children;
         componentProvider = parent.componentProvider;
         identifier        = nameFactory.createLocalName(parent.identifier(nameFactory).scope(), super.getDisplayName());
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
index 2908d59..30a33b2 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
@@ -111,26 +111,18 @@ public abstract class AbstractProvider extends DocumentedStoreProvider {
         /*
          * Usual case. This includes InputStream, DataInput, File, Path, URL, URI.
          */
-        final ByteBuffer buffer = connector.getStorageAs(ByteBuffer.class);
-        if (buffer != null) {
-            /*
-             * We do not use the safer `probeContent(…)` method because we do not have a mechanism
-             * for telling if `UNSUPPORTED_STORAGE` was determined by this block or if we got that
-             * result because the buffer was null.
-             */
+        Prober<ByteBuffer> prober = (buffer) -> {
             if (buffer.remaining() < HEADER.length) {
                 return ProbeResult.INSUFFICIENT_BYTES;
             }
             // Quick check for "<?xml " header.
-            final int p = buffer.position();
             for (int i=0; i<HEADER.length; i++) {
-                if (buffer.get(p + i) != HEADER[i]) {       // TODO: use ByteBuffer.mismatch(…) with JDK11.
+                if (buffer.get() != HEADER[i]) {              // TODO: use ByteBuffer.mismatch(…) with JDK11.
                     return ProbeResult.UNSUPPORTED_STORAGE;
                 }
             }
             // Now check for a more accurate MIME type.
-            buffer.position(p + HEADER.length);
-            final ProbeResult result = new MimeTypeDetector(mimeForNameSpaces, mimeForRootElements) {
+            return new MimeTypeDetector(mimeForNameSpaces, mimeForRootElements) {
                 @Override int read() {
                     if (buffer.hasRemaining()) {
                         return buffer.get();
@@ -139,14 +131,12 @@ public abstract class AbstractProvider extends DocumentedStoreProvider {
                     return -1;
                 }
             }.probeContent();
-            buffer.position(p);
-            return result;
-        }
+        };
         /*
          * We should enter in this block only if the user gave us explicitly a Reader.
          * A common case is a StringReader wrapping a String object.
          */
-        return probeContent(connector, Reader.class, (reader) -> {
+        prober = prober.orElse(Reader.class, (reader) -> {
             // Quick check for "<?xml " header.
             for (int i=0; i<HEADER.length; i++) {
                 if (reader.read() != HEADER[i]) {
@@ -154,13 +144,13 @@ public abstract class AbstractProvider extends DocumentedStoreProvider {
                 }
             }
             // Now check for a more accurate MIME type.
-            final ProbeResult result = new MimeTypeDetector(mimeForNameSpaces, mimeForRootElements) {
+            return new MimeTypeDetector(mimeForNameSpaces, mimeForRootElements) {
                 private int remaining = READ_AHEAD_LIMIT;
                 @Override int read() throws IOException {
                     return (--remaining >= 0) ? IOUtilities.readCodePoint(reader) : -1;
                 }
             }.probeContent();
-            return result;
         });
+        return probeContent(connector, ByteBuffer.class, prober);
     }
 }
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
index fb6bab3..ec30d09 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
@@ -119,9 +119,9 @@ public abstract class DataStoreProvider {
 
     /**
      * The logger where to reports warnings or change events. Created when first needed and kept
-     * by strong reference for avoiding configuration lost if the logger if garbage collected.
-     * This strategy assumes that {@code URIDataStore.Provider} instances are kept alive for
-     * the duration of JVM lifetime, which is the case with {@link DataStoreRegistry}.
+     * by strong reference for avoiding configuration lost if the logger is garbage collected.
+     * This strategy assumes that {@code DataStoreProvider} instances are kept alive for the
+     * duration of JVM lifetime, which is the case with {@link DataStoreRegistry}.
      *
      * @see #getLogger()
      */
@@ -339,6 +339,7 @@ public abstract class DataStoreProvider {
             final Class<S> type, final Prober<? super S> prober) throws DataStoreException
     {
         ArgumentChecks.ensureNonNull("prober", prober);
+        boolean undetermined = false;
         /*
          * Synchronization is not a documented feature for now because the policy may change in future version.
          * Current version uses the storage source as the synchronization lock because using `StorageConnector`
@@ -346,90 +347,141 @@ public abstract class DataStoreProvider {
          * which lock (if any) is used by the source. But `InputStream` for example uses `this`.
          */
         synchronized (connector.storage) {
-            final S input = connector.getStorageAs(type);
-            if (input == null) {        // Means that the given type is valid but not applicable for current storage.
-                return ProbeResult.UNSUPPORTED_STORAGE;
+            ProbeResult result = tryProber(connector, type, prober);
+            undetermined = (result == ProbeResult.UNDETERMINED);
+            if (result != null && !undetermined) {
+                return result;
             }
-            if (input == connector.storage && !StorageConnector.isSupportedType(type)) {
+            /*
+             * If the storage connector can not provide the type of source required by the specified prober,
+             * verify if there is any other probers specified by `Prober.orElse(…)`.
+             */
+            Prober<?> next = prober;
+            while (next instanceof ProberList<?,?>) {
+                final ProberList<?,?> list = (ProberList<?,?>) next;
+                result = tryNextProber(connector, list);
+                if (result != null && result != ProbeResult.UNDETERMINED) {
+                    return result;
+                }
+                undetermined |= (result == ProbeResult.UNDETERMINED);
+                next = list.next;
+            }
+        }
+        return undetermined ? ProbeResult.UNDETERMINED : ProbeResult.UNSUPPORTED_STORAGE;
+    }
+
+    /**
+     * Tries the {@link ProberList#next} probe. This method is defined for type parameterization
+     * (the caller has only {@code <?>} and we need a specific type {@code <N>}).
+     *
+     * @param  <N>        type of input requested by the next probe.
+     * @param  connector  information about the storage (URL, stream, JDBC connection, <i>etc</i>).
+     * @param  list       root of the chained list of next probes.
+     */
+    private <N> ProbeResult tryNextProber(final StorageConnector connector, final ProberList<?,N> list) throws DataStoreException {
+        return tryProber(connector, list.type, list.next);
+    }
+
+    /**
+     * Implementation of {@link #probeContent(StorageConnector, Class, Prober)}
+     * for a single element in a list of probe.
+     *
+     * @param  <S>        the compile-time type of the {@code type} argument (the source or storage type).
+     * @param  connector  information about the storage (URL, stream, JDBC connection, <i>etc</i>).
+     * @param  type       the desired type as one of {@code ByteBuffer}, {@code DataInput}, <i>etc</i>.
+     * @param  prober     the test to apply on the source of the given type.
+     * @return the result of executing the probe action with a source of the given type,
+     *         or {@code null} if the given type is supported but no view can be created.
+     * @throws IllegalArgumentException if the given {@code type} argument is not one of the supported types.
+     * @throws IllegalStateException if this {@code StorageConnector} has been {@linkplain #closeAllExcept closed}.
+     * @throws DataStoreException if another kind of error occurred.
+     */
+    private <S> ProbeResult tryProber(final StorageConnector connector,
+            final Class<S> type, final Prober<? super S> prober) throws DataStoreException
+    {
+        final S input = connector.getStorageAs(type);
+        if (input == null) {        // Means that the given type is valid but not applicable for current storage.
+            return null;
+        }
+        if (input == connector.storage && !StorageConnector.isSupportedType(type)) {
+            /*
+             * The given type is not one of the types known to `StorageConnector` (the list of supported types
+             * is hard-coded). We could give the input as-is to the prober, but we have no idea how to fulfill
+             * the method contract saying that the use of the input is safe. We throw an exception for telling
+             * to the users that they should manage the input themselves.
+             */
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, type));
+        }
+        ProbeResult result = null;
+        try {
+            if (input instanceof ByteBuffer) {
+                /*
+                 * No need to save buffer position because `asReadOnlyBuffer()` creates an independent buffer
+                 * with its own mark and position. Byte order of the view is intentionally fixed to BIG_ENDIAN
+                 * (the default) regardless the byte order of the original buffer.
+                 */
+                final ByteBuffer buffer = (ByteBuffer) input;
+                result = prober.test(type.cast(buffer.asReadOnlyBuffer()));
+            } else if (input instanceof Markable) {
+                /*
+                 * `Markable` stream can nest an arbitrary number of marks. So we allow users to create
+                 * their own marks. In principle a single call to `reset()` is enough, but we check the
+                 * position in case the user has done some marks without resets.
+                 */
+                final Markable stream = (Markable) input;
+                final long position = stream.getStreamPosition();
+                stream.mark();
+                result = prober.test(input);
+                stream.reset(position);
+            } else if (input instanceof ImageInputStream) {
                 /*
-                 * The given type is not one of the types known to `StorageConnector` (the list of supported types
-                 * is hard-coded). We could give the input as-is to the prober, but we have no idea how to fulfill
-                 * the method contract saying that the use of the input is safe. We throw an exception for telling
-                 * to the users that they should manage the input themselves.
+                 * `ImageInputStream` supports an arbitrary number of marks as well,
+                 * but we use absolute positioning for simplicity.
                  */
-                throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, type));
+                final ImageInputStream stream = (ImageInputStream) input;
+                final long position = stream.getStreamPosition();
+                result = prober.test(input);
+                stream.seek(position);
+            } else if (input instanceof InputStream) {
+                /*
+                 * `InputStream` supports at most one mark. So we keep it for ourselves
+                 * and wrap the stream in an object that prevent users from using marks.
+                 */
+                final ProbeInputStream stream = new ProbeInputStream(connector, (InputStream) input);
+                result = prober.test(type.cast(stream));
+                stream.close();                 // Reset (not close) the wrapped stream.
+            } else if (input instanceof RewindableLineReader) {
+                /*
+                 * `Reader` supports at most one mark. So we keep it for ourselves and prevent users
+                 * from using marks, but without wrapper if we can safely expose a `BufferedReader`
+                 * (because users may want to use the `BufferedReader.readLine()` method).
+                 */
+                final RewindableLineReader r = (RewindableLineReader) input;
+                r.protectedMark();
+                result = prober.test(input);
+                r.protectedReset();
+            } else if (input instanceof Reader) {
+                final Reader stream = new ProbeReader(connector, (Reader) input);
+                result = prober.test(type.cast(stream));
+                stream.close();                 // Reset (not close) the wrapped reader.
+            } else {
+                /*
+                 * All other cases are objects like File, URL, etc. which can be used without mark/reset.
+                 * Note that if the type was not known to be safe, an exception would have been thrown at
+                 * the beginning of this method.
+                 */
+                result = prober.test(input);
             }
-            ProbeResult result = null;
-            try {
-                if (input instanceof ByteBuffer) {
-                    /*
-                     * No need to save buffer position because `asReadOnlyBuffer()` creates an independent buffer
-                     * with its own mark and position. Byte order of the view is intentionally fixed to BIG_ENDIAN
-                     * (the default) regardless the byte order of the original buffer.
-                     */
-                    final ByteBuffer buffer = (ByteBuffer) input;
-                    result = prober.test(type.cast(buffer.asReadOnlyBuffer()));
-                } else if (input instanceof Markable) {
-                    /*
-                     * `Markable` stream can nest an arbitrary number of marks. So we allow users to create
-                     * their own marks. In principle a single call to `reset()` is enough, but we check the
-                     * position in case the user has done some marks without resets.
-                     */
-                    final Markable stream = (Markable) input;
-                    final long position = stream.getStreamPosition();
-                    stream.mark();
-                    result = prober.test(input);
-                    stream.reset(position);
-                } else if (input instanceof ImageInputStream) {
-                    /*
-                     * `ImageInputStream` supports an arbitrary number of marks as well,
-                     * but we use absolute positioning for simplicity.
-                     */
-                    final ImageInputStream stream = (ImageInputStream) input;
-                    final long position = stream.getStreamPosition();
-                    result = prober.test(input);
-                    stream.seek(position);
-                } else if (input instanceof InputStream) {
-                    /*
-                     * `InputStream` supports at most one mark. So we keep it for ourselves
-                     * and wrap the stream in an object that prevent users from using marks.
-                     */
-                    final ProbeInputStream stream = new ProbeInputStream(connector, (InputStream) input);
-                    result = prober.test(type.cast(stream));
-                    stream.close();                 // Reset (not close) the wrapped stream.
-                } else if (input instanceof RewindableLineReader) {
-                    /*
-                     * `Reader` supports at most one mark. So we keep it for ourselves and prevent users
-                     * from using marks, but without wrapper if we can safely expose a `BufferedReader`
-                     * (because users may want to use the `BufferedReader.readLine()` method).
-                     */
-                    final RewindableLineReader r = (RewindableLineReader) input;
-                    r.protectedMark();
-                    result = prober.test(input);
-                    r.protectedReset();
-                } else if (input instanceof Reader) {
-                    final Reader stream = new ProbeReader(connector, (Reader) input);
-                    result = prober.test(type.cast(stream));
-                    stream.close();                 // Reset (not close) the wrapped reader.
-                } else {
-                    /*
-                     * All other cases are objects like File, URL, etc. which can be used without mark/reset.
-                     * Note that if the type was not known to be safe, an exception would have been thrown at
-                     * the beginning of this method.
-                     */
-                    result = prober.test(input);
-                }
-            } catch (DataStoreException e) {
-                throw e;
-            } catch (Exception e) {
-                final String message = Errors.format(Errors.Keys.CanNotRead_1, connector.getStorageName());
-                if (result != null) {
-                    throw new ForwardOnlyStorageException(message, e);
-                }
-                throw new CanNotProbeException(this, connector, e);
+        } catch (DataStoreException e) {
+            throw e;
+        } catch (Exception e) {
+            final String message = Errors.format(Errors.Keys.CanNotRead_1, connector.getStorageName());
+            if (result != null) {
+                throw new ForwardOnlyStorageException(message, e);
             }
-            return result;
+            throw new CanNotProbeException(this, connector, e);
         }
+        return result;
     }
 
     /**
@@ -461,6 +513,64 @@ public abstract class DataStoreProvider {
          * @throws Exception if an error occurred during the execution of the probe action.
          */
         ProbeResult test(S input) throws Exception;
+
+        /**
+         * Returns a composed probe that attempts, in sequence, this probe followed by the alternative probe
+         * if the first probe can not be executed. The alternative probe is tried if and only if one of the
+         * following conditions is true:
+         *
+         * <ul>
+         *   <li>The storage connector can not provide an input of the type requested by this probe.</li>
+         *   <li>This probe {@link #test(S)} method returned {@link ProbeResult#UNDETERMINED}.</li>
+         * </ul>
+         *
+         * If any probe throws an exception, the exception is propagated
+         * (the alternative probe is not a fallback executed if this probe threw an exception).
+         *
+         * @param  <A>          the compile-time type of the {@code type} argument (the source or storage type).
+         * @param  type         the desired type as one of {@code ByteBuffer}, {@code DataInput}, <i>etc</i>.
+         * @param  alternative  the test to apply on the source of the given type.
+         * @return a composed probe that attempts the given probe if this probe can not be executed.
+         */
+        default <A> Prober<S> orElse(final Class<A> type, final Prober<? super A> alternative) {
+            return new ProberList<>(this, type, alternative);
+        }
+    }
+
+    /**
+     * Implementation of the composed probe returned by {@link Prober#orElse(Class, Prober)}.
+     * Instances of this class a nodes in a linked list.
+     *
+     * @param <S>  the source type of the original probe.
+     * @param <N>  the source type of the next probe to try as an alternative.
+     */
+    private static final class ProberList<S,N> implements Prober<S> {
+        /** The main probe to try first. */
+        private final Prober<S> first;
+
+        /** The probe to try next if the {@linkplain #first} probe can not be executed. */
+        Prober<? super N> next;
+
+        /** Type of input expected by the {@linkplain #next} probe. */
+        final Class<N> type;
+
+        /** Creates a new composed probe as a root node of a linked list. */
+        ProberList(final Prober<S> first, final Class<N> type, final Prober<? super N> next) {
+            this.first = first;
+            this.type  = type;
+            this.next  = next;
+        }
+
+        /** Forward to the primary probe. */
+        @Override public ProbeResult test(final S input) throws Exception {
+            return first.test(input);
+        }
+
+        /** Appends a new probe alternative at the end of this linked list. */
+        @Override public <A> Prober<S> orElse(final Class<A> type, final Prober<? super A> prober) {
+            next = next.orElse(type, prober);
+            return this;
+        }
     }
 
     /**
diff --git a/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreProviderTest.java b/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreProviderTest.java
index c423504..539f3e5 100644
--- a/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreProviderTest.java
+++ b/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreProviderTest.java
@@ -65,20 +65,20 @@ public final strictfp class DataStoreProviderTest extends TestCase {
      * Asserts that probing with {@link InputStream} input gives the expected result.
      */
     private void verifyProbeWithInputStream(final StorageConnector connector) throws DataStoreException {
-        assertEquals(provider.probeContent(connector, InputStream.class, stream -> {
+        assertEquals(ProbeResult.SUPPORTED, provider.probeContent(connector, InputStream.class, stream -> {
             StorageConnectorTest.assertExpectedBytes(stream);
             return ProbeResult.SUPPORTED;
-        }), ProbeResult.SUPPORTED);
+        }));
     }
 
     /**
      * Asserts that probing with {@link Reader} input gives the expected result.
      */
     private void verifyProbeWithReader(final StorageConnector connector) throws DataStoreException {
-        assertEquals(provider.probeContent(connector, Reader.class, stream -> {
+        assertEquals(ProbeResult.SUPPORTED, provider.probeContent(connector, Reader.class, stream -> {
             StorageConnectorTest.assertExpectedChars(stream);
             return ProbeResult.SUPPORTED;
-        }), ProbeResult.SUPPORTED);
+        }));
     }
 
     /**
@@ -99,13 +99,13 @@ public final strictfp class DataStoreProviderTest extends TestCase {
          * Verify that the byte buffer given to the prober always have the big endian order,
          * regardless the byte order of the original buffer. This is part of method contract.
          */
-        assertEquals(provider.probeContent(connector, ByteBuffer.class, buffer -> {
+        assertEquals(ProbeResult.UNDETERMINED, provider.probeContent(connector, ByteBuffer.class, buffer -> {
             assertEquals(ByteOrder.BIG_ENDIAN, buffer.order());
             assertEquals(3, buffer.position());
             assertEquals(8, buffer.limit());
             buffer.position(5).mark();
             return ProbeResult.UNDETERMINED;
-        }), ProbeResult.UNDETERMINED);
+        }));
         /*
          * Verifies that the origial buffer has its byte order and position unchanged.
          */
@@ -126,23 +126,23 @@ public final strictfp class DataStoreProviderTest extends TestCase {
          * without resetting the buffer position.
          */
         final StorageConnector connector = StorageConnectorTest.create(false);
-        assertEquals(provider.probeContent(connector, ByteBuffer.class, buffer -> {
+        assertEquals(ProbeResult.UNDETERMINED, provider.probeContent(connector, ByteBuffer.class, buffer -> {
             assertEquals(0, buffer.position());
             buffer.position(15).mark();
             return ProbeResult.UNDETERMINED;
-        }), ProbeResult.UNDETERMINED);
+        }));
         /*
          * Read again. The buffer position should be the original position
          * (i.e. above call to `position(15)` shall have no effect below).
          */
-        assertEquals(provider.probeContent(connector, ByteBuffer.class, buffer -> {
+        assertEquals(ProbeResult.SUPPORTED, provider.probeContent(connector, ByteBuffer.class, buffer -> {
             assertEquals(0, buffer.position());
             final byte[] expected = StorageConnectorTest.getFirstExpectedBytes();
             final byte[] actual = new byte[expected.length];
             buffer.get(actual);
             assertArrayEquals(expected, actual);
             return ProbeResult.SUPPORTED;
-        }), ProbeResult.SUPPORTED);
+        }));
     }
 
     /**
@@ -174,7 +174,7 @@ public final strictfp class DataStoreProviderTest extends TestCase {
          * Read a few bytes and verify that user can not overwrite the mark.
          */
         final StorageConnector connector = StorageConnectorTest.create(asStream);
-        assertEquals(provider.probeContent(connector, InputStream.class, stream -> {
+        assertEquals(ProbeResult.SUPPORTED, provider.probeContent(connector, InputStream.class, stream -> {
             assertEquals(!asStream, stream.markSupported());
             stream.skip(5);
             stream.mark(10);
@@ -188,7 +188,7 @@ public final strictfp class DataStoreProviderTest extends TestCase {
                 stream.reset();         // Should be supported if opened from URL.
             }
             return ProbeResult.SUPPORTED;
-        }), ProbeResult.SUPPORTED);
+        }));
         /*
          * Read the first bytes and verify that they are really the
          * beginning of the file despite above reading of some bytes.
@@ -253,7 +253,7 @@ public final strictfp class DataStoreProviderTest extends TestCase {
         /*
          * Read a few bytes and verify that user can not overwrite the mark.
          */
-        assertEquals(provider.probeContent(connector, Reader.class, stream -> {
+        assertEquals(ProbeResult.SUPPORTED, provider.probeContent(connector, Reader.class, stream -> {
             assertEquals(buffered, stream instanceof BufferedReader);
             assertFalse(stream.markSupported());
             stream.skip(5);
@@ -264,7 +264,7 @@ public final strictfp class DataStoreProviderTest extends TestCase {
                 assertTrue(e.getMessage().contains("mark"));
             }
             return ProbeResult.SUPPORTED;
-        }), ProbeResult.SUPPORTED);
+        }));
         /*
          * Read the first bytes and verify that they are really the
          * beginning of the file despite above reading of some bytes.