mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

neil_a_wilson
05.51.2007 2a5859ba830517514ca7aa21d90988327b0b51ea
Implement support for delete and modify operations in the task backend as
follows:

- Pending tasks and completed tasks may be deleted. Running tasks may not be
deleted.
- Any attributes in pending tasks may be modified.
- Only the task state in running tasks may be modified, and only to cancel the
task.
- No attributes of a completed task may be modified.

OpenDS Issue Number: 2181
1 files added
4 files modified
732 ■■■■■ changed files
opendj-sdk/opends/src/messages/messages/backend.properties 21 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java 208 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/task/TaskBackendTestCase.java 409 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java 64 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/TasksTestCase.java 30 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/messages/messages/backend.properties
@@ -944,5 +944,22 @@
SEVERE_WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED_328=Failed to set permissions \
 on trust store file %s
SEVERE_ERR_ROOT_CONTAINER_NOT_INITIALIZED_329=The root container for backend \
  %s has not been initialized preventing this backend from processing the \
  requested operation
 %s has not been initialized preventing this backend from processing the \
 requested operation
SEVERE_ERR_TASKBE_MODIFY_CANNOT_LOCK_ENTRY_330=Unable to obtain a write lock \
 on entry %s
SEVERE_ERR_TASKBE_MODIFY_INVALID_ENTRY_331=Entry %s cannot be modified \
 because it does not represent a task entry.  Only task entries may be \
 modified in the task backend
SEVERE_ERR_TASKBE_MODIFY_NO_SUCH_TASK_332=Entry %s cannot be modified because \
 it does not represent a valid task in the server
SEVERE_ERR_TASKBE_MODIFY_COMPLETED_333=Entry %s cannot be modified because \
 the assoicated task has completed running.  Completed tasks cannot be modified
SEVERE_ERR_TASKBE_MODIFY_RECURRING_334=Entry %s cannot be modified because \
 the server does not currently support modifying recurring task entries
SEVERE_ERR_TASKBE_MODIFY_RUNNING_335=The task associated with entry %s is \
 currently running.  The only modification allowed for running tasks is to \
 replace the value of the ds-task-state attribute with "cancel"
