Author: desruisseaux
Date: Wed Jan  9 07:55:21 2013
New Revision: 1430721

URL: http://svn.apache.org/viewvc?rev=1430721&view=rev
Log:
Implemented removal of (un)marshaller after a timeout.

Modified:
    
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java

Modified: 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java?rev=1430721&r1=1430720&r2=1430721&view=diff
==============================================================================
--- 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java
 (original)
+++ 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java
 Wed Jan  9 07:55:21 2013
@@ -18,14 +18,17 @@ package org.apache.sis.xml;
 
 import java.util.Map;
 import java.util.Deque;
-import java.util.LinkedList;
 import java.util.Collections;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.atomic.AtomicBoolean;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
 import net.jcip.annotations.ThreadSafe;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.internal.util.DelayedExecutor;
+import org.apache.sis.internal.util.DelayedRunnable;
 import org.apache.sis.internal.jaxb.AdapterReplacement;
 
 
@@ -51,15 +54,15 @@ import org.apache.sis.internal.jaxb.Adap
  * @module
  *
  * @see XML
- *
- * @todo Need a timeout for disposing marshallers that have been unused for a 
while.
  */
 @ThreadSafe
 public class MarshallerPool {
     /**
-     * Maximal amount of marshallers and unmarshallers to keep.
+     * Amount of nanoseconds to wait before to remove unused (un)marshallers.
+     * This is a very approximative value: actual timeout will not be shorter,
+     * but may be twice longer.
      */
-    private static final int CAPACITY = 16;
+    private static final long TIMEOUT = 15000000000L; // 15 seconds.
 
     /**
      * The key to be used in the map given to the constructors for specifying 
the root namespace.
@@ -85,16 +88,38 @@ public class MarshallerPool {
     private final Object mapper;
 
     /**
-     * The pool of marshaller. This pool is initially empty
-     * and will be filled with elements as needed.
+     * The pool of marshaller. This pool is initially empty and will be filled 
with elements as needed.
+     * Marshallers (if any) shall be fetched using the {@link Deque#poll()} 
method and, after use,
+     * given back to the pool using the {@link Deque#push(Object)} method.
+     *
+     * <p>This queue must be a thread-safe implementation, since it will not 
be invoked in
+     * synchronized block.</p>
+     *
+     * @see #acquireMarshaller()
+     * @see #release(Marshaller)
+     */
+    private final Deque<Marshaller> marshallers;
+
+    /**
+     * The pool of unmarshaller. This pool is initially empty and will be 
filled with elements as needed.
+     * Unmarshallers (if any) shall be fetched using the {@link Deque#poll()} 
method and, after use,
+     * given back to the pool using the {@link Deque#push(Object)} method.
+     *
+     * <p>This queue must be a thread-safe implementation, since it will not 
be invoked in
+     * synchronized block.</p>
+     *
+     * @see #acquireUnmarshaller()
+     * @see #release(Unmarshaller)
      */
-    private final Deque<Marshaller> marshallers = new LinkedList<>();
+    private final Deque<Unmarshaller> unmarshallers;
 
     /**
-     * The pool of unmarshaller. This pool is initially empty
-     * and will be filled with elements as needed.
+     * {@code true} if a task has been scheduled for removing expired 
(un)marshallers,
+     * or {@code false} if no removal task is currently scheduled.
+     *
+     * @see #scheduleRemoval()
      */
-    private final Deque<Unmarshaller> unmarshallers = new LinkedList<>();
+    private final AtomicBoolean isRemovalScheduled;
 
     /**
      * Creates a new factory for the given class to be bound, with a default 
empty namespace.
@@ -120,11 +145,14 @@ public class MarshallerPool {
 
     /**
      * Creates a new factory for the given packages, with a default empty 
namespace.
-     * The separator character for the packages is the colon.
+     * The separator character for the packages is the colon. Example:
      *
-     * @param  packages         The packages in which JAXB will search for 
annotated classes to be bound,
-     *                          for example {@code 
"org.apache.sis.metadata.iso:org.apache.sis.metadata.iso.citation"}.
-     * @throws JAXBException    If the JAXB context can not be created.
+     * {@preformat text
+     *     "org.apache.sis.metadata.iso:org.apache.sis.metadata.iso.citation"
+     * }
+     *
+     * @param  packages      The colon-separated list of packages in which 
JAXB will search for annotated classes.
+     * @throws JAXBException If the JAXB context can not be created.
      */
     public MarshallerPool(final String packages) throws JAXBException {
         this(Collections.<String,String>emptyMap(), packages);
@@ -136,8 +164,7 @@ public class MarshallerPool {
      * in this class like {@link #ROOT_NAMESPACE_KEY}.
      *
      * @param  properties    The set of properties to be given to the pool.
-     * @param  packages      The packages in which JAXB will search for 
annotated classes to be bound,
-     *                       for example {@code 
"org.apache.sis.metadata.iso:org.apache.sis.metadata.iso.citation"}.
+     * @param  packages      The colon-separated list of packages in which 
JAXB will search for annotated classes.
      * @throws JAXBException If the JAXB context can not be created.
      */
     public MarshallerPool(final Map<String,String> properties, final String 
packages) throws JAXBException {
@@ -173,50 +200,112 @@ public class MarshallerPool {
         }
         /*
          * Instantiates the OGCNamespacePrefixMapper appropriate for the 
implementation
-         * we just detected.
+         * we just detected. Note that we may get NoClassDefFoundError instead 
than the
+         * usual ClassNotFoundException if the class was found but its parent 
class has
+         * not been found.
          */
         try {
             mapper = 
Class.forName(type).getConstructor(String.class).newInstance(rootNamespace);
         } catch (ReflectiveOperationException | NoClassDefFoundError 
exception) {
-            // The NoClassDefFoundError is because of our trick using 
"geotk-provided".
             throw new JAXBException("Unsupported JAXB implementation.", 
exception);
         }
-    }
-
-    /**
-     * Returns the marshaller or unmarshaller to use from the given queue.
-     * If the queue is empty, returns {@code null}.
-     */
-    private static <T> T acquire(final Deque<T> queue) {
-        synchronized (queue) {
-            return queue.pollLast();
-        }
+        marshallers        = new ConcurrentLinkedDeque<>();
+        unmarshallers      = new ConcurrentLinkedDeque<>();
+        isRemovalScheduled = new AtomicBoolean();
     }
 
     /**
      * Marks the given marshaller or unmarshaller available for further reuse.
+     * This method:
+     *
+     * <ul>
+     *   <li>{@link Pooled#reset() Resets} the (un)marshaller to its initial 
state.</li>
+     *   <li>{@linkplain Deque#push(Object) Pushes} the (un)marshaller in the 
given queue.</li>
+     *   <li>Registers a delayed task for disposing expired (un)marshallers 
after the timeout.</li>
+     * </ul>
      */
-    private static <T> void release(final Deque<T> queue, final T marshaller) {
+    private <T> void release(final Deque<T> queue, final T marshaller) {
         try {
             ((Pooled) marshaller).reset();
         } catch (JAXBException exception) {
-            // Not expected to happen because the we are supposed
+            // Not expected to happen because we are supposed
             // to reset the properties to their initial values.
             Logging.unexpectedException(MarshallerPool.class, "release", 
exception);
             return;
         }
-        synchronized (queue) {
-            queue.addLast(marshaller);
-            while (queue.size() > CAPACITY) {
-                // Remove the least recently used marshallers.
-                queue.removeFirst();
+        queue.push(marshaller);
+        scheduleRemoval();
+    }
+
+    /**
+     * Schedule a new task for removing expired (un)marshallers if no such 
task is currently
+     * registered. If a task is already registered, then this method does 
nothing. Note that
+     * this task will actually wait for a longer time than the {@link 
#TIMEOUT} value before
+     * to execute, in order to increase the chances to process many 
(un)marshallers at once.
+     */
+    private void scheduleRemoval() {
+        if (isRemovalScheduled.compareAndSet(false, true)) {
+            DelayedExecutor.schedule(new DelayedRunnable(System.nanoTime() + 
2*TIMEOUT) {
+                @Override public void run() {
+                    removeExpired();
+                }
+            });
+        }
+    }
+
+    /**
+     * Invoked from the task scheduled by {@link #scheduleRemoval()} for 
removing expired
+     * (un)marshallers. If some (un)marshallers remain after execution of this 
task, then
+     * this method will reschedule a new task for checking again later.
+     */
+    final void removeExpired() {
+        isRemovalScheduled.set(false);
+        final long now = System.nanoTime();
+        if (!removeExpired(marshallers, now) | // Really |, not ||
+            !removeExpired(unmarshallers, now))
+        {
+            scheduleRemoval();
+        }
+    }
+
+    /**
+     * Removes expired (un)marshallers from the given queue.
+     *
+     * @param  <T>   Either {@code Marshaller} or {@code Unmarshaller} type.
+     * @param  queue The queue from which to remove expired (un)marshallers.
+     * @param  now   Current value of {@link System#nanoTime()}.
+     * @return {@code true} if the queue became empty as a result of this 
method call.
+     */
+    private static <T> boolean removeExpired(final Deque<T> queue, final long 
now) {
+        T next;
+        while ((next = queue.peekLast()) != null) {
+            /*
+             * The above line fetched the oldest (un)marshaller without 
removing it.
+             * If the timeout is not yet elapsed, do not remove that 
(un)marshaller.
+             * Since marshallers are enqueued in chronological order, the next 
ones
+             * should be yet more recent, so it is not worth to continue the 
search.
+             */
+            if (now - ((Pooled) next).resetTime < TIMEOUT) {
+                return false;
+            }
+            /*
+             * Now remove the (un)marshaller and check again the timeout. This 
second
+             * check is because the oldest (un)marshaller may have changed 
concurrently
+             * (depending on the queue implementation, which depends on the 
target JDK).
+             * If such case, restore the (un)marshaller on the queue.
+             */
+            next = queue.pollLast();
+            if (now - ((Pooled) next).resetTime < TIMEOUT) {
+                queue.addLast(next);
+                return false;
             }
         }
+        return true;
     }
 
     /**
      * Returns a JAXB marshaller from the pool. If there is no marshaller 
currently available
-     * in the pool, then this method will {@linkplain #createMarshaller 
create} a new one.
+     * in the pool, then this method will {@linkplain #createMarshaller() 
create} a new one.
      *
      * <p>This method shall be used as below:</p>
      *
@@ -234,7 +323,7 @@ public class MarshallerPool {
      * @throws JAXBException If an error occurred while creating and 
configuring a marshaller.
      */
     public Marshaller acquireMarshaller() throws JAXBException {
-        Marshaller marshaller = acquire(marshallers);
+        Marshaller marshaller = marshallers.poll();
         if (marshaller == null) {
             marshaller = new PooledMarshaller(createMarshaller(), internal);
         }
@@ -243,7 +332,7 @@ public class MarshallerPool {
 
     /**
      * Returns a JAXB unmarshaller from the pool. If there is no unmarshaller 
currently available
-     * in the pool, then this method will {@linkplain #createUnmarshaller 
create} a new one.
+     * in the pool, then this method will {@linkplain #createUnmarshaller() 
create} a new one.
      *
      * <p>This method shall be used as below:</p>
      *
@@ -261,7 +350,7 @@ public class MarshallerPool {
      * @throws JAXBException If an error occurred while creating and 
configuring the unmarshaller.
      */
     public Unmarshaller acquireUnmarshaller() throws JAXBException {
-        Unmarshaller unmarshaller = acquire(unmarshallers);
+        Unmarshaller unmarshaller = unmarshallers.poll();
         if (unmarshaller == null) {
             unmarshaller = new PooledUnmarshaller(createUnmarshaller(), 
internal);
         }

Modified: 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java?rev=1430721&r1=1430720&r2=1430721&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java 
(original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java 
Wed Jan  9 07:55:21 2013
@@ -132,6 +132,13 @@ abstract class Pooled {
     private int bitMasks;
 
     /**
+     * The {@link System#nanoTime()} value of the last call to {@link 
#reset()}.
+     * This is used for disposing (un)marshallers that have not been used for 
a while,
+     * since {@code reset()} is invoked just before to push a (un)marshaller 
in the pool.
+     */
+    volatile long resetTime;
+
+    /**
      * Default constructor.
      *
      * @param internal {@code true} if the JAXB implementation is the one 
bundled in JDK 6,
@@ -153,7 +160,9 @@ abstract class Pooled {
     }
 
     /**
-     * Resets the (un)marshaller to its initial state.
+     * Releases resources and resets the (un)marshaller to its initial state.
+     * This method is invoked by {@link MarshallerPool} just before to push a
+     * (un)marshaller in the pool after its usage.
      *
      * @throws JAXBException If an error occurred while restoring a property.
      */
@@ -169,6 +178,7 @@ abstract class Pooled {
         locale     = null;
         timezone   = null;
         bitMasks   = initialBitMasks();
+        resetTime  = System.nanoTime();
     }
 
     /**


Reply via email to