From c50c2de771f32a0fb4bd530da59d64ad7ad0af4e Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Wed, 03 Jan 2007 16:12:10 +0000
Subject: [PATCH] Update the task backend to properly acquire read locks on task and recurring task entries when evaluating them during search operations, and to acquire write locks for the entries when they are being updated. This should eliminate the possibility of concurrent modification exceptions being thrown in cases where a client issues a search for a task entry at the same time the task entry is being updated within the server.
---
opendj-sdk/opends/src/server/org/opends/server/backends/task/Task.java | 158 +++++++++++++++-------
opendj-sdk/opends/src/server/org/opends/server/backends/task/RecurringTask.java | 23 +++
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java | 75 ++++++----
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java | 119 +++++++++++++++-
4 files changed, 284 insertions(+), 91 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/task/RecurringTask.java b/opendj-sdk/opends/src/server/org/opends/server/backends/task/RecurringTask.java
index 703c7d4..fb6182c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/task/RecurringTask.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/task/RecurringTask.java
@@ -37,6 +37,7 @@
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
@@ -63,6 +64,9 @@
+ // The DN of the entry that actually defines this task.
+ private DN recurringTaskEntryDN;
+
// The entry that actually defines this task.
private Entry recurringTaskEntry;
@@ -97,8 +101,9 @@
assert debugConstructor(CLASS_NAME, String.valueOf(taskScheduler),
String.valueOf(recurringTaskEntry));
- this.taskScheduler = taskScheduler;
- this.recurringTaskEntry = recurringTaskEntry;
+ this.taskScheduler = taskScheduler;
+ this.recurringTaskEntry = recurringTaskEntry;
+ this.recurringTaskEntryDN = recurringTaskEntry.getDN();
// Get the recurring task ID from the entry. If there isn't one, then fail.
@@ -278,6 +283,20 @@
/**
+ * Retrieves the DN of the entry containing the data for this recurring task.
+ *
+ * @return The DN of the entry containing the data for this recurring task.
+ */
+ public DN getRecurringTaskEntryDN()
+ {
+ assert debugEnter(CLASS_NAME, "getRecurringTaskEntryDN");
+
+ return recurringTaskEntryDN;
+ }
+
+
+
+ /**
* Retrieves the entry containing the data for this recurring task.
*
* @return The entry containing the data for this recurring task.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/task/Task.java b/opendj-sdk/opends/src/server/org/opends/server/backends/task/Task.java
index 42c7b90..28c511f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/task/Task.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/task/Task.java
@@ -37,6 +37,7 @@
import java.util.List;
import java.util.TimeZone;
import java.util.UUID;
+import java.util.concurrent.locks.Lock;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1OctetString;
@@ -44,6 +45,7 @@
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
@@ -75,6 +77,9 @@
+ // The DN for the task entry.
+ private DN taskEntryDN;
+
// The entry that actually defines this task.
private Entry taskEntry;
@@ -141,8 +146,9 @@
this.taskScheduler = taskScheduler;
this.taskEntry = taskEntry;
+ this.taskEntryDN = taskEntry.getDN();
- String taskDN = taskEntry.getDN().toString();
+ String taskDN = taskEntryDN.toString();
logMessageCounter = 0;
@@ -438,6 +444,20 @@
/**
+ * Retrieves the DN of the entry containing the definition for this task.
+ *
+ * @return The DN of the entry containing the definition for this task.
+ */
+ public final DN getTaskEntryDN()
+ {
+ assert debugEnter(CLASS_NAME, "getTaskEntryDN");
+
+ return taskEntryDN;
+ }
+
+
+
+ /**
* Retrieves the entry containing the definition for this task.
*
* @return The entry containing the definition for this task.
@@ -507,22 +527,32 @@
{
assert debugEnter(CLASS_NAME, "setTaskState", String.valueOf(taskState));
- this.taskState = taskState;
+ Lock lock = taskScheduler.writeLockEntry(taskEntryDN);
- AttributeType type =
- DirectoryServer.getAttributeType(ATTR_TASK_STATE.toLowerCase());
- if (type == null)
+ try
{
- type = DirectoryServer.getDefaultAttributeType(ATTR_TASK_STATE);
+ this.taskState = taskState;
+
+ AttributeType type =
+ DirectoryServer.getAttributeType(ATTR_TASK_STATE.toLowerCase());
+ if (type == null)
+ {
+ type = DirectoryServer.getDefaultAttributeType(ATTR_TASK_STATE);
+ }
+
+ LinkedHashSet<AttributeValue> values =
+ new LinkedHashSet<AttributeValue>();
+ values.add(new AttributeValue(type,
+ new ASN1OctetString(taskState.toString())));
+
+ ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+ attrList.add(new Attribute(type, ATTR_TASK_STATE, values));
+ taskEntry.putAttribute(type, attrList);
}
-
- LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
- values.add(new AttributeValue(type,
- new ASN1OctetString(taskState.toString())));
-
- ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
- attrList.add(new Attribute(type, ATTR_TASK_STATE, values));
- taskEntry.putAttribute(type, attrList);
+ finally
+ {
+ taskScheduler.unlockEntry(taskEntryDN, lock);
+ }
}
@@ -574,27 +604,37 @@
assert debugEnter(CLASS_NAME, "setActualStartTime",
String.valueOf(actualStartTime));
- this.actualStartTime = actualStartTime;
+ Lock lock = taskScheduler.writeLockEntry(taskEntryDN);
- AttributeType type = DirectoryServer.getAttributeType(
- ATTR_TASK_ACTUAL_START_TIME.toLowerCase());
- if (type == null)
+ try
{
- type = DirectoryServer.getDefaultAttributeType(
- ATTR_TASK_ACTUAL_START_TIME);
+ this.actualStartTime = actualStartTime;
+
+ AttributeType type = DirectoryServer.getAttributeType(
+ ATTR_TASK_ACTUAL_START_TIME.toLowerCase());
+ if (type == null)
+ {
+ type = DirectoryServer.getDefaultAttributeType(
+ ATTR_TASK_ACTUAL_START_TIME);
+ }
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_UTC_TIME);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date d = new Date(actualStartTime);
+ ASN1OctetString s = new ASN1OctetString(dateFormat.format(d));
+
+ LinkedHashSet<AttributeValue> values =
+ new LinkedHashSet<AttributeValue>();
+ values.add(new AttributeValue(type, s));
+
+ ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+ attrList.add(new Attribute(type, ATTR_TASK_ACTUAL_START_TIME, values));
+ taskEntry.putAttribute(type, attrList);
}
-
- SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_UTC_TIME);
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- Date d = new Date(actualStartTime);
- ASN1OctetString s = new ASN1OctetString(dateFormat.format(d));
-
- LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
- values.add(new AttributeValue(type, s));
-
- ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
- attrList.add(new Attribute(type, ATTR_TASK_ACTUAL_START_TIME, values));
- taskEntry.putAttribute(type, attrList);
+ finally
+ {
+ taskScheduler.unlockEntry(taskEntryDN, lock);
+ }
}
@@ -629,26 +669,37 @@
assert debugEnter(CLASS_NAME, "setCompletionTime",
String.valueOf(completionTime));
- this.completionTime = completionTime;
+ Lock lock = taskScheduler.writeLockEntry(taskEntryDN);
- AttributeType type = DirectoryServer.getAttributeType(
- ATTR_TASK_COMPLETION_TIME.toLowerCase());
- if (type == null)
+ try
{
- type = DirectoryServer.getDefaultAttributeType(ATTR_TASK_COMPLETION_TIME);
+ this.completionTime = completionTime;
+
+ AttributeType type = DirectoryServer.getAttributeType(
+ ATTR_TASK_COMPLETION_TIME.toLowerCase());
+ if (type == null)
+ {
+ type =
+ DirectoryServer.getDefaultAttributeType(ATTR_TASK_COMPLETION_TIME);
+ }
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_UTC_TIME);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date d = new Date(completionTime);
+ ASN1OctetString s = new ASN1OctetString(dateFormat.format(d));
+
+ LinkedHashSet<AttributeValue> values =
+ new LinkedHashSet<AttributeValue>();
+ values.add(new AttributeValue(type, s));
+
+ ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+ attrList.add(new Attribute(type, ATTR_TASK_COMPLETION_TIME, values));
+ taskEntry.putAttribute(type, attrList);
}
-
- SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_UTC_TIME);
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- Date d = new Date(completionTime);
- ASN1OctetString s = new ASN1OctetString(dateFormat.format(d));
-
- LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
- values.add(new AttributeValue(type, s));
-
- ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
- attrList.add(new Attribute(type, ATTR_TASK_COMPLETION_TIME, values));
- taskEntry.putAttribute(type, attrList);
+ finally
+ {
+ taskScheduler.unlockEntry(taskEntryDN, lock);
+ }
}
@@ -756,10 +807,11 @@
String.valueOf(severity), String.valueOf(messageID),
String.valueOf(messageString));
- StringBuilder buffer = new StringBuilder();
+ Lock lock = taskScheduler.writeLockEntry(taskEntryDN);
- synchronized (logMessages)
+ try
{
+ StringBuilder buffer = new StringBuilder();
buffer.append("[");
buffer.append(TimeThread.getLocalTime());
buffer.append("] severity=\"");
@@ -809,6 +861,10 @@
attrList.add(new Attribute(type, ATTR_TASK_LOG_MESSAGES, values));
}
}
+ finally
+ {
+ taskScheduler.unlockEntry(taskEntryDN, lock);
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
index beed09f6..b6f90b1 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -34,6 +34,7 @@
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.locks.Lock;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
@@ -888,43 +889,61 @@
}
else if (parentDN.equals(scheduledTaskParentDN))
{
- Entry e = taskScheduler.getScheduledTaskEntry(baseDN);
- if (e == null)
- {
- int msgID = MSGID_TASKBE_SEARCH_NO_SUCH_TASK;
- String message = getMessage(msgID, String.valueOf(baseDN));
- throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message,
- msgID, scheduledTaskParentDN, null);
- }
+ Lock lock = taskScheduler.readLockEntry(baseDN);
- if (((searchScope == SearchScope.BASE_OBJECT) ||
- (searchScope == SearchScope.WHOLE_SUBTREE)) &&
- searchFilter.matchesEntry(e))
+ try
{
- searchOperation.returnEntry(e, null);
- }
+ Entry e = taskScheduler.getScheduledTaskEntry(baseDN);
+ if (e == null)
+ {
+ int msgID = MSGID_TASKBE_SEARCH_NO_SUCH_TASK;
+ String message = getMessage(msgID, String.valueOf(baseDN));
+ throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message,
+ msgID, scheduledTaskParentDN, null);
+ }
- return;
+ if (((searchScope == SearchScope.BASE_OBJECT) ||
+ (searchScope == SearchScope.WHOLE_SUBTREE)) &&
+ searchFilter.matchesEntry(e))
+ {
+ searchOperation.returnEntry(e, null);
+ }
+
+ return;
+ }
+ finally
+ {
+ taskScheduler.unlockEntry(baseDN, lock);
+ }
}
else if (parentDN.equals(recurringTaskParentDN))
{
- Entry e = taskScheduler.getRecurringTaskEntry(baseDN);
- if (e == null)
- {
- int msgID = MSGID_TASKBE_SEARCH_NO_SUCH_RECURRING_TASK;
- String message = getMessage(msgID, String.valueOf(baseDN));
- throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message,
- msgID, recurringTaskParentDN, null);
- }
+ Lock lock = taskScheduler.readLockEntry(baseDN);
- if (((searchScope == SearchScope.BASE_OBJECT) ||
- (searchScope == SearchScope.WHOLE_SUBTREE)) &&
- searchFilter.matchesEntry(e))
+ try
{
- searchOperation.returnEntry(e, null);
- }
+ Entry e = taskScheduler.getRecurringTaskEntry(baseDN);
+ if (e == null)
+ {
+ int msgID = MSGID_TASKBE_SEARCH_NO_SUCH_RECURRING_TASK;
+ String message = getMessage(msgID, String.valueOf(baseDN));
+ throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message,
+ msgID, recurringTaskParentDN, null);
+ }
- return;
+ if (((searchScope == SearchScope.BASE_OBJECT) ||
+ (searchScope == SearchScope.WHOLE_SUBTREE)) &&
+ searchFilter.matchesEntry(e))
+ {
+ searchOperation.returnEntry(e, null);
+ }
+
+ return;
+ }
+ finally
+ {
+ taskScheduler.unlockEntry(baseDN, lock);
+ }
}
else
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java b/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
index 9472b79..491850b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -37,6 +37,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.opends.server.api.AlertGenerator;
@@ -55,6 +56,7 @@
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFExportConfig;
+import org.opends.server.types.LockManager;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.util.LDIFException;
@@ -1448,7 +1450,83 @@
/**
- * Retrieves the scheduled task entry with the provided DN.
+ * Attempts to acquire a write lock on the specified entry, trying as many
+ * times as necessary until the lock has been acquired.
+ *
+ * @param entryDN The DN of the entry for which to acquire the write lock.
+ *
+ * @return The write lock that has been acquired for the entry.
+ */
+ Lock writeLockEntry(DN entryDN)
+ {
+ assert debugEnter(CLASS_NAME, "lockEntry", String.valueOf(entryDN));
+
+ Lock lock = LockManager.lockWrite(entryDN);
+ while (lock == null)
+ {
+ lock = LockManager.lockWrite(entryDN);
+ }
+
+ return lock;
+ }
+
+
+
+ /**
+ * Attempts to acquire a read lock on the specified entry, trying up to five
+ * times before failing.
+ *
+ * @param entryDN The DN of the entry for which to acquire the read lock.
+ *
+ * @return The read lock that has been acquired for the entry.
+ *
+ * @throws DirectoryException If the read lock cannot be acquired.
+ */
+ Lock readLockEntry(DN entryDN)
+ throws DirectoryException
+ {
+ assert debugEnter(CLASS_NAME, "lockEntry", String.valueOf(entryDN));
+
+ Lock lock = LockManager.lockRead(entryDN);
+ for (int i=0; ((lock == null) && (i < 4)); i++)
+ {
+ lock = LockManager.lockRead(entryDN);
+ }
+
+ if (lock == null)
+ {
+ int msgID = MSGID_BACKEND_CANNOT_LOCK_ENTRY;
+ String message = getMessage(msgID, String.valueOf(entryDN));
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ message, msgID);
+ }
+ else
+ {
+ return lock;
+ }
+ }
+
+
+
+ /**
+ * Releases the lock held on the specified entry.
+ *
+ * @param entryDN The DN of the entry for which the lock is held.
+ * @param lock The lock held on the entry.
+ */
+ void unlockEntry(DN entryDN, Lock lock)
+ {
+ assert debugEnter(CLASS_NAME, "unlockEntry", String.valueOf(entryDN),
+ String.valueOf(lock));
+
+ LockManager.unlock(entryDN, lock);
+ }
+
+
+
+ /**
+ * Retrieves the scheduled task entry with the provided DN. The caller should
+ * hold a read lock on the target entry.
*
* @param scheduledTaskEntryDN The entry DN that indicates which scheduled
* task entry to retrieve.
@@ -1516,14 +1594,24 @@
{
for (Task t : tasks.values())
{
- Entry e = t.getTaskEntry();
- if (filter.matchesEntry(e))
+ DN taskEntryDN = t.getTaskEntryDN();
+ Lock lock = readLockEntry(taskEntryDN);
+
+ try
{
- if (! searchOperation.returnEntry(e, null))
+ Entry e = t.getTaskEntry();
+ if (filter.matchesEntry(e))
{
- return false;
+ if (! searchOperation.returnEntry(e, null))
+ {
+ return false;
+ }
}
}
+ finally
+ {
+ unlockEntry(taskEntryDN, lock);
+ }
}
return true;
@@ -1601,7 +1689,8 @@
/**
- * Retrieves the recurring task entry with the provided DN.
+ * Retrieves the recurring task entry with the provided DN. The caller should
+ * hold a read lock on the target entry.
*
* @param recurringTaskEntryDN The entry DN that indicates which recurring
* task entry to retrieve.
@@ -1669,14 +1758,24 @@
{
for (RecurringTask rt : recurringTasks.values())
{
- Entry e = rt.getRecurringTaskEntry();
- if (filter.matchesEntry(e))
+ DN recurringTaskEntryDN = rt.getRecurringTaskEntryDN();
+ Lock lock = readLockEntry(recurringTaskEntryDN);
+
+ try
{
- if (! searchOperation.returnEntry(e, null))
+ Entry e = rt.getRecurringTaskEntry();
+ if (filter.matchesEntry(e))
{
- return false;
+ if (! searchOperation.returnEntry(e, null))
+ {
+ return false;
+ }
}
}
+ finally
+ {
+ unlockEntry(recurringTaskEntryDN, lock);
+ }
}
return true;
--
Gitblit v1.10.0