INFO_TASKBE_RUNNING_TASK_CANCELLED_336=Task processing was interrupted by a \
 modify request to cancel the task
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -33,6 +33,7 @@
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Lock;
@@ -495,57 +496,6 @@
  /**
   * Indicates whether an entry with the specified DN exists in the backend.
   * The default implementation obtains a read lock and calls
   * <CODE>getEntry</CODE>, but backend implementations may override this with a
   * more efficient version that does not require a lock.  The caller is not
   * required to hold any locks on the specified DN.
   *
   * @param  entryDN  The DN of the entry for which to determine existence.
   *
   * @return  <CODE>true</CODE> if the specified entry exists in this backend,
   *          or <CODE>false</CODE> if it does not.
   *
   * @throws  DirectoryException  If a problem occurs while trying to make the
   *                              determination.
   */
  public boolean entryExists(DN entryDN)
         throws DirectoryException
  {
    if (entryDN == null)
    {
      return false;
    }
    if (entryDN.equals(taskRootDN) || entryDN.equals(scheduledTaskParentDN) ||
        entryDN.equals(recurringTaskParentDN))
    {
      return true;
    }
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
    {
      return false;
    }
    if (parentDN.equals(scheduledTaskParentDN))
    {
      return (taskScheduler.getScheduledTaskEntry(entryDN) != null);
    }
    else if (parentDN.equals(recurringTaskParentDN))
    {
      return (taskScheduler.getRecurringTaskEntry(entryDN) != null);
    }
    else
    {
      return false;
    }
  }
  /**
   * Adds the provided entry to this backend.  This method must ensure that the
   * entry is appropriate for the backend and that no entry already exists with
   * the same DN.
@@ -701,10 +651,158 @@
  public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
         throws DirectoryException
  {
    // FIXME -- We need to support this.
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
            Message.raw("Modify operations are not yet supported in " +
                        "the task backend"));
    DN entryDN = entry.getDN();
    Lock entryLock = null;
    if (! taskScheduler.holdsSchedulerLock())
    {
      for (int i=0; i < 3; i++)
      {
        entryLock = LockManager.lockWrite(entryDN);
        if (entryLock != null)
        {
          break;
        }
      }
      if (entryLock == null)
      {
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     ERR_TASKBE_MODIFY_CANNOT_LOCK_ENTRY.get(
                                          String.valueOf(entryDN)));
      }
    }
    try
    {
      // Get the parent for the provided entry DN.  It must be either the
      // scheduled or recurring task parent DN.
      DN parentDN = entryDN.getParentDNInSuffix();
      if (parentDN == null)
      {
        Message message =
            ERR_TASKBE_MODIFY_INVALID_ENTRY.get(String.valueOf(entryDN));
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
      else if (parentDN.equals(scheduledTaskParentDN))
      {
        // It's a scheduled task.  Make sure that it exists.
        Task t = taskScheduler.getScheduledTask(entryDN);
        if (t == null)
        {
          Message message =
              ERR_TASKBE_MODIFY_NO_SUCH_TASK.get(String.valueOf(entryDN));
          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
        }
        // Look at the state of the task.  We will allow anything to be altered
        // for a pending task.  For a running task, we will only allow the state
        // to be altered in order to cancel it.  We will not allow any
        // modifications for completed tasks.
        TaskState state = t.getTaskState();
        if (TaskState.isPending(state))
        {
          Task newTask =
                    taskScheduler.entryToScheduledTask(entry, modifyOperation);
          taskScheduler.removePendingTask(t.getTaskID());
          taskScheduler.scheduleTask(newTask, true);
          return;
        }
        else if (TaskState.isRunning(state))
        {
          // If the task is running, we will only allow it to be cancelled.
          // This will only be allowed using the replace modification type on
          // the ds-task-state attribute if the value starts with "cancel" or
          // "stop".  In that case, we'll cancel the task.
          boolean acceptable = true;
          for (Modification m : modifyOperation.getModifications())
          {
            if (m.isInternal())
            {
              continue;
            }
            if (m.getModificationType() != ModificationType.REPLACE)
            {
              acceptable = false;
              break;
            }
            Attribute a = m.getAttribute();
            AttributeType at = a.getAttributeType();
            if (! at.hasName(ATTR_TASK_STATE))
            {
              acceptable = false;
              break;
            }
            Iterator<AttributeValue> iterator = a.getValues().iterator();
            if (! iterator.hasNext())
            {
              acceptable = false;
              break;
            }
            AttributeValue v = iterator.next();
            String valueString = toLowerCase(v.getStringValue());
            if (! (valueString.startsWith("cancel") ||
                   valueString.startsWith("stop")))
            {
              acceptable = false;
              break;
            }
            if (iterator.hasNext())
            {
              acceptable = false;
              break;
            }
          }
          if (acceptable)
          {
            Message message = INFO_TASKBE_RUNNING_TASK_CANCELLED.get();
            t.interruptTask(TaskState.STOPPED_BY_ADMINISTRATOR, message);
            return;
          }
          else
          {
            Message message =
                 ERR_TASKBE_MODIFY_RUNNING.get(String.valueOf(entryDN));
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message);
          }
        }
        else
        {
          Message message =
              ERR_TASKBE_MODIFY_COMPLETED.get(String.valueOf(entryDN));
          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                       message);
        }
      }
      else if (parentDN.equals(recurringTaskParentDN))
      {
        // We don't currently support altering recurring tasks.
        Message message =
            ERR_TASKBE_MODIFY_RECURRING.get(String.valueOf(entryDN));
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
      else
      {
        Message message =
            ERR_TASKBE_MODIFY_INVALID_ENTRY.get(String.valueOf(entryDN));
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
    }
    finally
    {
      if (entryLock != null)
      {
        LockManager.unlock(entryDN, entryLock);
      }
    }
  }
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/task/TaskBackendTestCase.java
New file
@@ -0,0 +1,409 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.backends.task;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.backends.BackendTestCase;
import org.opends.server.core.DirectoryServer;
import org.opends.server.tasks.TasksTestCase;
import org.opends.server.types.DN;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * A set of test cases that can be used to test the task backend.
 */
