Author: desruisseaux
Date: Wed Aug 7 16:54:59 2013
New Revision: 1511393
URL: http://svn.apache.org/r1511393
Log:
StorageConnector needs to be also able to provide InputStream and Reader
objects.
This support complicates a bit the task of closing the stream, in part because
AutoCloseable is not guaranteed to be idempotent (ImageInputStream in not).
Added:
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/InputStreamAdapter.java
(with props)
Modified:
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/DataStores.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/storage/StorageConnectorTest.java
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=1511393&r1=1511392&r2=1511393&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] Wed Aug 7 16:54:59 2013
@@ -73,7 +73,7 @@ final class DataStoreRegistry {
* <li>A {@link java.nio.file.Path} or a {@link java.io.File} for a file
or a directory.</li>
* <li>A {@link java.net.URI} or a {@link java.net.URL} to a distant
resource.</li>
* <li>A {@link java.lang.CharSequence} interpreted as a filename or a
URL.</li>
- * <li>A {@link java.nio.channels.Channel} or a {@link
java.io.DataInput}.</li>
+ * <li>A {@link java.nio.channels.Channel}, {@link java.io.DataInput},
{@link java.io.InputStream} or {@link java.io.Reader}.</li>
* <li>A {@link javax.sql.DataSource} or a {@link java.sql.Connection}
to a JDBC database.</li>
* <li>Any other {@code DataStore}-specific object, for example {@link
ucar.nc2.NetcdfFile}.</li>
* <li>An existing {@link StorageConnector} instance.</li>
Modified:
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStores.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStores.java?rev=1511393&r1=1511392&r2=1511393&view=diff
==============================================================================
---
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStores.java
[UTF-8] (original)
+++
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStores.java
[UTF-8] Wed Aug 7 16:54:59 2013
@@ -69,7 +69,7 @@ public final class DataStores extends St
* <li>A {@link java.nio.file.Path} or a {@link java.io.File} for a file
or a directory.</li>
* <li>A {@link java.net.URI} or a {@link java.net.URL} to a distant
resource.</li>
* <li>A {@link java.lang.CharSequence} interpreted as a filename or a
URL.</li>
- * <li>A {@link java.nio.channels.Channel} or a {@link
java.io.DataInput}.</li>
+ * <li>A {@link java.nio.channels.Channel}, {@link java.io.DataInput},
{@link java.io.InputStream} or {@link java.io.Reader}.</li>
* <li>A {@link javax.sql.DataSource} or a {@link java.sql.Connection}
to a JDBC database.</li>
* <li>Any other {@code DataStore}-specific object, for example {@link
ucar.nc2.NetcdfFile}.</li>
* <li>An existing {@link StorageConnector} instance.</li>
Added:
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/InputStreamAdapter.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/InputStreamAdapter.java?rev=1511393&view=auto
==============================================================================
---
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/InputStreamAdapter.java
(added)
+++
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/InputStreamAdapter.java
[UTF-8] Wed Aug 7 16:54:59 2013
@@ -0,0 +1,124 @@
+/*
+ * 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.storage;
+
+import java.io.InputStream;
+import java.io.IOException;
+import javax.imageio.stream.ImageInputStream;
+
+
+/**
+ * Wraps a {@link ImageInputStream} as a standard {@link InputStream}.
+ *
+ * @author Martin Desruisseaux (IRD)
+ * @since 0.4 (derived from geotk-1.2)
+ * @version 0.4
+ * @module
+ */
+final class InputStreamAdapter extends InputStream {
+ /**
+ * The data input stream.
+ */
+ final ImageInputStream input;
+
+ /**
+ * Constructs a new input stream.
+ */
+ InputStreamAdapter(final ImageInputStream input) {
+ this.input = input;
+ }
+
+ /**
+ * Reads the next byte of data from the input stream.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public int read() throws IOException {
+ return input.read();
+ }
+
+ /**
+ * Reads some number of bytes from the input stream.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public int read(final byte[] b) throws IOException {
+ return input.read(b);
+ }
+
+ /**
+ * Reads up to {@code len} bytes of data from the input stream.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public int read(final byte[] b, final int off, final int len) throws
IOException {
+ return input.read(b, off, len);
+ }
+
+ /**
+ * Skips over and discards {@code n} bytes of data from this input stream.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public long skip(final long n) throws IOException {
+ return input.skipBytes(n);
+ }
+
+ /**
+ * Returns always {@code true}, since marks support is mandatory in image
input stream.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ /**
+ * Marks the current position in this input stream.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public void mark(final int readlimit) {
+ input.mark();
+ }
+
+ /**
+ * Repositions this stream to the position at the time the {@code mark}
method was last called.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public void reset() throws IOException {
+ input.reset();
+ }
+
+ /**
+ * Closes this input stream.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public void close() throws IOException {
+ input.close();
+ }
+}
Propchange:
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/InputStreamAdapter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/InputStreamAdapter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
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=1511393&r1=1511392&r2=1511393&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] Wed Aug 7 16:54:59 2013
@@ -17,13 +17,20 @@
package org.apache.sis.storage;
import java.util.Map;
+import java.util.Queue;
+import java.util.Iterator;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.IdentityHashMap;
import java.util.ConcurrentModificationException;
+import java.io.Reader;
import java.io.DataInput;
+import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.Serializable;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.nio.channels.ReadableByteChannel;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
@@ -47,7 +54,7 @@ import org.apache.sis.setup.OptionKey;
* <li>A {@link java.nio.file.Path} or a {@link java.io.File} for a file or
a directory.</li>
* <li>A {@link java.net.URI} or a {@link java.net.URL} to a distant
resource.</li>
* <li>A {@link CharSequence} interpreted as a filename or a URL.</li>
- * <li>A {@link java.nio.channels.Channel} or a {@link DataInput}.</li>
+ * <li>A {@link java.nio.channels.Channel}, {@link DataInput}, {@link
InputStream} or {@link Reader}.</li>
* <li>A {@link DataSource} or a {@link Connection} to a JDBC database.</li>
* <li>Any other {@code DataStore}-specific object, for example {@link
ucar.nc2.NetcdfFile}.</li>
* </ul>
@@ -84,7 +91,7 @@ public class StorageConnector implements
* 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;
+ static final int MINIMAL_BUFFER_SIZE = 256;
/**
* The input/output object given at construction time.
@@ -121,6 +128,12 @@ public class StorageConnector implements
* <li>{@link ImageInputStream}:
* Same as {@code DataInput} if it can be casted, or {@code null}
otherwise.</li>
*
+ * <li>{@link InputStream}:
+ * If not explicitely provided, this is a wrapper around the above
{@link ImageInputStream}.</li>
+ *
+ * <li>{@link Reader}:
+ * If not explicitely provided, this is a wrapper around the above
{@link InputStream}.</li>
+ *
* <li>{@link Connection}:
* The storage object as a JDBC connection.</li>
* </ul>
@@ -134,6 +147,17 @@ public class StorageConnector implements
private transient Map<Class<?>, Object> views;
/**
+ * Objects which will need to be closed by the {@link
#closeAllExcept(Object)} method.
+ * For each (<var>key</var>, <var>value</var>) entry, if the object to
close (the key)
+ * is a wrapper around an other object (e.g. an {@link InputStreamReader}
wrapping an
+ * {@link InputStream}), then the value is the other object.
+ *
+ * @see #addViewToClose(Object, Object)
+ * @see #closeAllExcept(Object)
+ */
+ private transient Map<Object, Object> viewsToClose;
+
+ /**
* The options, created only when first needed.
*
* @see #getOption(OptionKey)
@@ -165,12 +189,13 @@ public class StorageConnector implements
}
/**
- * Sets the option value for the given key. The default implementation
recognizes the given options:
+ * Sets the option value for the given key. The default implementation
recognizes the following options:
*
* <ul>
+ * <li>{@link OptionKey#ENCODING} for decoding characters in an
input stream, if needed.</li>
* <li>{@link OptionKey#URL_ENCODING} for converting URL to URI or
filename, if needed.</li>
* <li>{@link OptionKey#OPEN_OPTIONS} for specifying whether the data
store shall be read only or read/write.</li>
- * <li>{@link OptionKey#BYTE_BUFFER} for allowing users to control the
byte buffer to be created.</li>
+ * <li>{@link OptionKey#BYTE_BUFFER} for allowing users to control the
byte buffer to be created.</li>
* </ul>
*
* @param <T> The type of option value.
@@ -265,12 +290,12 @@ public class StorageConnector implements
* </li>
* <li>{@link DataInput}:
* <ul>
- * <li>If the {@linkplain #getStorage() storage} object is already
an instance of {@link DataInput}
+ * <li>If the {@linkplain #getStorage() storage} object is already
an instance of {@code DataInput}
* (including the {@link ImageInputStream} and {@link
javax.imageio.stream.ImageOutputStream} types),
* then it is returned unchanged.</li>
*
* <li>Otherwise if the input is an instance of {@link
java.nio.file.Path}, {@link java.io.File},
- * {@link java.net.URI}, {@link java.net.URL}, {@link
CharSequence}, {@link java.io.InputStream} or
+ * {@link java.net.URI}, {@link java.net.URL}, {@link
CharSequence}, {@link InputStream} or
* {@link java.nio.channels.ReadableByteChannel}, then an {@link
ImageInputStream} backed by a
* {@link ByteBuffer} is created when first needed and
returned.</li>
*
@@ -282,7 +307,30 @@ public class StorageConnector implements
* </li>
* <li>{@link ImageInputStream}:
* <ul>
- * <li>If the {@code DataInput} computed above can be casted to
{@code null}, returns it.</li>
+ * <li>If the above {@code DataInput} can be created and casted to
{@code ImageInputStream}, returns it.</li>
+ *
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul>
+ * </li>
+ * <li>{@link InputStream}:
+ * <ul>
+ * <li>If the {@linkplain #getStorage() storage} object is already
an instance of {@link InputStream},
+ * then it is returned unchanged.</li>
+ *
+ * <li>Otherwise if the above {@code ImageInputStream} can be
created,
+ * returns a wrapper around that stream.</li>
+ *
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul>
+ * </li>
+ * <li>{@link Reader}:
+ * <ul>
+ * <li>If the {@linkplain #getStorage() storage} object is already
an instance of {@link Reader},
+ * then it is returned unchanged.</li>
+ *
+ * <li>Otherwise if the above {@code InputStream} can be created,
returns an {@link InputStreamReader}
+ * using the encoding specified by {@link OptionKey#ENCODING} if
any, or using the system default
+ * encoding otherwise.</li>
*
* <li>Otherwise this method returns {@code null}.</li>
* </ul>
@@ -379,6 +427,7 @@ public class StorageConnector implements
getOption(OptionKey.URL_ENCODING),
getOption(OptionKey.OPEN_OPTIONS));
ChannelDataInput asDataInput = null;
if (channel != null) {
+ addViewToClose(channel, storage);
ByteBuffer buffer = getOption(OptionKey.BYTE_BUFFER);
if (buffer == null) {
buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
@@ -391,6 +440,7 @@ public class StorageConnector implements
} else {
asDataInput = new ChannelDataInput(name, channel, buffer,
false);
}
+ addViewToClose(asDataInput, channel);
}
addView(ChannelDataInput.class, asDataInput);
}
@@ -419,13 +469,16 @@ public class StorageConnector implements
final ChannelDataInput c = getView(ChannelDataInput.class);
if (c == null) {
asDataInput = ImageIO.createImageInputStream(storage);
+ addViewToClose(asDataInput, storage);
} else if (c instanceof DataInput) {
asDataInput = (DataInput) c;
+ // No call to 'addViewToClose' because the instance already
exists.
} else {
asDataInput = new ChannelImageInputStream(c);
- if (views.put(ChannelDataInput.class, asDataInput) != c) {
+ if (views.put(ChannelDataInput.class, asDataInput) != c) { //
Replace the previous instance.
throw new ConcurrentModificationException();
}
+ addViewToClose(asDataInput, c.channel);
}
}
addView(DataInput.class, asDataInput);
@@ -532,13 +585,42 @@ public class StorageConnector implements
if (storage instanceof Connection) {
return storage;
} else if (storage instanceof DataSource) {
- return ((DataSource) storage).getConnection();
+ final Connection c = ((DataSource) storage).getConnection();
+ addViewToClose(c, storage);
+ return c;
}
}
if (type == ImageInputStream.class) {
final DataInput input = getStorageAs(DataInput.class);
return (input instanceof ImageInputStream) ? input : null;
}
+ if (type == InputStream.class) {
+ if (storage instanceof InputStream) {
+ return storage;
+ }
+ final DataInput input = getStorageAs(DataInput.class);
+ if (input instanceof InputStream) {
+ return (InputStream) input;
+ }
+ if (input instanceof ImageInputStream) {
+ final InputStream c = new
InputStreamAdapter((ImageInputStream) input);
+ addViewToClose(c, input);
+ return c;
+ }
+ }
+ if (type == Reader.class) {
+ if (storage instanceof Reader) {
+ return storage;
+ }
+ final InputStream input = getStorageAs(InputStream.class);
+ if (input != null) {
+ final Charset encoding = getOption(OptionKey.ENCODING);
+ final Reader c = (encoding != null) ? new
InputStreamReader(input, encoding)
+ : new
InputStreamReader(input);
+ addViewToClose(c, input);
+ return c;
+ }
+ }
throw new
IllegalArgumentException(Errors.format(Errors.Keys.UnknownType_1, type));
}
@@ -568,6 +650,23 @@ public class StorageConnector implements
}
/**
+ * Declares that the given {@code input} will need to be closed by the
{@link #closeAllExcept(Object)} method.
+ * The {@code input} argument is always a new instance wrapping, directly
or indirectly, the {@link #storage}.
+ * Callers must specify the wrapped object in the {@code delegate}
argument.
+ *
+ * @param input The newly created object which will need to be closed.
+ * @param delegate The object wrapped by the given {@code input}.
+ */
+ private void addViewToClose(final Object input, final Object delegate) {
+ if (viewsToClose == null) {
+ viewsToClose = new IdentityHashMap<>(4);
+ }
+ if (viewsToClose.put(input, delegate) != null) {
+ throw new AssertionError(input);
+ }
+ }
+
+ /**
* Closes all streams and connections created by this {@code
StorageConnector} except the given view.
* This method closes all objects created by the {@link
#getStorageAs(Class)} method except the given {@code view}.
* If {@code view} is {@code null}, then this method closes everything
including the {@linkplain #getStorage()
@@ -586,37 +685,86 @@ public class StorageConnector implements
* @see DataStoreProvider#open(StorageConnector)
*/
public void closeAllExcept(final Object view) throws DataStoreException {
+ final Map<Object,Object> toClose = viewsToClose;
+ viewsToClose = Collections.emptyMap();
+ views = Collections.emptyMap();
+ if (toClose == null) {
+ if (storage != view && storage instanceof AutoCloseable) try {
+ ((AutoCloseable) storage).close();
+ } catch (Exception e) {
+ throw new DataStoreException(e);
+ }
+ return;
+ }
/*
- * Need a set of objects to close without duplicated values. In
particular, the value for
- * DataInput and ImageInputStream are often the same instance. We must
avoid duplicated
- * values because ImageInputStream.close() is not indempotent.
+ * The "AutoCloseable.close() is not indempotent" problem
+ * ------------------------------------------------------
+ * We will need a set of objects to close without duplicated values.
For example the values associated to the
+ * 'ImageInputStream.class' and 'DataInput.class' keys are often the
same instance. We must avoid duplicated
+ * values because 'ImageInputStream.close()' is not indempotent, i.e.
invoking their 'close()' method twice
+ * will thrown an IOException.
+ *
+ * Generally speaking, all AutoCloseable instances are not guaranteed
to be indempotent because this is not
+ * required by the interface contract. Consequently we must be careful
to not invoke the close() method on
+ * the same instance twice (indirectly or indirectly).
+ *
+ * The set of objects to close will be the keys of the 'viewsToClose'
map. It can not be the values of the
+ * 'views' map.
*/
- final Map<AutoCloseable,Object> toClose = new IdentityHashMap<>(4);
- for (final Object value : views.values()) {
- if (value instanceof AutoCloseable) {
- toClose.put((AutoCloseable) value, null);
- }
+ toClose.put(storage, null);
+ if (view != null) {
+ /*
+ * If there is a view to not close, search for all views that are
wrapper for the given view.
+ * Those wrappers shall not be closed. For example if the caller
does not want to close the
+ * InputStream view, then we shall not close the InputStreamReader
wrapper neither.
+ */
+ final Queue<Object> deferred = new LinkedList<>();
+ Object doNotClose = view;
+ do {
+ final Iterator<Map.Entry<Object,Object>> it =
toClose.entrySet().iterator();
+ while (it.hasNext()) {
+ final Map.Entry<Object,Object> entry = it.next();
+ if (entry.getValue() == doNotClose) {
+ deferred.add(entry.getKey());
+ it.remove();
+ }
+ }
+ doNotClose = deferred.poll();
+ } while (doNotClose != null);
}
- toClose.remove(view);
- toClose.remove(storage);
- try {
- if (!toClose.isEmpty()) {
- for (final AutoCloseable value : toClose.keySet()) {
- value.close();
+ /*
+ * Remove the view to not close. If that view is a wrapper for an
other object, do not close the
+ * wrapped object neither. Proceed the dependency chain up to the
original 'storage' object.
+ */
+ for (Object doNotClose = view; doNotClose != null;) {
+ doNotClose = toClose.remove(doNotClose);
+ }
+ /*
+ * Remove all wrapped objects. After this loop, only the "top level"
objects should remain
+ * (typically only one object). This block is needed because of the
"AutoCloseable.close()
+ * is not idempotent" issue, otherwise we could have omitted it.
+ */
+ for (final Object delegate : toClose.values().toArray()) { //
'toArray()' is for avoiding ConcurrentModificationException.
+ toClose.remove(delegate);
+ }
+ /*
+ * Now close all remaining items. If an exception occurs, we will
propagate it only after we are
+ * done closing all items.
+ */
+ DataStoreException failure = null;
+ for (final Object c : toClose.keySet()) {
+ if (c instanceof AutoCloseable) try {
+ ((AutoCloseable) c).close();
+ } catch (Exception e) {
+ if (failure == null) {
+ failure = new DataStoreException(e);
+ } else {
+ failure.addSuppressed(e);
}
- } else if (view == null && storage instanceof AutoCloseable) {
- /*
- * Close only if we didn't closed a view because closing an
input stream view
- * automatically close the 'storage' if the former is a
wrapper for the later.
- * Since AutoCloseable.close() is not guaranteed to be
indempotent, we should
- * avoid to call it (indirectly) twice.
- */
- ((AutoCloseable) storage).close();
}
- } catch (Exception e) {
- throw new DataStoreException(e);
- } finally {
- views = Collections.emptyMap();
+ }
+ if (failure != null) {
+ throw failure;
}
}
Modified:
sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java?rev=1511393&r1=1511392&r2=1511393&view=diff
==============================================================================
---
sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java
[UTF-8] (original)
+++
sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java
[UTF-8] Wed Aug 7 16:54:59 2013
@@ -17,6 +17,8 @@
package org.apache.sis.storage;
import java.io.DataInput;
+import java.io.InputStream;
+import java.io.Reader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
@@ -37,7 +39,7 @@ import static org.opengis.test.Assert.*;
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.4
* @module
*/
@DependsOn(org.apache.sis.internal.storage.ChannelImageInputStreamTest.class)
@@ -149,6 +151,50 @@ public final strictfp class StorageConne
}
/**
+ * Tests the {@link StorageConnector#getStorageAs(Class)} method for the
{@link InputStream} type.
+ *
+ * @throws DataStoreException Should never happen.
+ * @throws IOException If an error occurred while reading the test file.
+ */
+ @Test
+ @DependsOnMethod("testGetAsImageInputStream")
+ public void testGetAsInputStream() throws DataStoreException, IOException {
+ StorageConnector connection = create(true);
+ InputStream in = connection.getStorageAs(InputStream.class);
+ assertSame(connection.getStorage(), in);
+ connection.closeAllExcept(null);
+
+ connection = create(false);
+ in = connection.getStorageAs(InputStream.class);
+ assertNotSame(connection.getStorage(), in);
+ assertSame("Expected cached value.", in,
connection.getStorageAs(InputStream.class));
+ assertInstanceOf("Expected Channel backend", InputStreamAdapter.class,
in);
+ assertInstanceOf("Expected Channel backend",
ChannelImageInputStream.class, ((InputStreamAdapter) in).input);
+ assertSame(((InputStreamAdapter) in).input,
connection.getStorageAs(DataInput.class));
+ assertSame(((InputStreamAdapter) in).input,
connection.getStorageAs(ImageInputStream.class));
+
+ final ReadableByteChannel channel = ((ChannelImageInputStream)
((InputStreamAdapter) in).input).channel;
+ assertTrue(channel.isOpen());
+ connection.closeAllExcept(null);
+ assertFalse(channel.isOpen());
+ }
+
+ /**
+ * Tests the {@link StorageConnector#getStorageAs(Class)} method for the
{@link Reader} type.
+ *
+ * @throws DataStoreException Should never happen.
+ * @throws IOException If an error occurred while reading the test file.
+ */
+ @Test
+ @DependsOnMethod("testGetAsInputStream")
+ public void testGetAsReader() throws DataStoreException, IOException {
+ StorageConnector connection = create(true);
+ final Reader in = connection.getStorageAs(Reader.class);
+ assertSame("Expected cached value.", in,
connection.getStorageAs(Reader.class));
+ connection.closeAllExcept(null);
+ }
+
+ /**
* Tests the {@link StorageConnector#getStorageAs(Class)} method for the
{@link ChannelDataInput} type.
* The initial value should not be an instance of {@link
ChannelImageInputStream} in order to avoid initializing
* the Image I/O classes. However after a call to {@code
getStorageAt(ChannelImageInputStream.class)}, the type
@@ -213,7 +259,7 @@ public final strictfp class StorageConne
final ByteBuffer buffer = connection.getStorageAs(ByteBuffer.class);
assertNotNull("getStorageAs(ByteBuffer.class)", buffer);
- assertTrue(buffer.capacity() < StorageConnector.DEFAULT_BUFFER_SIZE);
+ assertEquals(StorageConnector.MINIMAL_BUFFER_SIZE, buffer.capacity());
assertEquals(MAGIC_NUMBER, buffer.getInt());
connection.closeAllExcept(null);
}