sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1718735 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/factory/ sis-utility/src/main/java/org/apache/sis/internal/system/ sis-utility/src/main/java/org/apache/sis/util/collection/ sis-utility/src/mai...
Date Tue, 08 Dec 2015 22:50:01 GMT
Author: desruisseaux
Date: Tue Dec  8 22:50:00 2015
New Revision: 1718735

URL: http://svn.apache.org/viewvc?rev=1718735&view=rev
Log:
Retrofit 'ThreadedAuthorityFactory' (from Geotk) into CachedAuthorityFactory.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/package-info.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -18,7 +18,11 @@ package org.apache.sis.referencing.facto
 
 import java.util.Set;
 import java.util.Map;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.WeakHashMap;
+import java.util.concurrent.TimeUnit;
 import java.lang.ref.WeakReference;
 import java.io.PrintWriter;
 import javax.measure.unit.Unit;
@@ -34,30 +38,45 @@ import org.opengis.util.InternationalStr
 import org.opengis.metadata.extent.Extent;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.parameter.ParameterDescriptor;
+import org.apache.sis.util.Classes;
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.Disposable;
 import org.apache.sis.util.ArgumentChecks;
 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.simple.SimpleCitation;
 import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.system.DelayedExecutor;
+import org.apache.sis.internal.system.DelayedRunnable;
 import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.util.resources.Errors;
 
 
 /**
- * An authority factory that caches all objects created by another factory.
+ * A concurrent authority factory that caches all objects created by another factory.
  * All {@code createFoo(String)} methods first check if a previously created object exists for the given code.
  * If such object exists, it is returned. Otherwise, the object creation is delegated to another factory given
  * by {@link #createBackingStore()} and the result is cached in this factory.
  *
- * <p>Objects are cached by strong references, up to the amount of objects specified at construction time.
- * If a greater amount of objects are cached, the oldest ones will be retained through a
+ * <p>{@code CachingAuthorityFactory} 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>
+ *
+ * <div class="section">Caching strategy</div>
+ * Objects are cached by strong references, up to the amount of objects specified at construction time.
+ * If a greater amount of objects are cached, then the oldest ones will be retained through a
  * {@linkplain WeakReference weak reference} instead of a strong one.
- * This means that this caching factory will continue to return them as long as they are in use somewhere
- * else in the Java virtual machine, but will be discarded (and recreated on the fly if needed) otherwise.</p>
+ * This means that this caching factory will continue to return those objects as long as they are in use somewhere
+ * else in the Java virtual machine, but will be discarded (and recreated on the fly if needed) otherwise.
+ *
+ * <div class="section">Multi-threading</div>
+ * The cache managed by this class is concurrent. However the backing stores are assumed non-concurrent.
+ * If two or more threads are accessing this factory in same time, then two or more backing store instances
+ * may be created. The maximal amount of instances to create is specified at {@code CachingAuthorityFactory}
+ * construction time. If more backing store instances are needed, some of the threads will block until an
+ * instance become available.
  *
- * <div class="section">Not for subclasses</div>
+ * <div class="section">Note for subclasses</div>
  * This abstract class does not implement any of the {@link DatumAuthorityFactory}, {@link CSAuthorityFactory},
  * {@link CRSAuthorityFactory} and {@link CoordinateOperationAuthorityFactory} interfaces.
  * Subclasses should select the interfaces that they choose to implement.
@@ -69,24 +88,14 @@ import org.apache.sis.internal.system.Lo
  */
 public abstract class CachingAuthorityFactory extends GeodeticAuthorityFactory implements Disposable {
     /**
-     * The default value for {@link #maxStrongReferences}.
-     */
-    static final int DEFAULT_MAX = 100;
-
-    /**
-     * The citation for unknown authority name.
-     */
-    private static final Citation UNKNOWN = new SimpleCitation("Unknown");
-
-    /**
      * The authority, cached after first requested.
      */
     private transient volatile Citation authority;
 
     /**
-     * The pool of cached objects. The keys are instances of {@link Key} or {@link CodePair}.
+     * The pool of cached objects.
      */
-    private final Cache<Object,Object> cache;
+    private final Cache<Key,Object> cache;
 
     /**
      * The pool of objects identified by {@link Finder#find(IdentifiedObject)} for each comparison modes.
@@ -97,22 +106,145 @@ public abstract class CachingAuthorityFa
     private final Map<IdentifiedObject, IdentifiedObject> findPool = new WeakHashMap<>();
 
     /**
-     * Constructs an instance with a default number of entries to keep by strong reference.
+     * Holds the reference to a backing store used by {@link CachingAuthorityFactory} together with information
+     * about its usage. In a mono-thread application, there is typically only one {@code BackingStore} instance
+     * at a given time. However if more than one than one thread are requesting new objects concurrently, then
+     * many instances may exist for the same {@code CachingAuthorityFactory}.
+     *
+     * <p>If the backing store is currently in use, then {@code BackingStore} counts how many recursive invocations
+     * of a {@link #factory} {@code createFoo(String)} method is under way in the current thread.
+     * This information is used in order to reuse the same factory instead than creating new instances
+     * when a {@code GeodeticAuthorityFactory} implementation invokes itself indirectly through the
+     * {@link CachingAuthorityFactory}. 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>
+     */
+    private static final class BackingStore {
+        /**
+         * The factory used as a backing store.
+         */
+        final GeodeticAuthorityFactory factory;
+
+        /**
+         * Incremented on every call to {@link CachingAuthorityFactory#getBackingStore()} and decremented on every call
+         * to {@link CachingAuthorityFactory#release()}. When this value reach zero, the factory is really released.
+         */
+        int depth;
+
+        /**
+         * The timestamp (<strong>not</strong> relative to epoch) at the time the backing store factory has been
+         * released. This timestamp shall be obtained by a call to {@link System#nanoTime()} for consistency with
+         * {@link DelayedRunnable}.
+         */
+        long timestamp;
+
+        /**
+         * Creates new backing store information for the given factory.
+         */
+        BackingStore(final GeodeticAuthorityFactory factory) {
+            this.factory = factory;
+        }
+
+        /**
+         * Returns a string representation for debugging purpose only.
+         */
+        @Debug
+        @Override
+        public String toString() {
+            final String text;
+            final Number value;
+            if (depth != 0) {
+                text = "%s in use with at depth %d";
+                value = depth;
+            } else {
+                text = "%s made available %d seconds ago";
+                value = (System.nanoTime() - timestamp) / 1E+9;   // Convert nanoseconds to seconds.
+            }
+            return String.format(text, Classes.getShortClassName(factory), value);
+        }
+    }
+
+    /**
+     * The backing store in use by the current thread.
+     */
+    private final ThreadLocal<BackingStore> currentStore = new ThreadLocal<>();
+
+    /**
+     * The backing store instances previously created and released for future reuse.
+     * Last used factories must be {@linkplain Deque#addLast(Object) added last}.
+     * This is used as a LIFO stack.
+     */
+    private final Deque<BackingStore> availableStores = new LinkedList<>();
+
+    /**
+     * The amount of backing stores that can still be created. This number is decremented in a block synchronized
+     * on {@link #availableStores} every time a backing store is in use, and incremented once released.
+     */
+    private int remainingBackingStores;
+
+    /**
+     * {@code true} if the call to {@link #disposeExpired()} 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}.
+     *
+     * <p>Note that we can not use {@code !stores.isEmpty()} as a replacement of {@code isActive}
+     * because the queue is empty if all backing stores are currently in use.</p>
+     *
+     * <p>Every access to this field must be performed in a block synchronized on {@link #availableStores}.</p>
+     */
+    private boolean isActive;
+
+    /**
+     * {@code true} if {@link #dispose()} has been invoked.
+     *
+     * <p>Every access to this field must be performed in a block synchronized on {@link #availableStores}.</p>
+     */
+    private boolean isDisposed;
+
+    /**
+     * The delay of inactivity (in nanoseconds) before to close a backing store.
+     * Every access to this field must be performed in a block synchronized on {@link #availableStores}.
+     *
+     * @see #getTimeout(TimeUnit)
+     */
+    private long timeout = 60_000_000_000L;     // One minute
+
+    /**
+     * The maximal difference between the scheduled time and the actual time in order to perform the factory disposal,
+     * in nanoseconds. This is used as a tolerance value for possible wait time inaccuracy.
+     */
+    static final long TIMEOUT_RESOLUTION = 200_000_000L;    // 0.2 second
+
+    /**
+     * Constructs an instance with a default number of threads and a default number of entries to keep
+     * by strong references. Note that those default values may change in any future SIS versions based
+     * on experience gained.
      */
     protected CachingAuthorityFactory() {
-        this(DEFAULT_MAX);
+        this(100, 8);
+        /*
+         * NOTE: if the default maximum number of backing stores (currently 8) is augmented,
+         * make sure to augment the number of runner threads in the "StressTest" class to a greater amount.
+         */
     }
 
     /**
      * Constructs an instance with the specified number of entries to keep by strong references.
-     * In a number of object greater than {@code maxStrongReferences} are created, then the strong references
+     * If a number of object greater than {@code maxStrongReferences} are created, then the strong references
      * for the eldest ones will be replaced by weak references.
      *
      * @param maxStrongReferences The maximum number of objects to keep by strong reference.
+     * @param maxConcurrentQueries The maximal amount of backing stores to use concurrently.
+     *        If more than this amount of threads are querying this {@code CachingAuthorityFactory} concurrently,
+     *        additional threads will be blocked until a backing store become available.
      */
-    protected CachingAuthorityFactory(final int maxStrongReferences) {
-        super(DefaultFactories.forBuildin(NameFactory.class));    // TODO
+    protected CachingAuthorityFactory(final int maxStrongReferences, final int maxConcurrentQueries) {
+        super(DefaultFactories.forBuildin(NameFactory.class));
         ArgumentChecks.ensurePositive("maxStrongReferences", maxStrongReferences);
+        ArgumentChecks.ensureStrictlyPositive("maxConcurrentQueries", maxConcurrentQueries);
+        remainingBackingStores = maxConcurrentQueries;
         cache = new Cache<>(20, maxStrongReferences, false);
         cache.setKeyCollisionAllowed(true);
         /*
@@ -124,20 +256,238 @@ public abstract class CachingAuthorityFa
     }
 
     /**
-     * Returns the backing store authority factory. The returned backing store must be thread-safe.
-     * This method shall be used together with {@link #release()} in a {@code try ... finally} block.
+     * Returns the number of backing stores. This count does not include the backing stores
+     * that are currently under execution. This method is used only for testing purpose.
+     */
+    @Debug
+    final int countBackingStores() {
+        synchronized (availableStores) {
+            return availableStores.size();
+        }
+    }
+
+    /**
+     * 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.
+     *
+     * <div class="section">Multi-threading</div>
+     * This method (but not necessarily the returned factory) needs to be thread-safe;
+     * {@code CachingAuthorityFactory} does not hold any lock when invoking this method.
+     * Subclasses are responsible to apply their own synchronization if needed,
+     * but are encouraged to avoid doing so if possible.
+     * In addition, implementations should not invoke other {@code CachingAuthorityFactory}
+     * methods during this method execution in order to avoid never-ending loop.
+     *
+     * @return The backing store to uses in {@code createFoo(String)} methods.
+     * @throws UnavailableFactoryException if the backing store is unavailable because an optional resource is missing.
+     * @throws FactoryException if the creation of backing store failed for another reason.
+     */
+    protected abstract GeodeticAuthorityFactory createBackingStore() throws UnavailableFactoryException, FactoryException;
+
+    /**
+     * Returns a backing store authority factory. This method <strong>must</strong>
+     * be used together with {@link #release()} in a {@code try ... finally} block.
      *
      * @return The backing store to use in {@code createXXX(…)} methods.
-     * @throws FactoryException if the creation of backing store failed.
+     * @throws FactoryException if the backing store creation failed.
      */
     private GeodeticAuthorityFactory getBackingStore() throws FactoryException {
-        throw new UnsupportedOperationException("Not yet implemented");
+        /*
+         * First checks if the current thread is already using a factory. If yes, we will
+         * avoid creating new factories on the assumption that factories are reentrant.
+         */
+        BackingStore usage = currentStore.get();
+        if (usage == null) {
+            synchronized (availableStores) {
+                if (isDisposed) {
+                    throw new UnavailableFactoryException(Errors.format(Errors.Keys.DisposedFactory));
+                }
+                /**
+                 * If we have reached the maximal amount of backing stores allowed, wait for a backing store
+                 * to become available. In theory the 0.2 second timeout is not necessary, but we put it as a
+                 * safety in case we fail to invoke a notify() matching this wait(), for example someone else
+                 * is waiting on this monitor or because the release(…) method threw an exception.
+                 */
+                while (remainingBackingStores == 0) {
+                    try {
+                        availableStores.wait(TIMEOUT_RESOLUTION);
+                    } catch (InterruptedException e) {
+                        // Someone does not want to let us sleep.
+                        throw new FactoryException(e.getLocalizedMessage(), e);
+                    }
+                }
+                /*
+                 * Reuse the most recently used factory, if available. If there is no factory available for reuse,
+                 * creates a new one. We do not add it to the queue now; it will be done by the release(…) method.
+                 */
+                usage = availableStores.pollLast();
+                remainingBackingStores--;       // Should be done last when we are sure to not fail.
+            }
+            /*
+             * If there is a need to create a new factory, do that outside the synchronized block because this
+             * creation may involve a lot of client code. This is better for reducing the dead-lock risk.
+             * Subclasses are responsible of synchronizing their createBackingStore() method if necessary.
+             */
+            try {
+                if (usage == null) {
+                    final GeodeticAuthorityFactory factory = createBackingStore();
+                    if (factory == null) {
+                        throw new UnavailableFactoryException(Errors.format(
+                                Errors.Keys.FactoryNotFound_1, GeodeticAuthorityFactory.class));
+                    }
+                    usage = new BackingStore(factory);
+                    currentStore.set(usage);
+                }
+                assert usage.depth == 0 : usage;
+            } finally {
+                /*
+                 * If any kind of error occurred, restore the 'remainingBackingStores' field as if no code were executed.
+                 * This code would not have been needed if we were allowed to decrement 'remainingBackingStores' only as
+                 * the very last step (when we know that everything else succeed). But it needed to be decremented inside
+                 * the synchronized block.
+                 */
+                if (usage == null) {
+                    synchronized (availableStores) {
+                        remainingBackingStores++;
+                    }
+                }
+            }
+        }
+        /*
+         * Increment below is safe even if outside the synchronized block,
+         * because each thread own exclusively its BackingStore instance
+         */
+        usage.depth++;
+        return usage.factory;
     }
 
     /**
-     * Releases the backing store previously obtained with {@link #getBackingStore}.
+     * Releases the backing store previously obtained with {@link #getBackingStore()}.
+     * This method marks the factory as available for reuse by other threads.
      */
     private void release() {
+        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;
+                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.
+                 */
+                if (!isActive) {
+                    isActive = true;
+                    DelayedExecutor.schedule(new DisposeTask(usage.timestamp + timeout));
+                }
+                availableStores.notify();    // We released only one backing store, so awake only one thread - not all of them.
+            }
+        }
+        assert usage.depth >= 0 : usage;
+    }
+
+    /**
+     * A task for invoking {@link CachingAuthorityFactory#disposeExpired()} after a delay.
+     */
+    private final class DisposeTask extends DelayedRunnable {
+        /**
+         * Creates a new task to be executed at the given time,
+         * in nanoseconds relative to {@link System#nanoTime()}.
+         */
+        DisposeTask(final long timestamp) {
+            super(timestamp);
+        }
+
+        /** Invoked when the delay expired. */
+        @Override public void run() {
+            disposeExpired();
+        }
+    }
+
+    /**
+     * 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.
+     *
+     * @see #dispose()
+     */
+    final void disposeExpired() {
+        int count = 0;
+        final Disposable[] toDispose;
+        synchronized (availableStores) {
+            toDispose = new Disposable[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
+                 * and wait again.
+                 */
+                final long nextTime = store.timestamp + timeout;
+                if (nextTime - nanoTime > TIMEOUT_RESOLUTION) {
+                    /*
+                     * Found a factory which is not expired. Stop the search,
+                     * since the iteration is expected to be ordered.
+                     */
+                    DelayedExecutor.schedule(new DisposeTask(nextTime));
+                    break;
+                }
+                /*
+                 * Found an expired factory. Adds it to the list of
+                 * factories to dispose and search for other factories.
+                 */
+                it.remove();
+                if (store.factory instanceof Disposable) {
+                    toDispose[count++] = (Disposable) store.factory;
+                }
+            }
+            /*
+             * The stores list is empty if all worker factories in the queue have been disposed.
+             * 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.
+             */
+            isActive = !availableStores.isEmpty();
+        }
+        /*
+         * We must dispose the factories from outside the synchronized block.
+         */
+        while (--count >= 0) {
+            toDispose[count].dispose();
+        }
+    }
+
+    /**
+     * Returns the amount of time that {@code CachingAuthorityFactory} will wait before to dispose 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.
+     * @return The current timeout in the given unit of measurement.
+     */
+    public long getTimeout(final TimeUnit unit) {
+        synchronized (availableStores) {
+            return unit.convert(timeout, TimeUnit.NANOSECONDS);
+        }
+    }
+
+    /**
+     * Sets a timer for disposing 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.
+     *
+     * @param delay The delay of inactivity before to close a backing store.
+     * @param unit  The unit of measurement of the given delay.
+     */
+    public void setTimeout(long delay, final TimeUnit unit) {
+        ArgumentChecks.ensureStrictlyPositive("delay", delay);
+        delay = unit.toNanos(delay);
+        synchronized (availableStores) {
+            timeout = delay;                // Will be taken in account after the next factory to dispose.
+        }
     }
 
     /**
@@ -155,7 +505,10 @@ public abstract class CachingAuthorityFa
      *   </li>
      * </ul>
      *
-     * @return The organization responsible for definition of the database.
+     * If this method can not get a backing store factory (for example because no database connection is available),
+     * then this method returns {@code null}.
+     *
+     * @return The organization responsible for definition of the database, or {@code null} if unavailable.
      */
     @Override
     public Citation getAuthority() {
@@ -170,7 +523,6 @@ public abstract class CachingAuthorityFa
                 release();
             }
         } catch (FactoryException e) {
-            c = UNKNOWN;
             Logging.unexpectedException(Logging.getLogger(Loggers.CRS_FACTORY),
                     CachingAuthorityFactory.class, "getAuthority", e);
         }