public class TaskBackendTestCase
       extends BackendTestCase
{
  /**
   * Ensures that the Directory Server is running, and that we are allowed to
   * schedule the dummy task.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.dsconfig(
      "set-global-configuration-prop",
      "--add", "allowed-task:org.opends.server.tasks.DummyTask");
  }
  /**
   * Remove the dummy task from the set of allowed tasks.
   */
  @AfterClass()
  public void cleanUp()
         throws Exception
  {
    TestCaseUtils.dsconfig(
      "set-global-configuration-prop",
      "--remove", "allowed-task:org.opends.server.tasks.DummyTask");
  }
  /**
   * Tests to ensure that we can delete a task that is scheduled but hasn't
   * yet started.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testDeletePendingTask()
         throws Exception
  {
    // Schedule a task to start one hour from now that will simply sleep for
    // 30 seconds.
    String taskID = "testDeletePendingTask";
    String taskDN = "ds-task-id=" + taskID + ",cn=Scheduled Tasks,cn=tasks";
    long startTime = System.currentTimeMillis() + 3600000L;
    SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
    dateFormat.setTimeZone(TimeZone.getTimeZone(TIME_ZONE_UTC));
    String startTimeStr = dateFormat.format(new Date(startTime));
    TestCaseUtils.addEntry(
      "dn: " + taskDN,
      "objectClass: top",
      "objectClass: ds-task",
      "objectClass: extensibleObject",
      "ds-task-id: " + taskID,
      "ds-task-class-name: org.opends.server.tasks.DummyTask",
      "ds-task-scheduled-start-time: " + startTimeStr,
      "ds-task-dummy-sleep-time: 30000");
    assertTrue(DirectoryServer.entryExists(DN.decode(taskDN)));
    Task task = TasksTestCase.getTask(DN.decode(taskDN));
    assertTrue(TaskState.isPending(task.getTaskState()));
    // Perform a modification to delete that task.
    int resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: delete");
    assertEquals(resultCode, 0);
    assertFalse(DirectoryServer.entryExists(DN.decode(taskDN)));
  }
  /**
   * Tests to ensure that we cannot delete a task that is currently running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testDeleteRunningTask()
         throws Exception
  {
    // Schedule a task to start immediately that will simply sleep for 30
    // seconds.
    String taskID = "testDeleteRunningTask";
    String taskDN = "ds-task-id=" + taskID + ",cn=Scheduled Tasks,cn=tasks";
    TestCaseUtils.addEntry(
      "dn: " + taskDN,
      "objectClass: top",
      "objectClass: ds-task",
      "objectClass: extensibleObject",
      "ds-task-id: " + taskID,
      "ds-task-class-name: org.opends.server.tasks.DummyTask",
      "ds-task-dummy-sleep-time: 30000");
    assertTrue(DirectoryServer.entryExists(DN.decode(taskDN)));
    // Wait until we're sure that the task has started running.
    long startTime = System.currentTimeMillis();
    Task task = TasksTestCase.getTask(DN.decode(taskDN));
    while (TaskState.isPending(task.getTaskState()))
    {
      Thread.sleep(10);
      if (System.currentTimeMillis() > (startTime + 30000L))
      {
        throw new AssertionError("Waited too long for the task to start");
      }
    }
    assertTrue(TaskState.isRunning(task.getTaskState()));
    // Perform a modification to delete that task.
    int resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: delete");
    assertFalse(resultCode == 0);
    assertTrue(DirectoryServer.entryExists(DN.decode(taskDN)));
  }
  /**
   * Tests to ensure that we can delete a task that has completed.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testDeleteCompletedTask()
         throws Exception
  {
    // Schedule a task to start immediately that will simply sleep for 30
    // seconds.
    String taskID = "testDeleteCompltedTask";
    String taskDN = "ds-task-id=" + taskID + ",cn=Scheduled Tasks,cn=tasks";
    TestCaseUtils.addEntry(
      "dn: " + taskDN,
      "objectClass: top",
      "objectClass: ds-task",
      "objectClass: extensibleObject",
      "ds-task-id: " + taskID,
      "ds-task-class-name: org.opends.server.tasks.DummyTask");
    assertTrue(DirectoryServer.entryExists(DN.decode(taskDN)));
    // Wait until the task has completed.
    Task task = TasksTestCase.getCompletedTask(DN.decode(taskDN));
    assertTrue(TaskState.isDone(task.getTaskState()));
    // Perform a modification to delete that task.
    int resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: delete");
    assertEquals(resultCode, 0);
    assertFalse(DirectoryServer.entryExists(DN.decode(taskDN)));
  }
  /**
   * Tests to ensure that we can modify a task that is scheduled but hasn't
   * yet started to change the task state as well as other attributes in the
   * task entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testModifyPendingTask()
         throws Exception
  {
    // Schedule a task to start one hour from now that will simply sleep for
    // 30 seconds.
    String taskID = "testModifyPendingTask";
    String taskDN = "ds-task-id=" + taskID + ",cn=Scheduled Tasks,cn=tasks";
    long startTime = System.currentTimeMillis() + 3600000L;
    SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
    dateFormat.setTimeZone(TimeZone.getTimeZone(TIME_ZONE_UTC));
    String startTimeStr = dateFormat.format(new Date(startTime));
    TestCaseUtils.addEntry(
      "dn: " + taskDN,
      "objectClass: top",
      "objectClass: ds-task",
      "objectClass: extensibleObject",
      "ds-task-id: " + taskID,
      "ds-task-class-name: org.opends.server.tasks.DummyTask",
      "ds-task-scheduled-start-time: " + startTimeStr,
      "ds-task-dummy-sleep-time: 30000");
    assertTrue(DirectoryServer.entryExists(DN.decode(taskDN)));
    Task task = TasksTestCase.getTask(DN.decode(taskDN));
    assertTrue(TaskState.isPending(task.getTaskState()));
    // Perform a modification to update a non-state attribute.
    int resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: modify",
      "add: description",
      "description: foo");
    assertEquals(resultCode, 0);
    // Perform a modification to update the task state.
    resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: modify",
      "replace: ds-task-state",
      "ds-task-state: " + TaskState.CANCELED_BEFORE_STARTING.toString());
    assertEquals(resultCode, 0);
    // Delete the task.
    resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: delete");
    assertEquals(resultCode, 0);
    assertFalse(DirectoryServer.entryExists(DN.decode(taskDN)));
  }
  /**
   * Tests to ensure that we cannot modify a task that is currently running
   * other than to change its state.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testModifyRunningTask()
         throws Exception
  {
    // Schedule a task to start immediately that will simply sleep for 30
    // seconds.
    String taskID = "testModifyRunningTask";
    String taskDN = "ds-task-id=" + taskID + ",cn=Scheduled Tasks,cn=tasks";
    TestCaseUtils.addEntry(
      "dn: " + taskDN,
      "objectClass: top",
      "objectClass: ds-task",
      "objectClass: extensibleObject",
      "ds-task-id: " + taskID,
      "ds-task-class-name: org.opends.server.tasks.DummyTask",
      "ds-task-dummy-sleep-time: 30000");
    assertTrue(DirectoryServer.entryExists(DN.decode(taskDN)));
    // Wait until we're sure that the task has started running.
    long startTime = System.currentTimeMillis();
    Task task = TasksTestCase.getTask(DN.decode(taskDN));
    while (TaskState.isPending(task.getTaskState()))
    {
      Thread.sleep(10);
      if (System.currentTimeMillis() > (startTime + 30000L))
      {
        throw new AssertionError("Waited too long for the task to start");
      }
    }
    assertTrue(TaskState.isRunning(task.getTaskState()));
    // Perform a modification to change something other than the state.
    int resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: modify",
      "replace: description",
      "description: foo");
    assertFalse(resultCode == 0);
    // Perform a modification to cancel the task.
    resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: modify",
      "replace: ds-task-state",
      "ds-task-state: cancel");
    assertEquals(resultCode, 0);
    // We may have to wait for the task to register as done, but it should
    // definitely be done before it would have stopped normally.
    task = TasksTestCase.getCompletedTask(DN.decode(taskDN));
    assertTrue((System.currentTimeMillis() - startTime) < 30000L);
    assertTrue(TaskState.isDone(task.getTaskState()));
    // Perform a modification to delete that task.
    resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: delete");
    assertEquals(resultCode, 0);
    assertFalse(DirectoryServer.entryExists(DN.decode(taskDN)));
  }
  /**
   * Tests to ensure that we cannot modify a task that has completed.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testModifyCompletedTask()
         throws Exception
  {
    // Schedule a task to start immediately that will simply sleep for 30
    // seconds.
    String taskID = "testModifyCompltedTask";
    String taskDN = "ds-task-id=" + taskID + ",cn=Scheduled Tasks,cn=tasks";
    TestCaseUtils.addEntry(
      "dn: " + taskDN,
      "objectClass: top",
      "objectClass: ds-task",
      "objectClass: extensibleObject",
      "ds-task-id: " + taskID,
      "ds-task-class-name: org.opends.server.tasks.DummyTask");
    assertTrue(DirectoryServer.entryExists(DN.decode(taskDN)));
    // Wait until the task has completed.
    Task task = TasksTestCase.getCompletedTask(DN.decode(taskDN));
    assertTrue(TaskState.isDone(task.getTaskState()));
    // Perform a modification to update a non-state attribute.
    int resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: modify",
      "add: description",
      "description: foo");
    assertFalse(resultCode == 0);
    // Perform a modification to delete that task.
    resultCode = TestCaseUtils.applyModifications(
      "dn: " + taskDN,
      "changetype: delete");
    assertEquals(resultCode, 0);
    assertFalse(DirectoryServer.entryExists(DN.decode(taskDN)));
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
@@ -28,9 +28,15 @@
import java.util.List;
import org.opends.messages.Message;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
@@ -41,6 +47,15 @@
public class DummyTask
       extends Task
{
  // The length of time that the task should sleep before completing.
  private long sleepTime;
  // The task state to use when interrupting the task.  This will be null unless
  // the task gets interrupted.
  private volatile TaskState interruptedState;
  /**
   * {@inheritDoc}
   */
@@ -48,7 +63,25 @@
  public void initializeTask()
         throws DirectoryException
  {
    // No implementation is required.
    sleepTime = 0;
    interruptedState = null;
    Entry taskEntry = getTaskEntry();
    if (taskEntry != null)
    {
      List<Attribute> attrList =
           taskEntry.getAttribute("ds-task-dummy-sleep-time");
      if (attrList != null)
      {
        for (Attribute a : attrList)
        {
          for (AttributeValue v : a.getValues())
          {
            sleepTime = Long.parseLong(v.getStringValue());
          }
        }
      }
    }
  }
@@ -58,7 +91,34 @@
   */
  protected TaskState runTask()
  {
    return TaskState.COMPLETED_SUCCESSFULLY;
    long stopTime = System.currentTimeMillis() + sleepTime;
    while ((interruptedState == null) &&
           (System.currentTimeMillis() < stopTime))
    {
      try
      {
        Thread.sleep(10);
      } catch (Exception e) {}
    }
    if (interruptedState == null)
    {
      return TaskState.COMPLETED_SUCCESSFULLY;
    }
    else
    {
      return interruptedState;
    }
  }
  /**
   * {@inheritDoc}
   */
  public void interruptTask(TaskState taskState, Message interruptMessage)
  {
    interruptedState = taskState;
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/TasksTestCase.java
@@ -157,8 +157,8 @@
  /**
   * Retrieves the specified task from the server, waiting for it to finish all
   * the running its going to do before returning.
   * Retrieves the specified task from the server, regardless of its current
   * state.
   *
   * @param  taskEntryDN  The DN of the entry for the task to retrieve.
   *
@@ -166,8 +166,9 @@
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  protected Task getCompletedTask(DN taskEntryDN)
          throws Exception
  @Test(enabled=false) // This isn't a test method, but TestNG thinks it is.
  public static Task getTask(DN taskEntryDN)
         throws Exception
  {
    TaskBackend taskBackend =
         (TaskBackend) DirectoryServer.getBackend(DN.decode("cn=tasks"));
@@ -188,6 +189,27 @@
                               taskEntryDN.toString());
    }
    return task;
  }
  /**
   * Retrieves the specified task from the server, waiting for it to finish all
   * the running its going to do before returning.
   *
   * @param  taskEntryDN  The DN of the entry for the task to retrieve.
   *
   * @return  The requested task entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(enabled=false) // This isn't a test method, but TestNG thinks it is.
  public static Task getCompletedTask(DN taskEntryDN)
          throws Exception
  {
    Task task = getTask(taskEntryDN);
    if (! TaskState.isDone(task.getTaskState()))
    {
      long stopWaitingTime = System.currentTimeMillis() + 20000L;