This class provides a request control that may be included in an add, modify,
or modify DN request to ensure that the contents of that request will not
result in a uniqueness conflict with any other entry in the server. Each
instance of this control should define exactly one uniqueness constraint for
the associated operation. Multiple instances of this control can be included
in the same request to define multiple independent uniqueness constraints
that must all be satisfied. If any of the uniqueness constraints is not
satisfied, then the corresponding LDAP result should have a result code of
ResultCode.ASSERTION_FAILED
and a
UniquenessResponseControl
for each uniqueness constraint that was not satisfied.
NOTE: This class, and other classes within the
com.unboundid.ldap.sdk.unboundidds
package structure, are only
supported for use against Ping Identity, UnboundID, and
Nokia/Alcatel-Lucent 8661 server products. These classes provide support
for proprietary functionality or for external specifications that are not
considered stable or mature enough to be guaranteed to work in an
interoperable way with other types of LDAP servers.
The request properties must contain either one or more attribute types, a
filter, or both. If only a filter is specified, then the server will use
that filter to identify conflicts (for an add request, any matches at all
will be considered a conflict; for a modify or modify DN request, any matches
with any entry other than the one being updated will be considered a
conflict). If a single attribute type is specified with no filter, then any
change that would result in multiple entries having the same value for that
attribute will be considered a conflict. If multiple attribute types are
specified, then the multiple attribute behavior will be used to determine how
to identify conflicts, as documented in the
UniquenessMultipleAttributeBehavior
enum. If both a set of attribute
types and a filter are provided, then only entries matching both sets of
criteria will be considered a conflict.
The server can perform two different searches in an attempt to identify
conflicts. In the pre-commit phase, it will attempt to identify any
conflicts that already exist, and will reject the associated change if there
are any. In the post-commit phase, it can see if there were any conflicts
introduced by the change itself or by another change happening at the same
time. If a conflict is detected in the post-commit phase, then the server
won't have prevented it, but at least the control can be used to provide
notification about it.
This request control may be sent either directly to a Directory Server
instance, or it may be sent to a Directory Proxy Server with or without entry
balancing. If the request is sent directly to a Directory Server, then only
that one server will be checked for uniqueness conflicts, and it is possible
that concurrent conflicts may be introduced on other servers that have not
yet been replicated by the time control processing has completed. If the
request is sent to a Directory Proxy Server instance, then search may be
processed in one or more backend servers based on the pre-commit and
post-commit validation levels, and at the most paranoid levels, it is highly
unlikely that any conflicts will go unnoticed.
The request control has an OID of 1.3.6.1.4.1.30221.2.5.52, a criticality of
either
true
or
false
, and a value with the following
encoding:
UniquenessRequestValue ::= SEQUENCE {
uniquenessID [0] OCTET STRING,
attributeTypes [1] SET OF OCTET STRING OPTIONAL,
multipleAttributeBehavior [2] ENUMERATED {
uniqueWithinEachAttribute (0),
uniqueAcrossAllAttributesIncludingInSameEntry (1),
uniqueAcrossAllAttributesExceptInSameEntry (2),
uniqueInCombination (3),
... } DEFAULT uniqueWithinEachAttribute,
baseDN [3] LDAPDN OPTIONAL,
filter [4] Filter OPTIONAL,
preventConflictsWithSoftDeletedEntries [5] BOOLEAN DEFAULT FALSE,
preCommitValidationLevel [6] ENUMERATED {
none (0),
allSubtreeViews (1),
allBackendSets (2),
allAvailableBackendServers (3),
... } DEFAULT allSubtreeViews,
postCommitValidationLevel [7] ENUMERATED {
none (0),
allSubtreeViews (1),
allBackendSets (2),
allAvailableBackendServers (3),
... } DEFAULT allSubtreeViews,
... }
Example
The following example demonstrates how to use the uniqueness request control
to only process an add operation if it does not result in multiple entries
that have the same uid value:
// Create the properties to build a uniqueness request control that
// will try to prevent an add operation from creating a new entry
// that has the same uid as an existing entry in the server. During
// pre-commit processing (which happens before the server actually
// processes the add), the server will check at least one server in
// each entry-balancing backend set (or just one server in a
// non-entry-balanced deployment). During post-commit processing
// (which happens if the add succeeds), the server will double-check
// that no conflicting entry was added on any available server in the
// topology. Also ensure that the server will not allow conflicts
// with soft-deleted entries.
final UniquenessRequestControlProperties uniquenessProperties =
new UniquenessRequestControlProperties("uid");
uniquenessProperties.setPreCommitValidationLevel(
UniquenessValidationLevel.ALL_BACKEND_SETS);
uniquenessProperties.setPostCommitValidationLevel(
UniquenessValidationLevel.ALL_AVAILABLE_BACKEND_SERVERS);
uniquenessProperties.setPreventConflictsWithSoftDeletedEntries(true);
// Create the request control. It will be critical so that the
// server will not attempt to process the add if it can't honor the
// uniqueness request.
final boolean isCritical = true;
final String uniquenessID = "uid-uniqueness";
final UniquenessRequestControl uniquenessRequestControl =
new UniquenessRequestControl(isCritical, uniquenessID,
uniquenessProperties);
// Attach the control to an add request.
addRequest.addControl(uniquenessRequestControl);
// Send the add request to the server and read the result.
try
{
final LDAPResult addResult = connection.add(addRequest);
// The add operation succeeded, so the entry should have been
// created, but there is still the possibility that a post-commit
// conflict was discovered, indicating that another request
// processed at about the same time as our add introduced a
// conflicting entry.
final Map<String,UniquenessResponseControl> uniquenessResponses;
try
{
uniquenessResponses = UniquenessResponseControl.get(addResult);
}
catch (final LDAPException e)
{
throw new RuntimeException(
"The add succeeded, but an error occurred while trying " +
"to decode a uniqueness response control in add " +
"result " + addResult + ": " +
StaticUtils.getExceptionMessage(e),
e);
}
final UniquenessResponseControl uniquenessResponseControl =
uniquenessResponses.get(uniquenessID);
if ((uniquenessResponseControl != null) &&
uniquenessResponseControl.uniquenessConflictFound())
{
throw new RuntimeException(
"The add succeeded, but a uniqueness conflict was found " +
"Uniqueness validation message: " +
uniquenessResponseControl.getValidationMessage());
}
}
catch (final LDAPException e)
{
// The add attempt failed. It might have been because of a
// uniqueness problem, or it could have been for some other reason.
// To figure out which it was, look to see if there is an
// appropriate uniqueness response control.
final Map<String, UniquenessResponseControl> uniquenessResponses;
try
{
uniquenessResponses =
UniquenessResponseControl.get(e.toLDAPResult());
}
catch (final LDAPException e2)
{
throw new LDAPException(e.getResultCode(),
"The add attempt failed with result " + e.toLDAPResult() +
", and an error occurred while trying to decode a " +
"uniqueness response control in the result: " +
StaticUtils.getExceptionMessage(e2),
e);
}
final UniquenessResponseControl uniquenessResponseControl =
uniquenessResponses.get(uniquenessID);
if (uniquenessResponseControl == null)
{
// The add result didn't include a uniqueness response control,
// indicating that the failure was not because of a uniqueness
// conflict.
throw e;
}
if (uniquenessResponseControl.uniquenessConflictFound())
{
// The add failed, and the uniqueness response control indicates
// that the failure was because of a uniqueness conflict.
final UniquenessValidationResult preCommitResult =
uniquenessResponseControl.getPreCommitValidationResult();
final UniquenessValidationResult postCommitResult =
uniquenessResponseControl.getPreCommitValidationResult();
final String validationMessage =
uniquenessResponseControl.getValidationMessage();
throw e;
}
else
{
// The add failed, but the uniqueness response control indicates
// that the failure was not because of a uniqueness conflict.
throw e;
}
}