@@ -936,15 +1288,30 @@ public abstract class CachingAuthorityFa
 
     /**
      * The key objects to use in the {@link CachingAuthorityFactory#cache}.
+     * This is one of the following pairs of values:
+     * <ul>
+     *   <li>For all {@code CachingAuthorityFactory.createFoo(String)} methods:
+     *     <ol>
+     *       <li>The {@code Foo} {@link Class} of the cached object.</li>
+     *       <li>The authority code of the cached object.</li>
+     *     </ol>
+     *   </li>
+     *   <li>For {@link CachingAuthorityFactory#createFromCoordinateReferenceSystemCodes(String, String)}:
+     *     <ol>
+     *       <li>The authority code of source CRS (stored in the "type" field even if the name is not right).</li>
+     *       <li>The authority code of target CRS (stored in the "code" field).</li>
+     *     </ol>
+     *   </li>
+     * </ul>
      *
      * @see <a href="http://jira.geotoolkit.org/browse/GEOTK-2">GEOTK-2</a>
      */
     private static final class Key {
-        /** The type of the cached object.    */ final Class<?> type;
+        /** The type of the cached object.    */ final Object type;
         /** The cached object authority code. */ final String code;
 
         /** Creates a new key for the given type and code. */
-        Key(final Class<?> type, final String code) {
+        Key(final Object type, final String code) {
             this.type = type;
             this.code = code;
         }
@@ -965,12 +1332,18 @@ public abstract class CachingAuthorityFa
 
         /** String representation used by {@link CacheRecord}. */
         @Override @Debug public String toString() {
-            final StringBuilder buffer = new StringBuilder("Key[").append(code);
-            if (buffer.length() > 15) { // Arbitrary limit in string length.
-                buffer.setLength(15);
-                buffer.append('…');
+            final StringBuilder buffer = new StringBuilder();
+            if (type instanceof Class<?>) {
+                buffer.append("Code[“").append(code);
+                if (buffer.length() > 15) { // Arbitrary limit in string length.
+                    buffer.setLength(15);
+                    buffer.append('…');
+                }
+                buffer.append("” : ").append(((Class<?>) type).getSimpleName());
+            } else {
+                buffer.append("CodePair[“").append(type).append("” → “").append(code).append('”');
             }
-            return buffer.append(" : ").append(type.getSimpleName()).append(']').toString();
+            return buffer.append(']').toString();
         }
     }
 
@@ -1039,7 +1412,7 @@ public abstract class CachingAuthorityFa
     {
         ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
         ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
-        final CodePair key = new CodePair(trimAuthority(sourceCRS), trimAuthority(targetCRS));
+        final Key key = new Key(trimAuthority(sourceCRS), trimAuthority(targetCRS));
         Object value = cache.peek(key);
         if (!(value instanceof Set<?>)) {
             final Cache.Handler<Object> handler = cache.lock(key);
@@ -1061,40 +1434,6 @@ public abstract class CachingAuthorityFa
     }
 
     /**
-     * A pair of codes for operations to cache with
-     * {@link CachingAuthorityFactory#createFromCoordinateReferenceSystemCodes(String, String)}.
-     */
-    private static final class CodePair {
-        /** Codes of source and target CRS. */
-        private final String source, target;
-
-        /** Creates a new key for the given pair of codes. */
-        public CodePair(final String source, final String target) {
-            this.source = source;
-            this.target = target;
-        }
-
-        /** Returns the hash code value for this key. */
-        @Override public int hashCode() {
-            return source.hashCode() + target.hashCode() * 31;
-        }
-
-        /** Compares this key with the given object for equality .*/
-        @Override public boolean equals(final Object other) {
-            if (other instanceof CodePair) {
-                final CodePair that = (CodePair) other;
-                return source.equals(that.source) && target.equals(that.target);
-            }
-            return false;
-        }
-
-        /** String representation used by {@link CacheRecord}. */
-        @Override @Debug public String toString() {
-            return "CodePair[" + source + " → " + target + ']';
-        }
-    }
-
-    /**
      * Returns a finder which can be used for looking up unidentified objects.
      * The default implementation delegates lookup to the underlying backing store and caches the result.
      *
@@ -1280,10 +1619,32 @@ public abstract class CachingAuthorityFa
      */
     @Override
     public void dispose() {
-        cache.clear();
-        authority = null;
-        synchronized (findPool) {
-            findPool.clear();
+        try {
+            int count = 0;
+            final Disposable[] factories;
+            synchronized (availableStores) {
+                isDisposed = 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;
+                    }
+                }
+            }
+            /*
+             * Factory disposal must be done outside the synchronized block.
+             */
+            while (--count >= 0) {
+                factories[count].dispose();
+            }
+        } finally {
+            synchronized (findPool) {
+                findPool.clear();
+            }
+            cache.clear();
+            authority = null;
         }
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -32,9 +32,10 @@ import org.opengis.util.GenericName;
 import org.opengis.util.NameFactory;
 import org.opengis.util.FactoryException;
 import org.opengis.util.InternationalString;
-import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.internal.util.Citations;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.util.iso.AbstractFactory;
+import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
 
@@ -79,8 +80,12 @@ public abstract class GeodeticAuthorityF
 
     /**
      * Returns the organization or party responsible for definition and maintenance of the database.
+     * This method may return {@code null} if it can not obtain this information, for example because
+     * the connection to a database is not available.
      *
-     * @return The organization responsible for definition of the database.
+     * @return The organization responsible for definition of the database, or {@code null} if unknown.
+     *
+     * @see #getVendor()
      */
     @Override
     public abstract Citation getAuthority();
@@ -92,7 +97,7 @@ public abstract class GeodeticAuthorityF
      * <p>The default implementation returns always {@code null}.</p>
      *
      * @return A description of the underlying backing store, or {@code null} if none.
-     * @throws FactoryException if a failure occurs while fetching the backing store description.
+     * @throws FactoryException if a failure occurred while fetching the backing store description.
      */
     public InternationalString getBackingStoreDescription() throws FactoryException {
         return null;
@@ -751,7 +756,7 @@ public abstract class GeodeticAuthorityF
         final GenericName name  = nameFactory.parseGenericName(null, code);
         if (name instanceof ScopedName) {
             final GenericName scope = ((ScopedName) name).path();
-            if (Citations.identifierMatches(getAuthority(), scope.toString())) {
+            if (Citations.identifierMatches(getAuthority(), null, scope.toString())) {
                 return name.tip().toString().trim();
             }
         }
@@ -767,9 +772,10 @@ public abstract class GeodeticAuthorityF
      * @return An exception initialized with an error message built from the specified informations.
      */
     protected final NoSuchAuthorityCodeException noSuchAuthorityCode(final Class<?> type, final String code) {
-        final InternationalString authority = getAuthority().getTitle();
+        final String authority = Citations.getIdentifier(getAuthority(), false);
         return new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.NoSuchAuthorityCode_3,
-                   authority, type, code), authority.toString(), trimAuthority(code), code);
+                   (authority != null) ? authority : Vocabulary.formatInternational(Vocabulary.Keys.Untitled),
+                   type, code), authority, trimAuthority(code), code);
     }
 
     /**
@@ -795,7 +801,7 @@ public abstract class GeodeticAuthorityF
         } else {
             actual = object.getClass();
         }
-        throw new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.UnexpectedTypeForReference_3,
-                code, type, actual), getAuthority().getTitle().toString(), trimAuthority(code), code);
+        throw new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.UnexpectedTypeForReference_3, code, type, actual),
+                Citations.getIdentifier(getAuthority(), false), trimAuthority(code), code);
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -149,8 +149,9 @@ public class IdentifiedObjectFinder {
 
     /**
      * Returns the authority of the factory used by this finder.
+     * The default implementation delegates to {@link GeodeticAuthorityFactory#getAuthority()}.
      *
-     * @return The authority of the factory used for the searches.
+     * @return The authority of the factory used for the searches, or {@code null} if unknown.
      * @throws FactoryException If an error occurred while fetching the authority.
      */
     public Citation getAuthority() throws FactoryException {

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java?rev=1718735&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -0,0 +1,79 @@
+/*
+ * 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.referencing.factory;
+
+import org.opengis.util.FactoryException;
+
+
+/**
+ * Thrown when a factory can not be created because an optional resource is missing.
+ * The most common case is when the {@link org.apache.sis.referencing.factory.epsg.EPSGFactory}
+ * has no connection to an EPSG database.
+ *
+ * @author  Martin Desruisseaux (IRD)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ *
+ * @see CachingAuthorityFactory#createBackingStore()
+ */
+public class UnavailableFactoryException extends FactoryException {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = -661925454228937249L;
+
+    /**
+     * Construct an exception with no detail message.
+     */
+    public UnavailableFactoryException() {
+    }
+
+    /**
+     * Construct an exception with the specified detail message.
+     *
+     * @param  message The detail message. The detail message is saved
+     *         for later retrieval by the {@link #getMessage()} method.
+     */
+    public UnavailableFactoryException(String message) {
+        super(message);
+    }
+
+    /**
+     * Construct an exception with the specified cause.
+     *
+     * @param  cause The cause for this exception. The cause is saved
+     *         for later retrieval by the {@link #getCause()} method.
+     */
+    public UnavailableFactoryException(Throwable cause) {
+        super(cause.getLocalizedMessage(), cause);
+    }
+
+    /**
+     * Construct an exception with the specified detail message and cause.
+     * The cause is the exception thrown in the underlying database
+     * (e.g. {@link java.io.IOException} or {@link java.sql.SQLException}).
+     *
+     * @param  message The detail message. The detail message is saved
+     *         for later retrieval by the {@link #getMessage()} method.
+     * @param  cause The cause for this exception. The cause is saved
+     *         for later retrieval by the {@link #getCause()} method.
+     */
+    public UnavailableFactoryException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -24,97 +24,49 @@ import org.apache.sis.util.logging.Loggi
 
 /**
  * A thread executing short tasks after some (potentially zero nanosecond) delay.
- * This thread is reserved to internal SIS usage - no user code shall be executed here.
- * All submitted tasks shall be very quick, since there is only one thread shared by everyone.
+ * This class should be reserved to internal SIS usage without user's code.
+ * In practice some user code may be indirectly executed through SIS tasks invoking overrideable methods.
+ * But all submitted tasks shall be very quick, since there is only one thread shared by everyone.
  *
- * <div class="note"><b>Note:</b>
- * In practice some user code may be indirectly executed, since some SIS tasks invoke overrideable methods.
- * We may need to revisit the {@code DelayedExecutor} design in a future version if the above happens to be
- * a problem. For example we may allow the user to specify an application-wide scheduled executor and delegate
- * the tasks to that executor.</div>
- *
- * The methods for use in this class are:
+ * <p>The methods for use in this class are:</p>
  * <ul>
- *   <li>{@link #executeDaemonTask(DelayedRunnable)}</li>
- *   <li>{@link #schedule(DelayedRunnable)}</li>
+ *   <li>{@link #schedule(Runnable, long)}</li>
  * </ul>
  *
  * <div class="section">Comparison with {@code java.util.concurrent}</div>
- * We tried to use {@link java.util.concurrent.ScheduledThreadPoolExecutor} in a previous version,
- * but it seems more suitable to heavier tasks in applications controlling their own executor. For
- * example {@code ScheduledThreadPoolExecutor} acts as a fixed-sized pool, thus forcing us to use
- * only one thread if we don't want to waste resources (profiling shows that even a single thread
- * has very low activity). The {@code ThreadPoolExecutor} super-class is more flexible but still
- * have a quite aggressive policy on threads creation, and doesn't handle delayed tasks by itself.
- * We could combine both worlds with a {@code ThreadPoolExecutor} using a {@code DelayedQueue},
- * but it forces us to declare a core pool size of 0 otherwise {@code ThreadPoolExecutor} tries
- * to execute the tasks immediately without queuing them. Combined with the {@code DelayedQueue}
- * characteristics (being an unbounded queue), this result in {@code ThreadPoolExecutor} never
- * creating more than one thread (because it waits for the queue to reject a task before to create
- * more threads than the pool size).
+ * We tried to use {@link java.util.concurrent.ScheduledThreadPoolExecutor} in a previous SIS version,
+ * but its "fixed-sized pool" design forces us to use only one thread if we do not want to waste resources
+ * (profiling shows that even a single thread has very low activity), which reduces the interest of that class.
+ * Combination of {@code ThreadPoolExecutor} super-class with {@code DelayedQueue} were not successful neither.
  *
- * <p>Given that it seems difficult to configure {@code (Scheduled)ThreadPoolExecutor} in such
- * a way that two or more threads are created only when really needed, given that using those
- * thread pools seems an overkill when the pool size is fixed to one thread, given that our
- * profiling has show very low activity for that single thread anyway, and given that we do
- * not need cancellation and shutdown services for house keeping tasks (this is a daemon thread),
+ * <p>Given that it:</p>
+ * <ul>
+ *   <li>it seems difficult to configure {@code (Scheduled)ThreadPoolExecutor} in such a way
+ *       that two or more threads are created only when really needed,</li>
+ *   <li>using those executor services seems an overkill when the pool size is fixed to one thread,</li>
+ *   <li>our profiling has show very low activity for that single thread anyway,</li>
+ *   <li>we do not need cancellation and shutdown services for house keeping tasks (this is a daemon thread),</li>
+ * </ul>
  * a more lightweight solution seems acceptable here. Pseudo-benchmarking using the
- * {@code CacheTest.stress()} tests suggests that the lightweight solution is faster.</p>
- *
- * <div class="section">Future evolution</div>
- * We may remove (again) this class in a future SIS evolution if we happen to need an executor anyway.
- * However it may be better to wait and see what are the executor needs. Setting up an executor implies
- * choosing many arbitrary parameter values like the number of core threads, maximum threads, idle time,
- * queue capacity, etc. Furthermore some platforms (e.g. MacOS) provide OS-specific implementations
- * integrating well in their environment. We may want to let the user provides the executor of his
- * choice, or we way want to have more profiling data for choosing an appropriate executor. But we
- * may need to find some way to give priority to SIS tasks, since most of them are for releasing
- * resources - in which case quick execution probably help the system to run faster.
- * However before to switch from the lightweight solution to a more heavy solution,
- * micro-benchmarking is desirable. The {@code CacheTest.stress()} tests can be used
- * in first approximation.
+ * {@code CacheTest.stress()} tests suggests that the lightweight solution is faster.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.7
  * @module
  *
  * @see <a href="https://issues.apache.org/jira/browse/SIS-76">SIS-76</a>
  */
 public final class DelayedExecutor extends DaemonThread {
     /**
-     * Executes the given short task in a daemon thread. This method shall be invoked for
-     * Apache SIS tasks only, <strong>not</strong> for arbitrary user task. The task must
-     * completes quickly, because we will typically use only one thread for all submitted
-     * tasks. Completion of the task shall not be critical, since the JVM is allowed to
-     * shutdown before task completion.
-     *
-     * <div class="section">Future evolution</div>
-     * If {@code DelayedExecutor} is removed in a future SIS version in favor of JDK6 executors,
-     * then the method signature will probably be {@code Executors.execute(Runnable)}.
-     *
-     * @param task The task to execute.
-     */
-    public static void executeDaemonTask(final DelayedRunnable task) {
-        QUEUE.add(task);
-    }
-
-    /**
      * Schedules the given short task for later execution in a daemon thread.
-     * The task will be executed after the delay specified by {@link DelayedRunnable#getDelay}
-     * The task must completes quickly, because we will typically use only one thread for all
-     * submitted tasks. Completion of the task shall not be critical, since the JVM is allowed
-     * to shutdown before task completion.
-     *
-     * <div class="section">Future evolution</div>
-     * If {@code DelayedExecutor} is removed in a future SIS version in favor of JDK6 executors,
-     * then the method signature will probably be {@code Executors.schedule(Runnable, long, TimeUnit)}.
+     * The task will be executed after the delay specified by {@link DelayedRunnable#getDelay(TimeUnit)}
+     * The task must completes quickly, because we will typically use only one thread for all submitted tasks.
+     * Completion of the task shall not be critical, since the JVM is allowed to shutdown before task completion.
      *
      * @param task The task to schedule for later execution.
      */
     public static void schedule(final DelayedRunnable task) {
-        // For now the implementation is identical to 'execute'. However it may become
-        // different if we choose to use a library-wide executor in a future SIS version.
         QUEUE.add(task);
     }
 

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -39,6 +39,12 @@ public abstract class DelayedRunnable im
      * Time of execution of this task, in nanoseconds provided by {@link System#nanoTime()}.
      * In the particular case of the {@link Immediate} subclass, the meaning of this field is
      * modified: it is rather an ordinal value used for preserving task order.
+     *
+     * <div class="note"><b>Note:</b>
+     * we use {@link System#nanoTime()} instead than {@link System#currentTimeMillis()} because
+     * the later is not guaranteed to be monotonic: {@code currentTimeMillis} may change abruptly
+     * for example if the user adjusts the clock of his operating system.
+     * </div>
      */
     final long timestamp;
 
@@ -74,7 +80,7 @@ public abstract class DelayedRunnable im
     @Override
     public int compareTo(final Delayed other) {
         if (other instanceof Immediate) {
-            return +1; // "Immediate" tasks always have precedence over delayed ones.
+            return +1;                      // "Immediate" tasks always have precedence over delayed ones.
         }
         return Long.signum(timestamp - ((DelayedRunnable) other).timestamp);
     }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -16,8 +16,6 @@
  */
 package org.apache.sis.internal.system;
 
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.ExecutorService;
 import org.apache.sis.util.Static;
 import org.apache.sis.util.logging.Logging;
 
@@ -34,7 +32,7 @@ import org.apache.sis.util.logging.Loggi
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.7
  * @module
  */
 final class Threads extends Static {
@@ -83,19 +81,6 @@ final class Threads extends Static {
     static DaemonThread lastCreatedDaemon;
 
     /**
-     * Executor to shutdown. This is a copy of the {@code <removed class>} executor static final
-     * field, copied here only when the {@code <removed class>} class is loaded and initialized.
-     * We proceed that way for avoiding dependency from {@code Threads} to {@code <removed class>}.
-     *
-     * <p>This field has been temporarily fixed to {@code null} since we removed executor as of
-     * <a href="https://issues.apache.org/jira/browse/SIS-76">SIS-76</a>. However we may revert
-     * to a modifiable field in a future version if we choose to use executor again. In the main
-     * time, we declare this field as {@code final} for allowing the Javac compiler to omit all
-     * compiled code inside {@code if (executor != null)} block.</p>
-     */
-    private static final ExecutorService executor = null;
-
-    /**
      * Do not allows instantiation of this class.
      */
     private Threads() {
@@ -114,20 +99,6 @@ final class Threads extends Static {
      *         we were waiting for the daemon threads to die.
      */
     static synchronized void shutdown(final long stopWaitingAt) throws InterruptedException {
-        if (executor != null) {
-            executor.shutdown();
-            /*
-             * Wait for work completion. In theory this is not necessary since the daemon
-             * tasks are only house-cleaning work. We nevertheless wait for their completion
-             * as a safety. There tasks are supposed to be short.
-             */
-            final long delay = stopWaitingAt - System.nanoTime();
-            if (delay > 0) {
-                executor.awaitTermination(delay, TimeUnit.NANOSECONDS);
-                // Even if the tasks didn't completed, continue without waiting for them.
-                // We can not log at this point, since the logging framework may be shutdown.
-            }
-        }
         DaemonThread.killAll(lastCreatedDaemon, stopWaitingAt);
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/package-info.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/package-info.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/package-info.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -25,7 +25,7 @@
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.7
  * @module
  */
 package org.apache.sis.internal.system;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -32,10 +32,9 @@ import org.apache.sis.util.Disposable;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.system.DelayedRunnable;
+import org.apache.sis.internal.system.DelayedExecutor;
 import org.apache.sis.internal.system.ReferenceQueueConsumer;
 
-import static org.apache.sis.internal.system.DelayedExecutor.executeDaemonTask;
-
 // Branch-dependent imports
 import java.util.function.Supplier;
 
@@ -304,7 +303,7 @@ public class Cache<K,V> extends Abstract
         final Object previous;
         if (value != null) {
             previous = map.put(key, value);
-            executeDaemonTask(new Strong(key, value));
+            DelayedExecutor.schedule(new Strong(key, value));
         } else {
             previous = map.remove(key);
         }
@@ -383,8 +382,8 @@ public class Cache<K,V> extends Abstract
             final Reference<V> ref = (Reference<V>) value;
             final V result = ref.get();
             if (result != null && map.replace(key, ref, result)) {
-                ref.clear(); // Prevents the reference from being enqueued.
-                executeDaemonTask(new Strong(key, result));
+                ref.clear();                        // Prevents the reference from being enqueued.
+                DelayedExecutor.schedule(new Strong(key, result));
             }
             return result;
         }
@@ -476,8 +475,8 @@ public class Cache<K,V> extends Abstract
                      * would be useless.
                      */
                     if (map.replace(key, ref, result)) {
-                        ref.clear(); // Prevents the reference from being enqueued.
-                        executeDaemonTask(new Strong(key, result));
+                        ref.clear();                        // Prevents the reference from being enqueued.
+                        DelayedExecutor.schedule(new Strong(key, result));
                     }
                     return new Simple<>(result);
                 }
@@ -710,7 +709,7 @@ public class Cache<K,V> extends Abstract
                 lock.unlock();
             }
             if (done) {
-                executeDaemonTask(this);
+                DelayedExecutor.schedule(this);
             }
         }
 

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Tue Dec  8 22:50:00 2015
@@ -201,6 +201,11 @@ public final class Errors extends Indexe
         public static final short DirectoryNotExpected_1 = 165;
 
         /**
+         * The factory has been disposed.
+         */
+        public static final short DisposedFactory = 206;
+
+        /**
          * Element “{0}” is duplicated.
          */
         public static final short DuplicatedElement_1 = 16;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Tue Dec  8 22:50:00 2015
@@ -51,6 +51,7 @@ CloneNotSupported_1               = Can
 ColinearAxisDirections_2          = Axis directions {0} and {1} are colinear.
 DeadThread_1                      = Thread \u201c{0}\u201d is dead.
 DirectoryNotExpected_1            = The \u201c{0}\u201d file points to a directory instead of a regular file.
+DisposedFactory                   = The factory has been disposed.
 DuplicatedElement_1               = Element \u201c{0}\u201d is duplicated.
 DuplicatedIdentifier_1            = Name or identifier \u201c{0}\u201d is used more than once.
 DuplicatedOption_1                = Option \u201c{0}\u201d is duplicated.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1718735&r1=1718734&r2=1718735&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Tue Dec  8 22:50:00 2015
@@ -48,6 +48,7 @@ CloneNotSupported_1               = Un o
 ColinearAxisDirections_2          = Les directions d\u2019axes {0} et {1} sont colin\u00e9aires.
 DeadThread_1                      = La t\u00e2che \u00ab\u202f{0}\u202f\u00bb est morte.
 DirectoryNotExpected_1            = Le fichier \u00ab\u202f{0}\u202f\u00bb d\u00e9signe un r\u00e9pertoire plut\u00f4t qu\u2019un fichier r\u00e9gulier.
+DisposedFactory                   = La fabrique a \u00e9t\u00e9 dispos\u00e9e.
 DuplicatedElement_1               = L\u2019\u00e9lement \u00ab\u202f{0}\u202f\u00bb est dupliqu\u00e9.
 DuplicatedIdentifier_1            = Le nom ou l\u2019identifiant \u00ab\u202f{0}\u202f\u00bb est utilis\u00e9 plus d\u2019une fois.
 DuplicatedOption_1                = L\u2019option \u00ab\u202f{0}\u202f\u00bb est dupliqu\u00e9e.



Mime
View raw message