/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive.control;
import java.nio.ByteBuffer;
import org.ldaptive.LdapUtils;
import org.ldaptive.asn1.AbstractParseHandler;
import org.ldaptive.asn1.DERParser;
import org.ldaptive.asn1.DERPath;
import org.ldaptive.asn1.IntegerType;
import org.ldaptive.asn1.OctetStringType;
/**
* Response control for persistent search. See http://tools.ietf.org/id/draft-ietf-ldapext-psearch-03.txt. Control is
* defined as:
*
* <pre>
EntryChangeNotification ::= SEQUENCE {
changeType ENUMERATED {
add (1),
delete (2),
modify (4),
modDN (8)
},
previousDN LDAPDN OPTIONAL, -- modifyDN ops. only
changeNumber INTEGER OPTIONAL -- if supported
}
* </pre>
*
* @author Middleware Services
*/
public class EntryChangeNotificationControl extends AbstractControl implements ResponseControl
{
/** OID of this control. */
public static final String OID = "2.16.840.1.113730.3.4.7";
/** hash code seed. */
private static final int HASH_CODE_SEED = 773;
/** change type. */
private PersistentSearchChangeType changeType;
/** previous dn. */
private String previousDn;
/** change number. */
private long changeNumber = -1;
/** Default constructor. */
public EntryChangeNotificationControl()
{
super(OID);
}
/**
* Creates a new entry change notification control.
*
* @param critical whether this control is critical
*/
public EntryChangeNotificationControl(final boolean critical)
{
super(OID, critical);
}
/**
* Creates a new entry change notification control.
*
* @param type persistent search change type
*/
public EntryChangeNotificationControl(final PersistentSearchChangeType type)
{
this(type, false);
}
/**
* Creates a new entry change notification control.
*
* @param type persistent search change type
* @param critical whether this control is critical
*/
public EntryChangeNotificationControl(final PersistentSearchChangeType type, final boolean critical)
{
super(OID, critical);
setChangeType(type);
}
/**
* Creates a new entry change notification control.
*
* @param type persistent search change type
* @param dn previous dn
* @param number change number
*/
public EntryChangeNotificationControl(final PersistentSearchChangeType type, final String dn, final long number)
{
this(type, dn, number, false);
}
/**
* Creates a new entry change notification control.
*
* @param type persistent search change type
* @param dn previous dn
* @param number change number
* @param critical whether this control is critical
*/
public EntryChangeNotificationControl(
final PersistentSearchChangeType type,
final String dn,
final long number,
final boolean critical)
{
super(OID, critical);
setChangeType(type);
setPreviousDn(dn);
setChangeNumber(number);
}
/**
* Returns the change type.
*
* @return change type
*/
public PersistentSearchChangeType getChangeType()
{
return changeType;
}
/**
* Sets the change type.
*
* @param type change type
*/
public void setChangeType(final PersistentSearchChangeType type)
{
changeType = type;
}
/**
* Returns the previous dn.
*
* @return previous dn
*/
public String getPreviousDn()
{
return previousDn;
}
/**
* Sets the previous dn.
*
* @param dn previous dn
*/
public void setPreviousDn(final String dn)
{
previousDn = dn;
}
/**
* Returns the change number.
*
* @return change number
*/
public long getChangeNumber()
{
return changeNumber;
}
/**
* Sets the change number.
*
* @param number change number
*/
public void setChangeNumber(final long number)
{
changeNumber = number;
}
@Override
public boolean equals(final Object o)
{
if (o == this) {
return true;
}
if (o instanceof EntryChangeNotificationControl && super.equals(o)) {
final EntryChangeNotificationControl v = (EntryChangeNotificationControl) o;
return LdapUtils.areEqual(changeType, v.changeType) &&
LdapUtils.areEqual(previousDn, v.previousDn) &&
LdapUtils.areEqual(changeNumber, v.changeNumber);
}
return false;
}
@Override
public int hashCode()
{
return LdapUtils.computeHashCode(HASH_CODE_SEED, getOID(), getCriticality(), changeType, previousDn, changeNumber);
}
@Override
public String toString()
{
return
String.format(
"[%s@%d::criticality=%s, changeType=%s, previousDn=%s, " +
"changeNumber=%s]",
getClass().getName(),
hashCode(),
getCriticality(),
changeType,
previousDn,
changeNumber);
}
@Override
public void decode(final byte[] berValue)
{
logger.trace("decoding control: {}", LdapUtils.base64Encode(berValue));
final DERParser parser = new DERParser();
parser.registerHandler(ChangeTypeHandler.PATH, new ChangeTypeHandler(this));
parser.registerHandler(PreviousDnHandler.PATH, new PreviousDnHandler(this));
parser.registerHandler(ChangeNumberHandler.PATH, new ChangeNumberHandler(this));
parser.parse(ByteBuffer.wrap(berValue));
}
/** Parse handler implementation for the change type. */
private static class ChangeTypeHandler extends AbstractParseHandler<EntryChangeNotificationControl>
{
/** DER path to change type. */
public static final DERPath PATH = new DERPath("/SEQ/ENUM");
/**
* Creates a new change type handler.
*
* @param control to configure
*/
ChangeTypeHandler(final EntryChangeNotificationControl control)
{
super(control);
}
@Override
public void handle(final DERParser parser, final ByteBuffer encoded)
{
final int typeValue = IntegerType.decode(encoded).intValue();
final PersistentSearchChangeType ct = PersistentSearchChangeType.valueOf(typeValue);
if (ct == null) {
throw new IllegalArgumentException("Unknown change type code " + typeValue);
}
getObject().setChangeType(ct);
}
}
/** Parse handler implementation for the previous dn. */
private static class PreviousDnHandler extends AbstractParseHandler<EntryChangeNotificationControl>
{
/** DER path to previous dn. */
public static final DERPath PATH = new DERPath("/SEQ/OCTSTR[1]");
/**
* Creates a new previous dn handler.
*
* @param control to configure
*/
PreviousDnHandler(final EntryChangeNotificationControl control)
{
super(control);
}
@Override
public void handle(final DERParser parser, final ByteBuffer encoded)
{
getObject().setPreviousDn(OctetStringType.decode(encoded));
}
}
/** Parse handler implementation for the change number. */
private static class ChangeNumberHandler extends AbstractParseHandler<EntryChangeNotificationControl>
{
/** DER path to change number. */
public static final DERPath PATH = new DERPath("/SEQ/INT[2]");
/**
* Creates a new change number handler.
*
* @param control to configure
*/
ChangeNumberHandler(final EntryChangeNotificationControl control)
{
super(control);
}
@Override
public void handle(final DERParser parser, final ByteBuffer encoded)
{
getObject().setChangeNumber(IntegerType.decode(encoded).intValue());
}
}
}