sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1720367 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/factory/ sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/ sis-utility/src/main/java/org/apache/sis/internal/system/ sis-uti...
Date Wed, 16 Dec 2015 15:26:55 GMT
Author: desruisseaux
Date: Wed Dec 16 15:26:55 2015
New Revision: 1720367

URL: http://svn.apache.org/viewvc?rev=1720367&view=rev
Log:
Provide a shutdown hook for ConcurrentAuthorityFactory to be executed either when garbage collected,
at JVM shutdown time or when the OSGi or Servlet container uninstall the bundle.

Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/ServletListener.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Shutdown.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/About.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Disposable.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -22,8 +22,10 @@ import java.util.Deque;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.WeakHashMap;
+import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
 import java.lang.ref.WeakReference;
+import java.lang.ref.PhantomReference;
 import java.io.PrintWriter;
 import javax.measure.unit.Unit;
 import org.opengis.referencing.cs.*;
@@ -45,10 +47,13 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.collection.Cache;
 import org.apache.sis.internal.referencing.NilReferencingObject;
+import org.apache.sis.internal.system.ReferenceQueueConsumer;
 import org.apache.sis.internal.system.DelayedExecutor;
 import org.apache.sis.internal.system.DelayedRunnable;
+import org.apache.sis.internal.system.Shutdown;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.ArraysExt;
 
 
 /**
@@ -58,8 +63,8 @@ import org.apache.sis.util.resources.Err
  * by {@link #createBackingStore()} and the result is cached in this factory.
  *
  * <p>{@code ConcurrentAuthorityFactory} delays the call to {@code createBackingStore()} until first needed,
- * and {@linkplain Disposable#dispose() dispose} it after some timeout. This approach allows to establish
- * a connection to a database (for example) and keep it only for a relatively short amount of time.</p>
+ * and {@linkplain AutoCloseable#close() closes} the backing store after some timeout. This approach allows
+ * to establish a connection to a database (for example) and keep it only for a relatively short amount of time.</p>
  *
  * <div class="section">Caching strategy</div>
  * Objects are cached by strong references, up to the amount of objects specified at construction time.
@@ -85,7 +90,7 @@ import org.apache.sis.util.resources.Err
  * @version 0.7
  * @module
  */
-public abstract class ConcurrentAuthorityFactory extends GeodeticAuthorityFactory implements Disposable {
+public abstract class ConcurrentAuthorityFactory extends GeodeticAuthorityFactory implements AutoCloseable {
     /**
      * The authority, cached after first requested.
      */
@@ -117,7 +122,7 @@ public abstract class ConcurrentAuthorit
      * {@link ConcurrentAuthorityFactory}. This assumes that factory implementations are reentrant.</p>
      *
      * <p>If the backing store has been released, then {@code BackingStore} keep the release timestamp.
-     * This information is used for prioritize the backing stores to dispose.</p>
+     * This information is used for prioritize the backing stores to close.</p>
      */
     private static final class BackingStore {
         /**
@@ -183,7 +188,7 @@ public abstract class ConcurrentAuthorit
     private int remainingBackingStores;
 
     /**
-     * {@code true} if the call to {@link #disposeExpired()} is scheduled for future execution in the background
+     * {@code true} if the call to {@link #closeExpired()} is scheduled for future execution in the background
      * cleaner thread.  A value of {@code true} implies that this factory contains at least one active backing store.
      * However the reciprocal is not true: this field may be set to {@code false} while a worker factory is currently
      * in use because this field is set to {@code true} only when a worker factory is {@linkplain #release() released}.
@@ -196,11 +201,11 @@ public abstract class ConcurrentAuthorit
     private boolean isActive;
 
     /**
-     * {@code true} if {@link #dispose()} has been invoked.
+     * {@code true} if {@link #close()} has been invoked.
      *
      * <p>Every access to this field must be performed in a block synchronized on {@link #availableStores}.</p>
      */
-    private boolean isDisposed;
+    private boolean isClosed;
 
     /**
      * The delay of inactivity (in nanoseconds) before to close a backing store.
@@ -250,13 +255,23 @@ public abstract class ConcurrentAuthorit
         ArgumentChecks.ensureStrictlyPositive("maxConcurrentQueries", maxConcurrentQueries);
         remainingBackingStores = maxConcurrentQueries;
         cache = new Cache<>(20, maxStrongReferences, false);
-        cache.setKeyCollisionAllowed(true);
         /*
          * Key collision is usually an error. But in this case we allow them in order to enable recursivity.
          * If during the creation of an object the program asks to this ConcurrentAuthorityFactory for the same
          * object (using the same key), then the default Cache implementation considers that situation as an
          * error unless the above property has been set to 'true'.
          */
+        cache.setKeyCollisionAllowed(true);
+        /*
+         * The shutdown hook serves two purposes:
+         *
+         *   1) Closes the backing stores when the garbage collector determined
+         *      that this ConcurrentAuthorityFactory is no longer in use.
+         *
+         *   2) Closes the backing stores at JVM shutdown time if the application is standalone,
+         *      or when the bundle is uninstalled if running inside an OSGi or Servlet container.
+         */
+        Shutdown.register(new ShutdownHook(this));
     }
 
     /**
@@ -273,7 +288,7 @@ public abstract class ConcurrentAuthorit
     /**
      * Creates a new backing store authority factory. This method is invoked the first time a {@code createFoo(String)}
      * method is invoked. It may also be invoked again if additional factories are needed in different threads,
-     * or if all factories have been disposed after the timeout.
+     * or if all factories have been closed after the timeout.
      *
      * <div class="section">Multi-threading</div>
      * This method (but not necessarily the returned factory) needs to be thread-safe;
@@ -304,7 +319,7 @@ public abstract class ConcurrentAuthorit
         BackingStore usage = currentStore.get();
         if (usage == null) {
             synchronized (availableStores) {
-                if (isDisposed) {
+                if (isClosed) {
                     throw new UnavailableFactoryException(Errors.format(Errors.Keys.DisposedFactory));
                 }
                 /**
@@ -374,17 +389,25 @@ public abstract class ConcurrentAuthorit
         final BackingStore usage = currentStore.get();     // A null value here would be an error in our algorithm.
         if (--usage.depth == 0) {
             synchronized (availableStores) {
-                if (isDisposed) return;
+                if (isClosed) {
+                    final GeodeticAuthorityFactory factory = usage.factory;
+                    if (factory instanceof AutoCloseable) try {
+                        ((AutoCloseable) factory).close();
+                    } catch (Exception exception) {
+                        unexpectedException("release", exception);
+                    }
+                    return;
+                }
                 remainingBackingStores++;       // Must be done first in case an exception happen after this point.
                 usage.timestamp = System.nanoTime();
                 availableStores.addLast(usage);
                 /*
                  * If the backing store we just released is the first one, awake the
-                 * disposer thread which was waiting for an indefinite amount of time.
+                 * cleaner thread which was waiting for an indefinite amount of time.
                  */
                 if (!isActive) {
                     isActive = true;
-                    DelayedExecutor.schedule(new DisposeTask(usage.timestamp + timeout));
+                    DelayedExecutor.schedule(new CloseTask(usage.timestamp + timeout));
                 }
                 availableStores.notify();    // We released only one backing store, so awake only one thread - not all of them.
             }
@@ -393,42 +416,42 @@ public abstract class ConcurrentAuthorit
     }
 
     /**
-     * A task for invoking {@link ConcurrentAuthorityFactory#disposeExpired()} after a delay.
+     * A task for invoking {@link ConcurrentAuthorityFactory#closeExpired()} after a delay.
      */
