From ed08a89377a333c10202ead88d355e16bcb3a0fd Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 27 Feb 2014 23:31:10 +0000
Subject: [PATCH] Backport fix for OPENDJ-1197: API is lacking functionality to specify TCP connect timeout
---
opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/TimeoutChecker.java | 100 ++++++++++++++++++++++++++++++--------------------
1 files changed, 60 insertions(+), 40 deletions(-)
diff --git a/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/TimeoutChecker.java b/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/TimeoutChecker.java
index b169be8..4631bbb 100644
--- a/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/TimeoutChecker.java
+++ b/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/TimeoutChecker.java
@@ -22,9 +22,8 @@
*
*
* Copyright 2010 Sun Microsystems, Inc.
- * Portions copyright 2013 ForgeRock AS.
+ * Portions copyright 2013-2014 ForgeRock AS.
*/
-
package com.forgerock.opendj.ldap;
import static com.forgerock.opendj.util.StaticUtils.DEBUG_LOG;
@@ -37,13 +36,17 @@
import com.forgerock.opendj.util.ReferenceCountedObject;
/**
- * Checks connection for pending requests that have timed out.
+ * Checks {@code TimeoutEventListener listeners} for events that have timed out.
+ * <p>
+ * All listeners registered with the {@code #addListener()} method are called
+ * back with {@code TimeoutEventListener#handleTimeout()} to be able to handle
+ * the timeout.
*/
-final class TimeoutChecker {
+public final class TimeoutChecker {
/**
- * Global reference counted instance.
+ * Global reference on the timeout checker.
*/
- static final ReferenceCountedObject<TimeoutChecker> TIMEOUT_CHECKER =
+ public static final ReferenceCountedObject<TimeoutChecker> TIMEOUT_CHECKER =
new ReferenceCountedObject<TimeoutChecker>() {
@Override
protected void destroyInstance(final TimeoutChecker instance) {
@@ -62,11 +65,11 @@
private final Object stateLock = new Object();
/**
- * The connection set must be safe from CMEs because expiring requests can
- * cause the connection to be closed.
+ * The listener set must be safe from CMEs. For example, if the listener is
+ * a connection, expiring requests can cause the connection to be closed.
*/
- private final Set<LDAPConnection> connections =
- newSetFromMap(new ConcurrentHashMap<LDAPConnection, Boolean>());
+ private final Set<TimeoutEventListener> listeners =
+ newSetFromMap(new ConcurrentHashMap<TimeoutEventListener, Boolean>());
/**
* Used to signal thread shutdown.
@@ -74,48 +77,46 @@
private volatile boolean shutdownRequested = false;
/**
- * Used for signalling that new connections have been added while performing
- * timeout processing.
+ * Contains the minimum delay for listeners which were added while the
+ * timeout check was not sleeping (i.e. while it was processing listeners).
*/
- private volatile boolean pendingNewConnections = false;
+ private volatile long pendingListenerMinDelay = Long.MAX_VALUE;
private TimeoutChecker() {
- final Thread checkerThread = new Thread("OpenDJ LDAP SDK Connection Timeout Checker") {
+ final Thread checkerThread = new Thread("OpenDJ LDAP SDK Timeout Checker") {
@Override
public void run() {
DEBUG_LOG.fine("Timeout Checker Starting");
while (!shutdownRequested) {
- final long currentTime = System.currentTimeMillis();
- long delay = 0;
/*
- * New connections may be added during iteration and may be
- * missed resulting in the timeout checker waiting longer
- * than it should, or even forever (e.g. if the new
- * connection is the first).
+ * New listeners may be added during iteration and may not
+ * be included in the computation of the new delay. This
+ * could potentially result in the timeout checker waiting
+ * longer than it should, or even forever (e.g. if the new
+ * listener is the first).
*/
- pendingNewConnections = false;
- for (final LDAPConnection connection : connections) {
+ final long currentTime = System.currentTimeMillis();
+ long delay = Long.MAX_VALUE;
+ pendingListenerMinDelay = Long.MAX_VALUE;
+ for (final TimeoutEventListener listener : listeners) {
if (DEBUG_LOG.isLoggable(Level.FINER)) {
- DEBUG_LOG.finer("Checking connection " + connection + " delay = "
+ DEBUG_LOG.finer("Checking listener " + listener + " delay = "
+ delay);
}
-
// May update the connections set.
- final long newDelay = connection.cancelExpiredRequests(currentTime);
+ final long newDelay = listener.handleTimeout(currentTime);
if (newDelay > 0) {
- if (delay > 0) {
- delay = Math.min(newDelay, delay);
- } else {
- delay = newDelay;
- }
+ delay = Math.min(newDelay, delay);
}
}
try {
synchronized (stateLock) {
- if (shutdownRequested || pendingNewConnections) {
- // Loop immediately.
- pendingNewConnections = false;
+ // Include any pending listener delays.
+ delay = Math.min(pendingListenerMinDelay, delay);
+ if (shutdownRequested) {
+ // Stop immediately.
+ break;
} else if (delay <= 0) {
/*
* If there is at least one connection then the
@@ -137,16 +138,35 @@
checkerThread.start();
}
- void addConnection(final LDAPConnection connection) {
- connections.add(connection);
- synchronized (stateLock) {
- pendingNewConnections = true;
- stateLock.notifyAll();
+ /**
+ * Registers a timeout event listener for timeout notification.
+ *
+ * @param listener
+ * The timeout event listener.
+ */
+ public void addListener(final TimeoutEventListener listener) {
+ /*
+ * Only add the listener if it has a non-zero timeout. This assumes that
+ * the timeout is fixed.
+ */
+ final long timeout = listener.getTimeout();
+ if (timeout > 0) {
+ listeners.add(listener);
+ synchronized (stateLock) {
+ pendingListenerMinDelay = Math.min(pendingListenerMinDelay, timeout);
+ stateLock.notifyAll();
+ }
}
}
- void removeConnection(final LDAPConnection connection) {
- connections.remove(connection);
+ /**
+ * Deregisters a timeout event listener for timeout notification.
+ *
+ * @param listener
+ * The timeout event listener.
+ */
+ public void removeListener(final TimeoutEventListener listener) {
+ listeners.remove(listener);
// No need to signal.
}
--
Gitblit v1.10.0