| opends/resource/config/config.ldif | ●●●●● patch | view | raw | blame | history | |
| opends/resource/schema/02-config.ldif | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/extensions/GetConnectionIDExtendedOperation.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/messages/TaskMessages.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/tasks/DisconnectClientTask.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/util/ServerConstants.java | ●●●●● patch | view | raw | blame | history | |
| opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DisconnectClientTaskTestCase.java | ●●●●● patch | view | raw | blame | history |
opends/resource/config/config.ldif
@@ -405,6 +405,13 @@ ds-cfg-extended-operation-handler-class: org.opends.server.extensions.CancelExtendedOperation ds-cfg-extended-operation-handler-enabled: true dn: cn=Get Connection ID,cn=Extended Operations,cn=config objectClass: top objectClass: ds-cfg-extended-operation-handler cn: Cancel ds-cfg-extended-operation-handler-class: org.opends.server.extensions.GetConnectionIDExtendedOperation ds-cfg-extended-operation-handler-enabled: true dn: cn=Password Modify,cn=Extended Operations,cn=config objectClass: top objectClass: ds-cfg-extended-operation-handler opends/resource/schema/02-config.ldif
@@ -1520,6 +1520,16 @@ NAME 'ds-task-import-clear-backend' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.453 NAME 'ds-task-disconnect-connection-id' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.454 NAME 'ds-task-disconnect-message' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.455 NAME 'ds-task-disconnect-notify-client' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.1 NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled ) @@ -2138,4 +2148,9 @@ MUST ( ds-cfg-sender-address $ ds-cfg-recipient-address $ ds-cfg-message-subject $ ds-cfg-message-body ) X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.119 NAME 'ds-task-disconnect' SUP ds-task STRUCTURAL MUST ds-task-disconnect-connection-id MAY ( ds-task-disconnect-message $ ds-task-disconnect-notify-client ) X-ORIGIN 'OpenDS Directory Server' ) opends/src/server/org/opends/server/extensions/GetConnectionIDExtendedOperation.java
New file @@ -0,0 +1,139 @@ /* * 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.extensions; import org.opends.server.admin.std.server.ExtendedOperationHandlerCfg; import org.opends.server.api.ExtendedOperationHandler; import org.opends.server.config.ConfigException; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ExtendedOperation; import org.opends.server.protocols.asn1.ASN1Exception; import org.opends.server.protocols.asn1.ASN1Long; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.types.InitializationException; import org.opends.server.types.ResultCode; import static org.opends.server.messages.ExtensionsMessages.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.util.ServerConstants.*; /** * This class implements the "Get Connection ID" extended operation that can be * used to get the connection ID of the associated client connection. */ public class GetConnectionIDExtendedOperation extends ExtendedOperationHandler<ExtendedOperationHandlerCfg> { /** * Create an instance of this "Get Connection ID" extended operation. All * initialization should be performed in the * {@code initializeExtendedOperationHandler} method. */ public GetConnectionIDExtendedOperation() { super(); } /** * {@inheritDoc} */ public void initializeExtendedOperationHandler( ExtendedOperationHandlerCfg config) throws ConfigException, InitializationException { // No special configuration is required. DirectoryServer.registerSupportedExtension(OID_GET_CONNECTION_ID_EXTOP, this); registerControlsAndFeatures(); } /** * {@inheritDoc} */ public void finalizeExtendedOperationHandler() { DirectoryServer.deregisterSupportedExtension(OID_WHO_AM_I_REQUEST); deregisterControlsAndFeatures(); } /** * {@inheritDoc} */ public void processExtendedOperation(ExtendedOperation operation) { operation.setResponseOID(OID_GET_CONNECTION_ID_EXTOP); operation.setResponseValue( encodeResponseValue(operation.getConnectionID())); operation.setResultCode(ResultCode.SUCCESS); } /** * Encodes the provided connection ID in an octet string suitable for use as * the value for this extended operation. * * @param connectionID The connection ID to be encoded. * * @return The ASN.1 octet string containing the encoded connection ID. */ public static ASN1OctetString encodeResponseValue(long connectionID) { return new ASN1OctetString(new ASN1Long(connectionID).encode()); } /** * Decodes the provided ASN.1 octet string to extract the connection ID. * * @param responseValue The response value to be decoded. * * @return The connection ID decoded from the provided response value. * * @throws ASN1Exception If an error occurs while trying to decode the * response value. */ public static long decodeResponseValue(ASN1OctetString responseValue) throws ASN1Exception { return ASN1Long.decodeAsLong(responseValue.value()).longValue(); } } opends/src/server/org/opends/server/messages/TaskMessages.java
@@ -274,6 +274,76 @@ /** * The message ID for the message that will be used if the client does not * have the DISCONNECT_CLIENT privilege. It does not take any arguments. */ public static final int MSGID_TASK_DISCONNECT_NO_PRIVILEGE = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 25; /** * The message ID for the message that will be used if the provided connection * ID cannot be decoded. This takes a single argument, which is the invalid * value. */ public static final int MSGID_TASK_DISCONNECT_INVALID_CONN_ID = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 26; /** * The message ID for the message that will be used if the task entry does * not specify a target connection ID. This takes a single argument, which is * the name of the attribute type used to specify the target connection ID. */ public static final int MSGID_TASK_DISCONNECT_NO_CONN_ID = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 27; /** * The message ID for the message that will be used if the notifyClient * attribute value cannot be decoded. This takes a single argument, which is * the invalid value. */ public static final int MSGID_TASK_DISCONNECT_INVALID_NOTIFY_CLIENT = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 28; /** * The message ID for the message that will be used as a generic message that * may be sent to the client if no other value is given. It does not take * any arguments. */ public static final int MSGID_TASK_DISCONNECT_GENERIC_MESSAGE = CATEGORY_MASK_TASK | SEVERITY_MASK_INFORMATIONAL | 29; /** * The message ID for the message that will be used if no client connection * can be found with the specified connection ID. It takes a single argument, * which is the target connection ID. */ public static final int MSGID_TASK_DISCONNECT_NO_SUCH_CONNECTION = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 30; /** * The message ID for the message that will be used as the message ID for the * disconnectClient method. The associated message will be defined, but it * will not actually be used, since only the message ID is needed. It does * not take any arguments. */ public static final int MSGID_TASK_DISCONNECT_MESSAGE = CATEGORY_MASK_TASK | SEVERITY_MASK_INFORMATIONAL | 31; /** * Associates a set of generic messages with the message IDs defined in this * class. */ @@ -362,6 +432,26 @@ registerMessage(MSGID_TASK_LEAVELOCKDOWN_NOT_LOOPBACK, "Only root users connected from a loopback address may " + "cause the server to leave lockdown mode"); registerMessage(MSGID_TASK_DISCONNECT_NO_PRIVILEGE, "You do not have sufficient privileges to terminate " + "client connections"); registerMessage(MSGID_TASK_DISCONNECT_INVALID_CONN_ID, "Unable to decode value %s as an integer connection ID"); registerMessage(MSGID_TASK_DISCONNECT_NO_CONN_ID, "Attribute %s must be provided to specify the connection " + "ID for the client to disconnect"); registerMessage(MSGID_TASK_DISCONNECT_INVALID_NOTIFY_CLIENT, "Unable to decode value %s as an indication of whether " + "to notify the client before disconnecting it. The " + "provided value should be either 'true' or 'false'"); registerMessage(MSGID_TASK_DISCONNECT_GENERIC_MESSAGE, "An administrator has terminated this client connection"); registerMessage(MSGID_TASK_DISCONNECT_NO_SUCH_CONNECTION, "There is no client connection with connection ID %s"); registerMessage(MSGID_TASK_DISCONNECT_MESSAGE, "An administrator has terminated this client connection"); } } opends/src/server/org/opends/server/tasks/DisconnectClientTask.java
New file @@ -0,0 +1,236 @@ /* * 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.tasks; import java.util.List; import org.opends.server.admin.std.server.ConnectionHandlerCfg; import org.opends.server.backends.task.Task; import org.opends.server.backends.task.TaskState; import org.opends.server.api.ClientConnection; import org.opends.server.api.ConnectionHandler; import org.opends.server.core.DirectoryServer; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.DirectoryException; import org.opends.server.types.DisconnectReason; import org.opends.server.types.Entry; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.Operation; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.messages.TaskMessages.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; /** * This class provides an implementation of a Directory Server task that can be * used to terminate a client connection. */ public class DisconnectClientTask extends Task { // Indicates whether to send a notification message to the client. private boolean notifyClient; // The connection ID for the client connection to terminate. private long connectionID; // The disconnect message to send to the client. private String disconnectMessage; /** * {@inheritDoc} */ @Override public void initializeTask() throws DirectoryException { // If the client connection is available, then make sure the client has the // DISCONNECT_CLIENT privilege. Operation operation = getOperation(); if (operation != null) { ClientConnection conn = operation.getClientConnection(); if (! conn.hasPrivilege(Privilege.DISCONNECT_CLIENT, operation)) { int msgID = MSGID_TASK_DISCONNECT_NO_PRIVILEGE; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } // Get the connection ID for the client connection. Entry taskEntry = getTaskEntry(); connectionID = -1L; AttributeType attrType = DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_CONN_ID, true); List<Attribute> attrList = taskEntry.getAttribute(attrType); if (attrList != null) { connIDLoop: for (Attribute a : attrList) { for (AttributeValue v : a.getValues()) { try { connectionID = Long.parseLong(v.getStringValue()); break connIDLoop; } catch (Exception e) { int msgID = MSGID_TASK_DISCONNECT_INVALID_CONN_ID; String message = getMessage(msgID, v.getStringValue()); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, msgID, e); } } } } if (connectionID < 0) { int msgID = MSGID_TASK_DISCONNECT_NO_CONN_ID; String message = getMessage(msgID, ATTR_TASK_DISCONNECT_CONN_ID); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, msgID); } // Determine whether to notify the client. notifyClient = false; attrType = DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_NOTIFY_CLIENT, true); attrList = taskEntry.getAttribute(attrType); if (attrList != null) { notifyClientLoop: for (Attribute a : attrList) { for (AttributeValue v : a.getValues()) { String stringValue = toLowerCase(v.getStringValue()); if (stringValue.equals("true")) { notifyClient = true; break notifyClientLoop; } else if (stringValue.equals("false")) { break notifyClientLoop; } else { int msgID = MSGID_TASK_DISCONNECT_INVALID_NOTIFY_CLIENT; String message = getMessage(msgID, stringValue); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, msgID); } } } } // Get the disconnect message. disconnectMessage = getMessage(MSGID_TASK_DISCONNECT_GENERIC_MESSAGE); attrType = DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_MESSAGE, true); attrList = taskEntry.getAttribute(attrType); if (attrList != null) { disconnectMessageLoop: for (Attribute a : attrList) { for (AttributeValue v : a.getValues()) { disconnectMessage = v.getStringValue(); break disconnectMessageLoop; } } } } /** * {@inheritDoc} */ protected TaskState runTask() { // Get the specified client connection. ClientConnection clientConnection = null; for (ConnectionHandler handler : DirectoryServer.getConnectionHandlers()) { ConnectionHandler<? extends ConnectionHandlerCfg> connHandler = (ConnectionHandler<? extends ConnectionHandlerCfg>) handler; for (ClientConnection c : connHandler.getClientConnections()) { if (c.getConnectionID() == connectionID) { clientConnection = c; break; } } } // If there is no such client connection, then return an error. Otherwise, // terminate it. if (clientConnection == null) { int msgID = MSGID_TASK_DISCONNECT_NO_SUCH_CONNECTION; String message = getMessage(msgID, connectionID); logError(ErrorLogCategory.TASK, ErrorLogSeverity.SEVERE_ERROR, message, msgID); return TaskState.COMPLETED_WITH_ERRORS; } else { clientConnection.disconnect(DisconnectReason.ADMIN_DISCONNECT, notifyClient, disconnectMessage, MSGID_TASK_DISCONNECT_MESSAGE); return TaskState.COMPLETED_SUCCESSFULLY; } } } opends/src/server/org/opends/server/util/ServerConstants.java
@@ -498,6 +498,32 @@ /** * The name of the attribute that is used to specify the connection ID of the * connection to disconnect. */ public static final String ATTR_TASK_DISCONNECT_CONN_ID = "ds-task-disconnect-connection-id"; /** * The name of the attribute that is used to specify the disconnect message. */ public static final String ATTR_TASK_DISCONNECT_MESSAGE = "ds-task-disconnect-message"; /** * The name of the attribute that is used to indicate whether to notify the * connection it is about to be terminated. */ public static final String ATTR_TASK_DISCONNECT_NOTIFY_CLIENT = "ds-task-disconnect-notify-client"; /** * The name of the attribute that is used to specify the total number of * connections established since startup, formatted in camel case. */ @@ -687,6 +713,15 @@ /** * The OID for the extended operation that can be used to get the client * connection ID. It will be both the request and response OID. */ public static final String OID_GET_CONNECTION_ID_EXTOP = "1.3.6.1.4.1.26027.1.6.2"; /** * The request OID for the password modify extended operation. */ public static final String OID_PASSWORD_MODIFY_REQUEST = opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DisconnectClientTaskTestCase.java
New file @@ -0,0 +1,271 @@ /* * 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.tasks; import java.net.Socket; import org.testng.annotations.Test; import org.testng.annotations.BeforeClass; import org.opends.server.TestCaseUtils; import org.opends.server.backends.task.Task; import org.opends.server.backends.task.TaskBackend; import org.opends.server.backends.task.TaskState; import org.opends.server.core.DirectoryServer; import org.opends.server.extensions.GetConnectionIDExtendedOperation; import org.opends.server.protocols.asn1.*; import org.opends.server.protocols.ldap.*; import org.opends.server.types.DN; import static org.testng.Assert.*; import static org.opends.server.util.ServerConstants.*; /** * Tests the disconnect client task. */ public class DisconnectClientTaskTestCase extends TasksTestCase { /** * Make sure that the Directory Server is running. * * @throws Exception If an unexpected problem occurs. */ @BeforeClass() public void startServer() throws Exception { TestCaseUtils.startServer(); } /** * Tests the ability of the server to disconnect an arbitrary client * connection with a notice of disconnection. * * @throws Exception If an unexpected problem occurs. */ @Test() public void testDisconnectWithNotification() throws Exception { // Establish a connection to the server, bind, and get the connection ID. Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort()); ASN1Reader r = new ASN1Reader(s); ASN1Writer w = new ASN1Writer(s); BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"), 3, new ASN1OctetString("password")); LDAPMessage message = new LDAPMessage(1, bindRequest); w.writeElement(message.encode()); message = LDAPMessage.decode(r.readElement().decodeAsSequence()); BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp(); assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS); ExtendedRequestProtocolOp extendedRequest = new ExtendedRequestProtocolOp(OID_GET_CONNECTION_ID_EXTOP); message = new LDAPMessage(2, extendedRequest); w.writeElement(message.encode()); message = LDAPMessage.decode(r.readElement().decodeAsSequence()); ExtendedResponseProtocolOp extendedResponse = message.getExtendedResponseProtocolOp(); assertEquals(extendedResponse.getResultCode(), LDAPResultCode.SUCCESS); assertEquals(extendedResponse.getOID(), OID_GET_CONNECTION_ID_EXTOP); long connectionID = GetConnectionIDExtendedOperation.decodeResponseValue( extendedResponse.getValue()); // Invoke the disconnect client task. String taskID = "Disconnect Client " + connectionID; String disconnectMessage = "testDisconnectWithNotification"; DN taskDN = DN.decode("ds-task-id=" + taskID + ",cn=Scheduled Tasks,cn=Tasks"); TestCaseUtils.addEntry( "dn: " + taskDN.toString(), "objectClass: top", "objectClass: ds-task", "objectClass: ds-task-disconnect", "ds-task-id: " + taskID, "ds-task-class-name: org.opends.server.tasks.DisconnectClientTask", "ds-task-disconnect-connection-id: " + connectionID, "ds-task-disconnect-notify-client: true", "ds-task-disconnect-message: " + disconnectMessage); Task task = getCompletedTask(taskDN); assertNotNull(task); assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY); // Make sure that we get a notice of disconnection on the initial // connection. message = LDAPMessage.decode(r.readElement().decodeAsSequence()); extendedResponse = message.getExtendedResponseProtocolOp(); assertEquals(extendedResponse.getOID(), LDAPConstants.OID_NOTICE_OF_DISCONNECTION); assertEquals(extendedResponse.getErrorMessage(), disconnectMessage); try { s.close(); } catch (Exception e) {} } /** * Tests the ability of the server to disconnect an arbitrary client * connection without a notice of disconnection. * * @throws Exception If an unexpected problem occurs. */ @Test() public void testDisconnectWithoutNotification() throws Exception { // Establish a connection to the server, bind, and get the connection ID. Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort()); ASN1Reader r = new ASN1Reader(s); ASN1Writer w = new ASN1Writer(s); BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"), 3, new ASN1OctetString("password")); LDAPMessage message = new LDAPMessage(1, bindRequest); w.writeElement(message.encode()); message = LDAPMessage.decode(r.readElement().decodeAsSequence()); BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp(); assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS); ExtendedRequestProtocolOp extendedRequest = new ExtendedRequestProtocolOp(OID_GET_CONNECTION_ID_EXTOP); message = new LDAPMessage(2, extendedRequest); w.writeElement(message.encode()); message = LDAPMessage.decode(r.readElement().decodeAsSequence()); ExtendedResponseProtocolOp extendedResponse = message.getExtendedResponseProtocolOp(); assertEquals(extendedResponse.getResultCode(), LDAPResultCode.SUCCESS); assertEquals(extendedResponse.getOID(), OID_GET_CONNECTION_ID_EXTOP); long connectionID = GetConnectionIDExtendedOperation.decodeResponseValue( extendedResponse.getValue()); // Invoke the disconnect client task. String taskID = "Disconnect Client " + connectionID; DN taskDN = DN.decode("ds-task-id=" + taskID + ",cn=Scheduled Tasks,cn=Tasks"); TestCaseUtils.addEntry( "dn: " + taskDN.toString(), "objectClass: top", "objectClass: ds-task", "objectClass: ds-task-disconnect", "ds-task-id: " + taskID, "ds-task-class-name: org.opends.server.tasks.DisconnectClientTask", "ds-task-disconnect-connection-id: " + connectionID, "ds-task-disconnect-notify-client: false"); Task task = getCompletedTask(taskDN); assertNotNull(task); assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY); // Make sure that the client connection has been closed with no notice of // disconnection. assertNull(r.readElement()); try { s.close(); } catch (Exception e) {} } /** * 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. */ private Task getCompletedTask(DN taskEntryDN) throws Exception { TaskBackend taskBackend = (TaskBackend) DirectoryServer.getBackend(DN.decode("cn=tasks")); Task task = taskBackend.getScheduledTask(taskEntryDN); if (task == null) { long stopWaitingTime = System.currentTimeMillis() + 10000L; while ((task == null) && (System.currentTimeMillis() < stopWaitingTime)) { Thread.sleep(10); task = taskBackend.getScheduledTask(taskEntryDN); } } if (task == null) { throw new AssertionError("There is no such task " + taskEntryDN.toString()); } if (! TaskState.isDone(task.getTaskState())) { long stopWaitingTime = System.currentTimeMillis() + 20000L; while ((! TaskState.isDone(task.getTaskState())) && (System.currentTimeMillis() < stopWaitingTime)) { Thread.sleep(10); } } if (! TaskState.isDone(task.getTaskState())) { throw new AssertionError("Task " + taskEntryDN.toString() + " did not complete in a timely manner."); } return task; } }