-    private final class DisposeTask extends DelayedRunnable {
+    private final class CloseTask extends DelayedRunnable {
         /**
          * Creates a new task to be executed at the given time,
          * in nanoseconds relative to {@link System#nanoTime()}.
          */
-        DisposeTask(final long timestamp) {
+        CloseTask(final long timestamp) {
             super(timestamp);
         }
 
         /** Invoked when the delay expired. */
         @Override public void run() {
-            disposeExpired();
+            closeExpired();
         }
     }
 
     /**
-     * Disposes the expired backing stores. This method should be invoked from a background task only.
-     * This method may reschedule the task again for an other execution if it appears that at least one
-     * backing store was not ready for disposal.
+     * Closes the expired backing stores. This method should be invoked from a background task only.
+     * This method may reschedule the task again for an other execution if it appears that at least
+     * one backing store was not ready for disposal.
      *
-     * @see #dispose()
+     * @see #close()
      */
-    final void disposeExpired() {
+    final void closeExpired() {
         int count = 0;
-        final Disposable[] toDispose;
+        final AutoCloseable[] factories;
         synchronized (availableStores) {
-            toDispose = new Disposable[availableStores.size()];
+            factories = new AutoCloseable[availableStores.size()];
             final Iterator<BackingStore> it = availableStores.iterator();
             final long nanoTime = System.nanoTime();
             while (it.hasNext()) {
                 final BackingStore store = it.next();
                 /*
-                 * Computes how much time we need to wait again before we can dispose the factory.
-                 * If this time is greater than some arbitrary amount, do not dispose the factory
+                 * Computes how much time we need to wait again before we can close the factory.
+                 * If this time is greater than some arbitrary amount, do not close the factory
                  * and wait again.
                  */
                 final long nextTime = store.timestamp + timeout;
@@ -437,20 +460,20 @@ public abstract class ConcurrentAuthorit
                      * Found a factory which is not expired. Stop the search,
                      * since the iteration is expected to be ordered.
                      */
-                    DelayedExecutor.schedule(new DisposeTask(nextTime));
+                    DelayedExecutor.schedule(new CloseTask(nextTime));
                     break;
                 }
                 /*
                  * Found an expired factory. Adds it to the list of
-                 * factories to dispose and search for other factories.
+                 * factories to close and search for other factories.
                  */
                 it.remove();
-                if (store.factory instanceof Disposable) {
-                    toDispose[count++] = (Disposable) store.factory;
+                if (store.factory instanceof AutoCloseable) {
+                    factories[count++] = (AutoCloseable) store.factory;
                 }
             }
             /*
-             * The stores list is empty if all worker factories in the queue have been disposed.
+             * The stores list is empty if all worker factories in the queue have been closed.
              * Note that some worker factories may still be active outside the queue, because the
              * workers are added to the queue only after completion of their work.
              * In the later case, release() will reschedule a new task.
@@ -458,15 +481,31 @@ public abstract class ConcurrentAuthorit
             isActive = !availableStores.isEmpty();
         }
         /*
-         * We must dispose the factories from outside the synchronized block.
+         * We must close the factories from outside the synchronized block.
          */
-        while (--count >= 0) {
-            toDispose[count].dispose();
+        try {
+            close(factories, count);
+        } catch (Exception exception) {
+            unexpectedException("closeExpired", exception);
         }
     }
 
     /**
-     * Returns the amount of time that {@code ConcurrentAuthorityFactory} will wait before to dispose a backing store.
+     * Invoked when an exception occurred while closing a factory and we can not propagate the exception to the user.
+     * This situation happen when the factories are closed in a background thread. There is not much we can do except
+     * logging the problem. {@code ConcurrentAuthorityFactory} should be able to continue its work normally since the
+     * factory that we failed to close will not be used anymore.
+     *
+     * @param method     The name of the method to report as the source of the problem.
+     * @param exception  The exception that occurred while closing a backing store factory.
+     */
+    static void unexpectedException(final String method, final Exception exception) {
+        Logging.unexpectedException(Logging.getLogger(Loggers.CRS_FACTORY),
+                ConcurrentAuthorityFactory.class, method, exception);
+    }
+
+    /**
+     * Returns the amount of time that {@code ConcurrentAuthorityFactory} will wait before to close a backing store.
      * This delay is measured from the last time the backing store has been used by a {@code createFoo(String)} method.
      *
      * @param unit The desired unit of measurement for the timeout.
@@ -479,7 +518,7 @@ public abstract class ConcurrentAuthorit
     }
 
     /**
-     * Sets a timer for disposing the backing store after the specified amount of time of inactivity.
+     * Sets a timer for closing the backing store after the specified amount of time of inactivity.
      * If a new backing store is needed after the disposal of the last one, then the {@link #createBackingStore()}
      * method will be invoked again.
      *
@@ -490,7 +529,7 @@ public abstract class ConcurrentAuthorit
         ArgumentChecks.ensureStrictlyPositive("delay", delay);
         delay = unit.toNanos(delay);
         synchronized (availableStores) {
-            timeout = delay;                // Will be taken in account after the next factory to dispose.
+            timeout = delay;                // Will be taken in account after the next factory to close.
         }
     }
 
@@ -1595,32 +1634,130 @@ public abstract class ConcurrentAuthorit
     }
 
     /**
-     * Releases resources immediately instead of waiting for the garbage collector.
-     * Once a factory has been disposed, further {@code createFoo(String)} method invocations
-     * may throw a {@link FactoryException}. Disposing a previously-disposed factory, however, has no effect.
+     * A hook to be executed either when the {@link ConcurrentAuthorityFactory} is collected by the garbage collector,
+     * when the Java Virtual Machine is shutdown, or when the module is uninstalled by the OSGi or Servlet container.
+     *
+     * <p><strong>Do not keep reference to the enclosing factory</strong> - in particular,
+     * this class must not be static - otherwise the factory would never been garbage collected.</p>
+     */
+    private static final class ShutdownHook extends PhantomReference<ConcurrentAuthorityFactory>    // MUST be static!
+            implements Disposable, Callable<Object>
+    {
+        /**
+         * The {@link ConcurrentAuthorityFactory#availableStores} queue.
+         */
+        private final Deque<BackingStore> availableStores;
+
+        /**
+         * Creates a new shutdown hook for the given factory.
+         */
+        ShutdownHook(final ConcurrentAuthorityFactory factory) {
+            super(factory, ReferenceQueueConsumer.QUEUE);
+            availableStores = factory.availableStores;
+        }
+
+        /**
+         * Invoked indirectly by the garbage collector when the {@link ConcurrentAuthorityFactory} is disposed.
+         */
+        @Override
+        public void dispose() {
+            Shutdown.unregister(this);
+            try {
+                call();
+            } catch (Exception exception) {
+                /*
+                 * Pretend that the exception is logged by ConcurrentAuthorityFactory.finalize().
+                 * This is not true, but carries the idea that the error occurred while cleaning
+                 * ConcurrentAuthorityFactory after garbage collection.
+                 */
+                unexpectedException("finalize", exception);
+            }
+        }
+
+        /**
+         * Invoked at JVM shutdown time, or when the container (OSGi or Servlet) uninstall the bundle containing SIS.
+         */
+        @Override
+        public Object call() throws Exception {
+            final AutoCloseable[] factories;
+            synchronized (availableStores) {
+                factories = getCloseables(availableStores);
+            }
+            close(factories, factories.length);
+            return null;
+        }
+    }
+
+    /**
+     * Returns all factories implementing the {@link AutoCloseable} interfaces in the given queue.
+     * The given queue shall be the {@link ConcurrentAuthorityFactory#availableStores} queue.
+     *
+     * @param availableStores The queue of factories to close.
+     */
+    static AutoCloseable[] getCloseables(final Deque<BackingStore> availableStores) {
+        assert Thread.holdsLock(availableStores);
+        int count = 0;
+        final AutoCloseable[] factories = new AutoCloseable[availableStores.size()];
+        BackingStore store;
+        while ((store = availableStores.pollFirst()) != null) {
+            if (store.factory instanceof AutoCloseable) {
+                factories[count++] = (AutoCloseable) store.factory;
+            }
+        }
+        return ArraysExt.resize(factories, count);
+    }
+
+    /**
+     * Invokes {@link AutoCloseable#close()} on all the given factories.
+     * Exceptions will be collected and rethrown only after all factories have been closed.
+     *
+     * @param  factories The factories to close.
+     * @param  count Number of valid elements in the {@code factories} array.
+     * @throws Exception the exception thrown by the first factory that failed to close.
+     */
+    static void close(final AutoCloseable[] factories, int count) throws Exception {
+        Exception exception = null;
+        while (--count >= 0) try {
+            factories[count].close();
+        } catch (Exception e) {
+            if (exception == null) {
+                exception = e;
+            } else {
+                exception.addSuppressed(e);
+            }
+        }
+        if (exception != null) {
+            throw exception;
+        }
+    }
+
+    /**
+     * Releases resources immediately instead of waiting for the garbage collector. Once a factory has been closed,
+     * further {@code createFoo(String)} method invocations will throw a {@link FactoryException}.
+     * Closing a previously-closed factory, however, has no effect.
+     *
+     * <p>If this method is not invoked, backing stores will be closed when this {@code ConcurrentAuthorityFactory}
+     * will be garbage collected or at JVM shutdown time, depending which event happen first.</p>
+     *
+     * @throws FactoryException if an error occurred while closing the backing stores.
      */
     @Override
-    public void dispose() {
+    public void close() throws FactoryException {
+        Exception exception = null;
         try {
-            int count = 0;
-            final Disposable[] factories;
+            final AutoCloseable[] factories;
             synchronized (availableStores) {
-                isDisposed = true;
+                isClosed = true;
                 remainingBackingStores = 0;
-                factories = new Disposable[availableStores.size()];
-                BackingStore store;
-                while ((store = availableStores.pollFirst()) != null) {
-                    if (store.factory instanceof Disposable) {
-                        factories[count++] = (Disposable) store.factory;
-                    }
-                }
+                factories = getCloseables(availableStores);
             }
             /*
              * Factory disposal must be done outside the synchronized block.
+             * If an exception occurs, it will be thrown only after we finished closing all factories.
              */
-            while (--count >= 0) {
-                factories[count].dispose();
-            }
+            close(factories, factories.length);
+        } catch (Exception e) {
+            exception = e;
         } finally {
             synchronized (findPool) {
                 findPool.clear();
@@ -1628,5 +1765,12 @@ public abstract class ConcurrentAuthorit
             cache.clear();
             authority = null;
         }
+        if (exception != null) {
+            if (exception instanceof FactoryException) {
+                throw (FactoryException) exception;
+            } else {
+                throw new FactoryException(exception);
+            }
+        }
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -24,7 +24,6 @@ import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.PreparedStatement;
 import java.sql.Statement;
-import java.lang.ref.Reference;
 import org.opengis.referencing.operation.Projection;
 import org.opengis.util.NoSuchIdentifierException;
 import org.apache.sis.util.collection.BackingStoreException;
@@ -187,7 +186,7 @@ final class AuthorityCodes extends Abstr
      * when the garbage collector determined that this {@code AuthorityCodes} instance is no longer in use.
      * See class Javadoc for more information.
      */
-    final Reference<AuthorityCodes> createReference() {
+    final CloseableReference<AuthorityCodes> createReference() {
         return new CloseableReference<>(this, factory, statements);
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -57,10 +57,11 @@ final class CloseableReference<T> extend
     }
 
     /**
-     * Invoked indirectly by the garbage collector.
+     * Closes the statements. If an exception occurred, it will be thrown only after all statements have been closed.
+     *
+     * @throws SQLException if an error occurred while closing the statements.
      */
-    @Override
-    public void dispose() {
+    final void close() throws SQLException {
         SQLException exception = null;
         synchronized (factory) {
             for (int i=statements.length; --i >= 0;) {
@@ -78,6 +79,18 @@ final class CloseableReference<T> extend
             }
         }
         if (exception != null) {
+            throw exception;
+        }
+    }
+
+    /**
+     * Invoked indirectly by the garbage collector.
+     */
+    @Override
+    public void dispose() {
+        try {
+            close();
+        } catch (SQLException exception) {
             /*
              * There is nothing we can do here. It is not even worth to throw an unchecked exception because
              * this method is invoked from a background thread, so the exception would not reach user's code.

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -24,6 +24,7 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
@@ -37,7 +38,6 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.Statement;
 import java.sql.SQLException;
-import java.lang.ref.Reference;
 import java.text.DateFormat;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -86,7 +86,6 @@ import org.apache.sis.util.resources.Err
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
-import org.apache.sis.util.Disposable;
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.Version;
 import org.apache.sis.measure.Units;
@@ -108,7 +107,7 @@ import org.apache.sis.measure.Units;
  *
  * <div class="section">Life cycle and caching</div>
  * {@code EPSGFactory} instances should be short-lived since they may hold a significant amount of JDBC resources.
- * It is recommended to have those instances created on the fly by {@link ConcurrentAuthorityFactory} and disposed
+ * It is recommended to have those instances created on the fly by {@link ConcurrentAuthorityFactory} and closed
  * after a relatively short {@linkplain ConcurrentAuthorityFactory#getTimeout timeout}.
  * In addition {@code ConcurrentAuthorityFactory} caches the most recently created objects, which reduce greatly
  * the amount of {@code EPSGFactory} instantiations (and consequently the amount of database accesses)
@@ -133,7 +132,7 @@ import org.apache.sis.measure.Units;
  * @see <a href="http://sis.apache.org/book/tables/CoordinateReferenceSystems.html">List of authority codes</a>
  */
 public abstract class EPSGFactory extends GeodeticAuthorityFactory implements CRSAuthorityFactory,
-        CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory, Localized, Disposable
+        CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory, Localized, AutoCloseable
 {
     /**
      * The prefix in table names. The SQL scripts are provided by EPSG with this prefix in front of all table names.
@@ -236,18 +235,18 @@ public abstract class EPSGFactory extend
      * method as a cache for returning the set created in a previous call. We do not want this map to exist for
      * a long time anyway.
      *
-     * <p>Note that this {@code EPSGFactory} instance can not be disposed as long as this map is not empty, since
-     * {@link AuthorityCodes} caches some SQL statements and consequently require the {@linkplain #connection} to
-     * be open. This is why we use weak references rather than hard ones, in order to know when no
+     * <p>Note that this {@code EPSGFactory} instance can not be closed as long as this map is not empty, since
+     * {@link AuthorityCodes} caches some SQL statements and consequently require the {@linkplain #connection}
+     * to be open. This is why we use weak references rather than hard ones, in order to know when no
      * {@link AuthorityCodes} are still in use.</p>
      *
      * <p>The {@link CloseableReference#dispose()} method takes care of closing the statements used by the map.
      * The {@link AuthorityCodes} reference in this map is then cleared by the garbage collector.
-     * The {@link #canDispose()} method checks if there is any remaining live reference in this map,
-     * and returns {@code false} if some are found (thus blocking the call to {@link #dispose()}
+     * The {@link #canClose()} method checks if there is any remaining live reference in this map,
+     * and returns {@code false} if some are found (thus blocking the call to {@link #close()}
      * by the {@link ConcurrentAuthorityFactory} timer).</p>
      */
-    private final Map<Class<?>, Reference<AuthorityCodes>> authorityCodes = new HashMap<>();
+    private final Map<Class<?>, CloseableReference<AuthorityCodes>> authorityCodes = new HashMap<>();
 
     /**
      * Cache for axis names. This service is not provided by {@link CachingAuthorityFactory}
@@ -309,7 +308,9 @@ public abstract class EPSGFactory extend
 
     /**
      * The connection to the EPSG database. This connection is specified at {@linkplain #EPSGFactory construction time}
-     * and closed by the {@link #dispose()} method, or when this {@code EPSGFactory} instance is garbage collected.
+     * and closed by the {@link #close()} method.
+     *
+     * @see #close()
      */
     protected final Connection connection;
 
@@ -337,7 +338,7 @@ public abstract class EPSGFactory extend
 
     /**
      * Creates a factory using the given connection. The connection will be {@linkplain Connection#close() closed}
-     * when this factory will be {@linkplain #dispose() disposed}.
+     * when this factory will be {@linkplain #close() closed}.
      *
      * @param connection    The connection to the underlying EPSG database.
      * @param crsFactory    The factory to use for creating {@link CoordinateReferenceSystem} instances.
@@ -505,7 +506,7 @@ addURIs:    for (int i=0; ; i++) {
      * Returns a map of EPSG authority codes as keys and object names as values.
      */
     private synchronized Map<String,String> getCodeMap(final Class<?> type) throws FactoryException {
-        Reference<AuthorityCodes> reference = authorityCodes.get(type);
+        CloseableReference<AuthorityCodes> reference = authorityCodes.get(type);
         if (reference != null) {
             AuthorityCodes existing = reference.get();
             if (existing != null) {
@@ -1050,7 +1051,7 @@ addURIs:    for (int i=0; ; i++) {
          * this method. This approach assumes that two consecutive calls will often return the same type of object.
          * If the object type changed, then this method will have to discard the old prepared statement and prepare
          * a new one, which may be a costly operation. Only the last successful prepared statement is cached,
-         * in order to keep the amount of statements low. Unsuccessful statements are immediately disposed.
+         * in order to keep the amount of statements low. Unsuccessful statements are immediately closed.
          */
         final String  epsg         = trimAuthority(code);
         final boolean isPrimaryKey = isPrimaryKey(epsg);
@@ -1536,4 +1537,82 @@ addURIs:    for (int i=0; ; i++) {
     private static void unexpectedException(final String method, final Exception exception) {
         Logging.unexpectedException(Logging.getLogger(Loggers.CRS_FACTORY), EPSGFactory.class, method, exception);
     }
+
+    /**
+     * Returns {@code true} if it is safe to close this factory. This method is invoked indirectly
+     * by {@link ConcurrentAuthorityFactory} after some timeout in order to release resources.
+     * This method will block the disposal if some {@link AuthorityCodes} are still in use.
+     */
+    final synchronized boolean canClose() {
+        boolean can = true;
+        if (authorityCodes != null) {
+            System.gc();                // For cleaning as much weak references as we can before we check them.
+            final Iterator<CloseableReference<AuthorityCodes>> it = authorityCodes.values().iterator();
+            while (it.hasNext()) {
+                final AuthorityCodes codes = it.next().get();
+                if (codes == null) {
+                    it.remove();
+                } else {
+                    /*
+                     * A set of authority codes is still in use. We can not close this factory.
+                     * But we continue the iteration anyway in order to cleanup weak references.
+                     */
+                    can = false;
+                }
+            }
+        }
+        return can;
+    }
+
+    /**
+     * Closes the JDBC connection used by this factory.
+     * If this {@code EPSGFactory} is used by a {@link ConcurrentAuthorityFactory}, then this method
+     * will be automatically invoked after some {@linkplain ConcurrentAuthorityFactory#getTimeout timeout}.
+     *
+     * @throws FactoryException if an error occurred while closing the connection.
+     *
+     * @see #connection
+     */
+    @Override
+    public synchronized void close() throws FactoryException {
+        SQLException exception = null;
+        final Iterator<PreparedStatement> ip = statements.values().iterator();
+        while (ip.hasNext()) {
+            try {
+                ip.next().close();
+            } catch (SQLException e) {
+                if (exception == null) {
+                    exception = e;
+                } else {
+                    exception.addSuppressed(e);
+                }
+            }
+            ip.remove();
+        }
+        final Iterator<CloseableReference<AuthorityCodes>> it = authorityCodes.values().iterator();
+        while (it.hasNext()) {
+            try {
+                it.next().close();
+            } catch (SQLException e) {
+                if (exception == null) {
+                    exception = e;
+                } else {
+                    exception.addSuppressed(e);
+                }
+            }
+            it.remove();
+        }
+        try {
+            connection.close();
+        } catch (SQLException e) {
+            if (exception == null) {
+                exception = e;
+            } else {
+                e.addSuppressed(exception);     // Keep the connection thrown be Connection as the main one to report.
+            }
+        }
+        if (exception != null) {
+            throw new FactoryException(exception);
+        }
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.internal.system;
 
-import javax.management.JMException;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleEvent;
@@ -30,7 +29,7 @@ import org.osgi.framework.BundleListener
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.7
  * @module
  *
  * @see ServletListener
@@ -50,6 +49,7 @@ public final class OSGiActivator impleme
     @Override
     public void start(final BundleContext context) {
         context.addBundleListener(this);
+        Shutdown.setContainer("OSGi");
     }
 
     /**
@@ -57,10 +57,10 @@ public final class OSGiActivator impleme
      * This method shutdowns the {@code sis-utility} threads.
      *
      * @param  context The execution context of the bundle being stopped.
-     * @throws JMException If an error occurred during unregistration of the supervisor MBean.
+     * @throws Exception If an error occurred during unregistration of the supervisor MBean or resource disposal.
      */
     @Override
-    public void stop(final BundleContext context) throws JMException {
+    public void stop(final BundleContext context) throws Exception {
         context.removeBundleListener(this);
         Shutdown.stop(getClass());
     }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/ServletListener.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/ServletListener.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/ServletListener.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/ServletListener.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.internal.system;
 
-import javax.management.JMException;
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
 import javax.servlet.annotation.WebListener;
@@ -28,7 +27,7 @@ import javax.servlet.annotation.WebListe
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.7
  * @module
  *
  * @see OSGiActivator
@@ -42,6 +41,8 @@ public final class ServletListener imple
      */
     @Override
     public void contextInitialized(final ServletContextEvent event) {
+        final String env = event.getServletContext().getServerInfo();
+        Shutdown.setContainer(env != null ? env : "Servlet");
     }
 
     /**
@@ -53,7 +54,7 @@ public final class ServletListener imple
     public void contextDestroyed(final ServletContextEvent event) {
         try {
             Shutdown.stop(getClass());
-        } catch (JMException e) {
+        } catch (Exception e) {
             event.getServletContext().log(e.toString(), e);
         }
     }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Shutdown.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Shutdown.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Shutdown.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Shutdown.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -16,48 +16,180 @@
  */
 package org.apache.sis.internal.system;
 
-import javax.management.JMException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
 import org.apache.sis.util.logging.Logging;
 
+// Branch-dependent imports
+import java.util.Objects;
+
 
 /**
  * A central place where to manage SIS shutdown process.
- * For now this class is not yet registered as a shutdown hock,
- * but it will be in a future version.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.7
  * @module
  */
-public final class Shutdown {
+public final class Shutdown extends Thread {
+    /**
+     * Non-null if a shutdown hook is already registered. That shutdown hook is not necessarily {@link #hook}.
+     * It may be an OSGi or Servlet shutdown hook instead, as notified by {@link #setContainer(String)}.
+     */
+    private static String container;
+
+    /**
+     * The shutdown hook to be registered to the JVM {@link Runtime}, created when first needed.
+     */
+    private static Shutdown hook;
+
     /**
-     * Do not allow instantiation of this class.
+     * The resources to dispose. Most recently added resources are last.
+     */
+    private static final List<Callable<?>> resources = new ArrayList<>();
+
+    /**
+     * Creates the thread to be executed at shutdown time.
      */
     private Shutdown() {
+        super(Threads.SIS, "Shutdown");
     }
 
     /**
-     * Shutdowns the {@code sis-utility} threads and unregister the supervisor MBean.
+     * Invoked at JVM shutdown time.
+     */
+    @Override
+    @SuppressWarnings("CallToPrintStackTrace")
+    public void run() {
+        try {
+            Shutdown.stop((Class<?>) null);
+        } catch (Exception e) {
+            /*
+             * Too late for logging since we are in process of shutting down the Java Virtual Machine.
+             * It is still possible to write the stack trace to System.err, but this is about all we can do.
+             */
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Returns the value set by the last call to {@link #setContainer(String)}.
+     *
+     * @return Typically {@code "OSGi"}, {@code "Servlet"} or {@code null}.
+     */
+    public static String getContainer() {
+        synchronized (resources) {
+            return container;
+        }
+    }
+
+    /**
+     * Invoked if the Apache SIS library is executed from an environment that provide its own shutdown hook.
+     * Example of such environments are OSG and servlet containers. In such case, the shutdown hook will not
+     * be registered to the JVM {@link Runtime}.
      *
-     * @param  caller The class invoking this method, to be used only for logging purpose,
-     *         or {@code null} if the logging system is not available anymore (i.e. the JVM
-     *         itself is shutting down).
-     * @throws JMException If an error occurred during unregistration of the supervisor MBean.
+     * @param env A description of the container. Should contain version information if possible.
+     *            Example: {@code "OSGi"} or {@code "JavaServer Web Dev Kit/1.0"}.
      */
-    public static void stop(final Class<?> caller) throws JMException {
+    public static void setContainer(final String env) {
+        Objects.requireNonNull(env);
+        synchronized (resources) {
+            removeShutdownHook();       // Should not be needed but we are paranoiac.
+            container = env;
+        }
+    }
+
+    /**
+     * Registers a code to execute at JVM shutdown time. The resources will be disposed at
+     * shutdown time in reverse order (most recently added resources will be disposed first).
+     *
+     * <p>The same resource shall not be added twice.</p>
+     *
+     * @param resource The resource disposal to register for execution at shutdown time.
+     */
+    public static void register(final Callable<?> resource) {
+        synchronized (resources) {
+            assert !resources.contains(resource);
+            resources.add(resource);
+            if (hook == null && container == null) {
+                hook = new Shutdown();
+                Runtime.getRuntime().addShutdownHook(hook);
+            }
+        }
+    }
+
+    /**
+     * Removes the shutdown hook, if any.
+     */
+    private static void removeShutdownHook() {
+        assert Thread.holdsLock(resources);
+        if (hook != null) {
+            Runtime.getRuntime().removeShutdownHook(hook);
+            hook = null;
+        }
+    }
+
+    /**
+     * Unregisters a code from execution at JVM shutdown time.
+     * This method uses identity comparison (it does not use {@link Object#equals(Object)}).
+     *
+     * @param resource The resource disposal to cancel execution.
+     */
+    public static void unregister(final Callable<?> resource) {
+        synchronized (resources) {
+            for (int i = resources.size(); --i>=0;) {       // Check most recently added resources first.
+                if (resources.get(i) == resource) {
+                    resources.remove(i);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Unregister the supervisor MBean, executes the disposal tasks and shutdowns the {@code sis-utility} threads.
+     *
+     * @param  caller The class invoking this method, to be used only for logging purpose, or {@code null}
+     *         if the logging system is not available anymore (i.e. the JVM itself is shutting down).
+     * @throws Exception If an error occurred during unregistration of the supervisor MBean
+     *         or during a resource disposal.
+     */
+    public static void stop(final Class<?> caller) throws Exception {
+        synchronized (resources) {
+            container = "Shutdown";
+            if (caller != null) {
+                removeShutdownHook();
+            }
+        }
         /*
          * Unregister the MBean before to stop the threads, in order to avoid false alerts
          * in the superviror 'warnings()' method. Failure to unregister the MBean is worth
          * to report, but we will do that only after we completed the other shutdown steps.
          */
-        JMException exception = null;
+        Exception exception = null;
         if (Supervisor.ENABLED) try {
             Supervisor.unregister();
-        } catch (JMException deferred) {
+        } catch (Exception deferred) {
             exception = deferred;
         }
         /*
+         * Dispose resources, if any, starting with most recently registered. The disposal code should not
+         * invoke Shutdown.[un]register(Disposable), but we nevertheless make the loop robust to this case.
+         */
+        synchronized (resources) {
+            int i;
+            while ((i = resources.size()) != 0) try {       // In case run() modifies the resources list.
+                resources.remove(i - 1).call();             // Dispose most recently added resources first.
+            } catch (Exception e) {
+                if (exception != null) {
+                    e.addSuppressed(exception);
+                }
+                exception = e;
+            }
+        }
+        /*
          * Following is usually fast, but may potentially take a little while.
          * If an other thread invoked Thread.interrupt() while we were waiting
          * for the threads to terminate, maybe not all threads have terminated

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/About.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/About.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/About.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/About.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -49,6 +49,7 @@ import org.apache.sis.util.collection.Tr
 import org.apache.sis.util.collection.DefaultTreeTable;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.system.Shutdown;
 import org.apache.sis.internal.system.DataDirectory;
 
 import static java.lang.System.getProperty;
@@ -224,6 +225,13 @@ fill:   for (int i=0; ; i++) {
                     break;
                 }
                 case 3: {
+                    if (sections.contains(VERSIONS)) {
+                        nameKey = Vocabulary.Keys.Container;
+                        value = Shutdown.getContainer();        // Sometime contains version information.
+                    }
+                    break;
+                }
+                case 4: {
                     newSection = LOCALIZATION;
                     if (sections.contains(LOCALIZATION)) {
                         final Locale current = Locale.getDefault();
@@ -238,7 +246,7 @@ fill:   for (int i=0; ; i++) {
                     }
                     break;
                 }
-                case 4: {
+                case 5: {
                     if (sections.contains(LOCALIZATION)) {
                         final TimeZone current = TimeZone.getDefault();
                         if (current != null) {
@@ -259,7 +267,7 @@ fill:   for (int i=0; ; i++) {
                     }
                     break;
                 }
-                case 5: {
+                case 6: {
                     if (sections.contains(LOCALIZATION)) {
                         nameKey = Vocabulary.Keys.CurrentDateTime;
                         final DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, formatLocale);
@@ -270,7 +278,7 @@ fill:   for (int i=0; ; i++) {
                     }
                     break;
                 }
-                case 6: {
+                case 7: {
                     if (sections.contains(LOCALIZATION)) {
                         final Charset current = Charset.defaultCharset();
                         if (current != null) {
@@ -290,7 +298,7 @@ fill:   for (int i=0; ; i++) {
                     }
                     break;
                 }
-                case 7: {
+                case 8: {
                     newSection = LOGGING;
                     if (sections.contains(LOGGING)) {
                         nameKey = Vocabulary.Keys.Implementation;
@@ -299,7 +307,7 @@ fill:   for (int i=0; ; i++) {
                     }
                     break;
                 }
-                case 8: {
+                case 9: {
                     newSection = PATHS;
                     if (sections.contains(PATHS)) {
                         nameKey = Vocabulary.Keys.UserHome;
@@ -307,14 +315,14 @@ fill:   for (int i=0; ; i++) {
                     }
                     break;
                 }
-                case 9: {
+                case 10: {
                     if (sections.contains(PATHS)) {
                         nameKey = Vocabulary.Keys.CurrentDirectory;
                         value = getProperty("user.dir");
                     }
                     break;
                 }
-                case 10: {
+                case 11: {
                     if (sections.contains(PATHS)) {
                         nameKey = Vocabulary.Keys.DataDirectory;
                         value = System.getenv(DataDirectory.ENV);
@@ -331,21 +339,21 @@ fill:   for (int i=0; ; i++) {
                     }
                     break;
                 }
-                case 11: {
+                case 12: {
                     if (sections.contains(PATHS)) {
                         nameKey = Vocabulary.Keys.TemporaryFiles;
                         value = getProperty("java.io.tmpdir");
                     }
                     break;
                 }
-                case 12: {
+                case 13: {
                     if (sections.contains(PATHS)) {
                         nameKey = Vocabulary.Keys.JavaHome;
                         value = javaHome = getProperty("java.home");
                     }
                     break;
                 }
-                case 13: {
+                case 14: {
                     newSection = LIBRARIES;
                     if (sections.contains(LIBRARIES)) {
                         nameKey = Vocabulary.Keys.JavaExtensions;
@@ -353,7 +361,7 @@ fill:   for (int i=0; ; i++) {
                     }
                     break;
                 }
-                case 14: {
+                case 15: {
                     if (sections.contains(LIBRARIES)) {
                         nameKey = Vocabulary.Keys.Classpath;
                         value = classpath(getProperty("java.class.path"), false);

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Disposable.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Disposable.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Disposable.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Disposable.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -18,18 +18,19 @@ package org.apache.sis.util;
 
 
 /**
- * A resource that can be disposed when waiting for the garbage collector would be overly
- * conservative. Invoking the {@link #dispose()} method allows any resources held by this
- * object to be released. The result of calling any other method subsequent to a call to
- * this method is undefined.
+ * A resource that can be disposed when waiting for the garbage collector would be overly conservative.
+ * Invoking the {@link #dispose()} method allows any resources held by this object to be released.
+ * The result of calling any other method subsequent to a call to this method is undefined.
  *
  * <div class="section">Relationship with {@code Closeable}</div>
- * Some SIS classes may implement both the {@code Disposeable} and {@link java.io.Closeable}
- * interfaces. While very similar, those two interfaces serve slightly different purposes.
- * The {@code Closeable} interface closes a stream or a connection, but some classes can be
- * reused with a different stream. For example an {@link javax.imageio.ImageReader} can be
- * instantiated once and reused many time for reading different image streams of the same
- * format. However once an object has been disposed, it can not be used anymore.
+ * Some classes may implement both the {@code Disposeable} and {@link java.io.Closeable} interfaces.
+ * While very similar, those two interfaces serve slightly different purposes. The {@code Closeable}
+ * interface closes a stream or a connection, but some classes allow the object to be reused with a
+ * different stream. However once an object has been disposed, it can not be used anymore.
+ *
+ * <div class="note"><b>Example:</b>
+ * {@link javax.imageio.ImageReader} and {@link javax.imageio.ImageWriter} allow to reuse the same instance
+ * many time for reading or writing different image streams of the same format.</div>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -126,6 +126,11 @@ public final class Vocabulary extends In
         public static final short ConstantPressureSurface = 19;
 
         /**
+         * Container
+         */
+        public static final short Container = 92;
+
+        /**
          * Correlation
          */
         public static final short Correlation = 83;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] Wed Dec 16 15:26:55 2015
@@ -28,6 +28,7 @@ Classpath               = Classpath
 Code_1                  = {0} code
 Commands                = Commands
 ConstantPressureSurface = Constant pressure surface
+Container               = Container
 Correlation             = Correlation
 CurrentDateTime         = Current date and time
 CurrentDirectory        = Current directory

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] Wed Dec 16 15:26:55 2015
@@ -35,6 +35,7 @@ Classpath               = Chemin de clas
 Code_1                  = Code {0}
 Commands                = Commandes
 ConstantPressureSurface = Surface \u00e0 pression constante
+Container               = Conteneur
 Correlation             = Corr\u00e9lation
 CurrentDateTime         = Date et heure courantes
 CurrentDirectory        = R\u00e9pertoire courant

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java?rev=1720367&r1=1720366&r2=1720367&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java [UTF-8] Wed Dec 16 15:26:55 2015
@@ -26,7 +26,6 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URL;
 import java.net.URISyntaxException;
-import javax.management.JMException;
 import org.apache.sis.internal.system.Shutdown;
 import org.apache.sis.internal.system.SystemListener;
 import org.apache.sis.util.Classes;
@@ -42,7 +41,7 @@ import static org.junit.Assert.*;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.5
+ * @version 0.7
  * @module
  */
 @RunWith(Suite.class)
@@ -247,10 +246,11 @@ public abstract strictfp class TestSuite
      * <p>Since this method stops SIS daemon threads, the SIS library shall not be used anymore after
      * this method execution.</p>
      *
-     * @throws JMException If an error occurred during unregistration of the supervisor MBean.
+     * @throws Exception If an error occurred during unregistration of the supervisor MBean or resource disposal.
      */
     @AfterClass
-    public static void shutdown() throws JMException {
+    @SuppressWarnings("UseOfSystemOutOrSystemErr")
+    public static void shutdown() throws Exception {
         if (!skipShutdown) {
             skipShutdown = true;
             TestCase.LOGGER.removeHandler(LogRecordCollector.INSTANCE);



Mime
View raw message