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 2013/05/31 15:50:46 UTC
svn commit: r1488226 - in /sis/branches/JDK7:
core/sis-utility/src/main/java/org/apache/sis/util/resources/
storage/sis-storage/src/main/java/org/apache/sis/storage/
Author: desruisseaux
Date: Fri May 31 13:50:46 2013
New Revision: 1488226
URL: http://svn.apache.org/r1488226
Log:
One more refactoring: uses a more dynamic approach for producing views of different kinds.
Modified:
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-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java
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=1488226&r1=1488225&r2=1488226&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] Fri May 31 13:50:46 2013
@@ -89,6 +89,11 @@ public final class Errors extends Indexe
public static final int CanNotInstantiate_1 = 81;
/**
+ * Can not open “{0}”.
+ */
+ public static final int CanNotOpen_1 = 97;
+
+ /**
* Can not parse “{1}” as a file in the {0} format.
*/
public static final int CanNotParseFile_2 = 79;
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=1488226&r1=1488225&r2=1488226&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] Fri May 31 13:50:46 2013
@@ -29,6 +29,7 @@ CanNotConvertFromType_2 = Can no
CanNotConvertValue_2 = Can not convert value \u201c{0}\u201d to type \u2018{1}\u2019.
CanNotComputeDerivative = Can not compute the derivative.
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.
CanNotSetPropertyValue_1 = Can not set a value for property \u201c{0}\u201d.
ClassNotFinal_1 = Class \u2018{0}\u2019 is not final.
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=1488226&r1=1488225&r2=1488226&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] Fri May 31 13:50:46 2013
@@ -19,6 +19,7 @@ CanNotConvertFromType_2 = Ne peu
CanNotConvertValue_2 = La valeur \u201c{0}\u201d ne peut pas \u00eatre convertie vers le type \u2018{1}\u2019.
CanNotComputeDerivative = La d\u00e9riv\u00e9 ne peut pas \u00eatre calcul\u00e9e.
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}.
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.
Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java?rev=1488226&r1=1488225&r2=1488226&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java [UTF-8] Fri May 31 13:50:46 2013
@@ -16,6 +16,10 @@
*/
package org.apache.sis.storage;
+import java.util.Map;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.ConcurrentModificationException;
import java.io.DataInput;
import java.io.IOException;
import java.io.Serializable;
@@ -24,7 +28,6 @@ import java.nio.channels.ReadableByteCha
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.sql.Connection;
-import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.sis.util.Classes;
import org.apache.sis.util.ArgumentChecks;
@@ -50,10 +53,6 @@ import org.apache.sis.internal.storage.C
* Once a suitable {@code DataStore} has been found, the {@code DataStoreConnection} instance is typically
* discarded since each data store implementation will use their own input/output objects.
*
- * <p>This class does not implement {@link AutoCloseable} on intend, because the connection shall not be closed
- * if it has been taken by a {@link DataStore} instance. The connection shall be closed only if no suitable
- * {@code DataStore} has been found.</p>
- *
* <p>Instances of this class are serializable if the {@code storage} object given at construction time
* is serializable.</p>
*
@@ -88,45 +87,37 @@ public class DataStoreConnection impleme
private transient String extension;
/**
- * A read-only view of the buffer over the first bytes of the stream, or {@code null} if none.
- * This field is initialized together with {@link #asDataInput} when first needed.
+ * Views of {@link #storage} as some of the following supported types:
*
- * @see #asByteBuffer()
- */
- private transient ByteBuffer asByteBuffer;
-
- /**
- * The input as a data input stream, or {@code null} if none.
- * This field is initialized together with {@link #asByteBuffer} when first needed.
+ * <ul>
+ * <li>{@link ByteBuffer}:
+ * A read-only view of the buffer over the first bytes of the stream.</li>
*
- * <p>Unless the {@link #storage} is already an instance of {@link DataInput}, this field will be
- * given an instance of {@link ChannelImageInputStream} if possible rather than an arbitrary stream.
- * In particular, we invoke the {@link ImageIO#createImageInputStream(Object)} factory method only in
- * last resort because some SIS data stores will want to access the channel and buffer directly.</p>
+ * <li>{@link DataInput}:
+ * The input as a data input stream. Unless the {@link #storage} is already an instance of {@link DataInput},
+ * this entry will be given an instance of {@link ChannelImageInputStream} if possible rather than an arbitrary
+ * stream. In particular, we invoke the {@link ImageIO#createImageInputStream(Object)} factory method only in
+ * last resort because some SIS data stores will want to access the channel and buffer directly.</li>
*
- * @see #asDataInput()
+ * <li>{@link Connection}:
+ * The input/output object as a JDBC connection.</li>
+ * </ul>
+ *
+ * A non-existent entry means that the value has not yet been computed. A {@link Void#TYPE} value means the value
+ * has been computed and we have determined that {@link #viewAs(Class)} shall returns {@code null} for that type.
+ *
+ * @see #viewAs(Class)
*/
- private transient DataInput asDataInput;
+ private transient Map<Class<?>, Object> views;
/**
- * If {@link #asDataInput} is an instance of {@link ImageInputStream}, then the stream position
- * at the time the {@code asDataInput} field has been initialized. This is often zero.
+ * If an {@link ImageInputStream} vie exists, then the stream position at the time the view has been initialized.
+ * This is usually zero, but could be different if the {@link #storage} provided by the users is already an instance
+ * of {@code ImageInputStream}.
*/
private transient long streamOrigin;
/**
- * {@code true} if {@link #asDataInput} and {@link #asByteBuffer} have been initialized.
- */
- private transient boolean isInitialized;
-
- /**
- * The input/output object as a JDBC connection.
- *
- * @see #asDatabase()
- */
- private transient Connection asDatabase;
-
- /**
* Creates a new data store connection wrapping the given input/output object.
* The object can be of any type, but the class javadoc lists the most typical ones.
*
@@ -195,90 +186,101 @@ public class DataStoreConnection impleme
}
/**
- * Returns a read-only view of the first bytes of the input stream, or {@code null} if unavailable.
- * If non-null, this buffer can be used for a quick check of file magic number.
- *
- * @return The first bytes in the stream (read-only), or {@code null} if unavailable.
- * @throws IOException If an error occurred while opening a stream for the input.
- */
- public ByteBuffer asByteBuffer() throws IOException {
- if (!isInitialized) {
- initialize();
- }
- return asByteBuffer;
- }
-
- /**
- * Returns the input as a {@link DataInput} if possible, or {@code null} otherwise.
- * The default implementation performs the following choice based on the type of the
- * {@linkplain #getStorage() storage} object:
+ * Returns the storage as a view of the given type if possible, or {@code null} otherwise.
+ * The default implementation accepts the following types:
*
* <ul>
- * <li>If the storage is already an instance of {@link 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.nio.channels.ReadableByteChannel}, then an {@link ImageInputStream} backed by a
- * {@link ByteBuffer} is created when first needed and returned.</li>
+ * <li><p>{@link ByteBuffer}:
+ * A read-only view of the first bytes of the input stream, or {@code null} if unavailable.
+ * If non-null, then the buffer can be used for a quick check of file magic number.</p></li>
+ *
+ * <li><p>{@link DataInput}:
+ * Performs the following choice based on the type of the {@linkplain #getStorage() storage} object:
+ * <ul>
+ * <li>If the storage is already an instance of {@link 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.nio.channels.ReadableByteChannel}, then an {@link ImageInputStream} backed by a
+ * {@link ByteBuffer} is created when first needed and returned.</li>
+ *
+ * <li>Otherwise if {@link ImageIO#createImageInputStream(Object)} returns a non-null value,
+ * then this value is cached and returned.</li>
+ *
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul></p></li>
+ *
+ * <li><p>{@link DataInput}:
+ * Performs the following choice based on the type of the {@linkplain #getStorage() storage} object:
+ * <ul>
+ * <li>If the storage is already an instance of {@link Connection}, then it is returned unchanged.</li>
*
- * <li>Otherwise if {@link ImageIO#createImageInputStream(Object)} returns a non-null value,
- * then this value is cached and returned.</li>
+ * <li>Otherwise if the storage is an instance of {@link DataSource}, then a connection is obtained
+ * when first needed and returned.</li>
*
- * <li>Otherwise this method returns {@code null}.</li>
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul></p></li>
* </ul>
*
- * Multiple invocations of this method on the same {@code DataStoreConnection} instance will return the same
- * {@code ImageInputStream} instance.
+ * Multiple invocations of this method on the same {@code DataStoreConnection} instance will return the same view
+ * instance. Consequently, callers shall not close the stream or database connection returned by this method.
*
- * @return The input as a {@code DataInput}, or {@code null} if the input is an object of unknown type.
- * @throws IOException If an error occurred while opening a stream for the input.
- */
- public DataInput asDataInput() throws IOException {
- if (!isInitialized) {
- initialize();
+ * @param <T> The compile-time type of the {@code type} argument.
+ * @param type The type of desired view, as one of {@code ByteBuffer}, {@code DataInput}, {@code Connection}
+ * class or other type supported by {@code DataStoreConnection} subclasses.
+ * @return The storage as a view of the given type, or {@code null} if no view can be created for the given type.
+ * @throws IllegalArgumentException If the given {@code type} argument is not a known type.
+ * @throws DataStoreException If an error occurred while opening a stream or database connection.
+ */
+ public <T> T viewAs(final Class<T> type) throws IllegalArgumentException, DataStoreException {
+ ArgumentChecks.ensureNonNull("type", type);
+ if (views != null) {
+ final Object view = views.get(type);
+ if (view != null) {
+ return (view != Void.TYPE) ? type.cast(view) : null;
+ }
+ } else {
+ views = new IdentityHashMap<>();
}
- return asDataInput;
- }
-
- /**
- * Returns the input as a connection to a JDBC database if possible, or {@code null} otherwise.
- * The default implementation performs the following choice based on the type of the
- * {@linkplain #getStorage() storage} object:
- *
- * <ul>
- * <li>If the storage is already an instance of {@link Connection}, then it is returned unchanged.</li>
- *
- * <li>Otherwise if the storage is an instance of {@link DataSource}, then a connection is obtained
- * when first needed and returned.</li>
- *
- * <li>Otherwise this method returns {@code null}.</li>
- * </ul>
- *
- * Multiple invocations of this method on the same {@code DataStoreConnection} instance will return the same
- * {@code Connection} instance.
- *
- * @return The storage as a {@code Connection}, or {@code null} if the storage is an object of unknown type.
- * @throws SQLException If an error occurred while opening a database connection for the storage.
- */
- public Connection asDatabase() throws SQLException {
- if (asDatabase == null) {
- if (storage instanceof Connection) {
- asDatabase = (Connection) storage;
- } else if (storage instanceof DataSource) {
- asDatabase = ((DataSource) storage).getConnection();
+ /*
+ * Special case for DataInput and ByteBuffer, because those values are created together.
+ * In addition, ImageInputStream creation assigns a value to the 'streamOrigin' field.
+ */
+ if (type == DataInput.class || type == ByteBuffer.class) {
+ try {
+ createDataInput();
+ } catch (IOException e) {
+ throw new DataStoreException(Errors.format(Errors.Keys.CanNotOpen_1, getStorageName()), e);
}
+ return getView(type);
+ }
+ /*
+ * All other cases.
+ */
+ final Object value;
+ try {
+ value = createView(type);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new DataStoreException(Errors.format(Errors.Keys.CanNotOpen_1, getStorageName()), e);
}
- return asDatabase;
+ final T view = type.cast(value);
+ addView(type, view);
+ return view;
}
/**
- * Initializes I/O part, namely the {@link #asDataInput} and {@link #asByteBuffer} fields.
- * Note that some or all of those fields may still be null after this method call.
+ * Creates a view for the input as a {@link DataInput} if possible. This method performs the choice
+ * documented in the {@link #viewAs(Class)} method for the {@code DataInput} case, and create the
+ * {@code ByteBuffer} in same time if possible.
*
- * @see #rewind()
+ * @throws IOException If an error occurred while opening a stream for the input.
*/
- private void initialize() throws IOException {
+ private void createDataInput() throws IOException {
+ final DataInput asDataInput;
+ ByteBuffer asByteBuffer = null;
if (storage instanceof DataInput) {
asDataInput = (DataInput) storage;
} else {
@@ -299,7 +301,54 @@ public class DataStoreConnection impleme
if (asDataInput instanceof ImageInputStream) {
streamOrigin = ((ImageInputStream) asDataInput).getStreamPosition();
}
- isInitialized = true;
+ addView(ByteBuffer.class, asByteBuffer);
+ addView(DataInput.class, asDataInput);
+ }
+
+ /**
+ * Creates a storage view of the given type if possible, or returns {@code null} otherwise.
+ * This method is invoked by {@link #viewAs(Class)} when first needed, and the result is cached.
+ *
+ * @param <T> The compile-time type of the {@code type} argument.
+ * @param type The type of desired view.
+ * @return The storage as a view of the given type, or {@code null} if no view can be created for the given type.
+ * @throws IllegalArgumentException If the given {@code type} argument is not a known type.
+ * @throws Exception If an error occurred while opening a stream or database connection.
+ */
+ private Object createView(final Class<?> type) throws IllegalArgumentException, Exception {
+ if (type == Connection.class) {
+ if (storage instanceof Connection) {
+ return storage;
+ } else if (storage instanceof DataSource) {
+ return ((DataSource) storage).getConnection();
+ }
+ }
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.UnknownType_1, type));
+ }
+
+ /**
+ * Adds the given view in the caches.
+ *
+ * @param <T> The compile-time type of the {@code type} argument.
+ * @param type The view type.
+ * @param view The view, or {@code null} if none.
+ */
+ private <T> void addView(final Class<T> type, final T view) {
+ if (views.put(type, (view != null) ? view : Void.TYPE) != null) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ /**
+ * Returns the view for the given type from the caches.
+ *
+ * @param <T> The compile-time type of the {@code type} argument.
+ * @param type The view type.
+ * @return The view, or {@code null} if none.
+ */
+ private <T> T getView(final Class<T> type) {
+ final Object view = views.get(type);
+ return (view != Void.TYPE) ? type.cast(view) : null;
}
/**
@@ -307,56 +356,67 @@ public class DataStoreConnection impleme
* This method is invoked when more than one {@link DataStore} instance is tried in search
* for a data store that accept this {@code DataStoreInput} instance.
*
- * <p>In the default implementation, this method does nothing if {@link #asDataInput()}
- * returns {@code null}.</p>
- *
* @throws DataStoreException If the stream is open but can not be rewinded.
*/
public void rewind() throws DataStoreException {
- /*
- * Restores the ImageInputStream to its original position if possible. Note that in
- * the ChannelImageInputStream, this may reload the buffer content if necessary.
- */
- if (asDataInput instanceof ImageInputStream) try {
- ((ImageInputStream) asDataInput).seek(streamOrigin);
- } catch (IOException | IndexOutOfBoundsException e) {
- throw new DataStoreException(Errors.format(Errors.Keys.StreamIsForwardOnly_1, getStorageName()), e);
- }
- /*
- * Copy the position and limits from the buffer. Note that this copy must be performed after the
- * above 'seek', because the seek operation may have modified the buffer position and limit.
- */
- if (asByteBuffer != null && asDataInput instanceof ChannelImageInputStream) {
- final ByteBuffer buffer = ((ChannelImageInputStream) asDataInput).buffer;
- asByteBuffer.clear().limit(buffer.limit()).position(buffer.position());
- asByteBuffer.order(buffer.order());
+ if (views != null) {
+ /*
+ * Restores the ImageInputStream to its original position if possible. Note that in
+ * the ChannelImageInputStream, this may reload the buffer content if necessary.
+ */
+ final DataInput asDataInput = getView(DataInput.class);
+ if (asDataInput instanceof ImageInputStream) try {
+ ((ImageInputStream) asDataInput).seek(streamOrigin);
+ } catch (IOException | IndexOutOfBoundsException e) {
+ throw new DataStoreException(Errors.format(Errors.Keys.StreamIsForwardOnly_1, getStorageName()), e);
+ }
+ /*
+ * Copy the position and limits from the buffer. Note that this copy must be performed after the
+ * above 'seek', because the seek operation may have modified the buffer position and limit.
+ */
+ final ByteBuffer asByteBuffer = getView(ByteBuffer.class);
+ if (asByteBuffer != null && asDataInput instanceof ChannelImageInputStream) {
+ final ByteBuffer buffer = ((ChannelImageInputStream) asDataInput).buffer;
+ asByteBuffer.clear().limit(buffer.limit()).position(buffer.position());
+ asByteBuffer.order(buffer.order());
+ }
}
}
/**
- * Closes all streams and connections created by this object, and closes the storage it is closeable.
- * This method closes the objects created by {@link #asDataInput()} and {@link #asDatabase()}, if any,
- * then closes the {@linkplain #getStorage() storage} if it is closeable.
- *
- * <p>This method shall be invoked <strong>only</strong> if no {@link DataStore} accepted this input.
- * Invoking this method in a {@code try} … {@code finally} block is usually not appropriate.</p>
+ * Closes all streams and connections created by this {@code DataStoreConnection} except the given view.
+ * This method closes all objects created by the {@link #viewAs(Class)} method, leaving only {@code view} open.
+ * If {@code view} is {@code null}, then this method closes everything including the {@linkplain #getStorage()
+ * storage} if it is closeable.
+ *
+ * <p>This method is invoked when a suitable {@link DataStore} has been found - in which case the view used
+ * by the data store is given in argument to this method - or when no suitable {@code DataStore} has been
+ * found - in which case the {@code view} argument is null.</p>
*
+ * @param view The view to leave open, or {@code null} if none.
* @throws DataStoreException If an error occurred while closing the stream or database connection.
*/
- public void close() throws DataStoreException {
+ public void closeAllExcept(final Object view) throws DataStoreException {
+ final Map<Class<?>, Object> map = views;
+ views = Collections.emptyMap();
+ boolean close = (view == null);
try {
- if (asDatabase != null) {
- asDatabase.close();
- }
- if (asDataInput instanceof AutoCloseable) {
- ((AutoCloseable) asDataInput).close();
+ for (final Object value : map.values()) {
+ if (value != view) {
+ if (value instanceof AutoCloseable) {
+ ((AutoCloseable) value).close();
+ close = false;
+ }
+ }
/*
* On JDK6, ImageInputStream does not extend Closeable and must
* be checked explicitely. On JDK7, this is not needed anymore.
+ * Likewise, Connection extends AutoCloseable only in JDK7.
*/
- } else if (storage instanceof AutoCloseable) {
+ }
+ if (close && storage instanceof AutoCloseable) {
/*
- * Close only if we didn't closed 'asDataInput', because closing 'asDataInput'
+ * 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.
@@ -365,10 +425,6 @@ public class DataStoreConnection impleme
}
} catch (Exception e) {
throw new DataStoreException(e);
- } finally {
- asDatabase = null;
- asDataInput = null;
- asByteBuffer = null;
}
}