/*
* Sun Public License
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is available at http://www.sun.com/
*
* The Original Code is the SLAMD Distributed Load Generation Engine.
* The Initial Developer of the Original Code is Neil A. Wilson.
* Portions created by Neil A. Wilson are Copyright (C) 2004-2010.
* Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): Neil A. Wilson
*/
package com.slamd.tools.ldapdecoder.protocol;
import com.slamd.asn1.ASN1Boolean;
import com.slamd.asn1.ASN1Element;
import com.slamd.asn1.ASN1Integer;
import com.slamd.asn1.ASN1OctetString;
import com.slamd.asn1.ASN1Sequence;
/**
* This class defines the LDAP persistent search control, which is used to
* provide notification of changes to entries meeting certain criteria.
*
*
* @author Neil A. Wilson
*/
public class PersistentSearchControl
extends LDAPControl
{
/**
* The OID of the persistent search control.
*/
public static final String PERSISTENT_SEARCH_CONTROL_OID =
"2.16.840.1.113730.3.4.3";
/**
* The change type that indicates that notification should be provided for
* add operations.
*/
public static final int CHANGE_TYPE_ADD = 1;
/**
* The change type that indicates that notification should be provided for
* delete operations.
*/
public static final int CHANGE_TYPE_DELETE = 2;
/**
* The change type that indicates that notification should be provided for
* modify operations.
*/
public static final int CHANGE_TYPE_MODIFY = 4;
/**
* The change type that indicates that notification should be provided for
* modify DN operations.
*/
public static final int CHANGE_TYPE_MODIFY_DN = 8;
// Indicates whether only changes should be returned.
private boolean changesOnly;
// Indicates whether change notification controls should be included in
// matching entries.
private boolean returnChangeControls;
// The types of changes for which notification should be provided.
private int changeTypes;
/**
* Creates a new persistent search control with the provided information.
*
* @param isCritical Indicates whether this control should be
* marked critical.
* @param changeTypes The types of changes for which to provide
* notification.
* @param changesOnly Indicates whether only changes should be
* returned, or both changes and any existing
* matches.
* @param returnChangeControls Indicates whether entry change notification
* controls should be returned in matching
* entries.
*/
public PersistentSearchControl(boolean isCritical, int changeTypes,
boolean changesOnly,
boolean returnChangeControls)
{
super(PERSISTENT_SEARCH_CONTROL_OID, isCritical,
encodeValue(changeTypes, changesOnly, returnChangeControls));
this.changeTypes = changeTypes;
this.changesOnly = changesOnly;
this.returnChangeControls = returnChangeControls;
}
/**
* Creates a new persistent search control with the provided information.
*
* @param isCritical Indicates whether this control should be marked
* critical.
* @param controlValue The encoded value for this persistent search control.
*
* @throws ProtocolException If a problem occurs while decoding the control
* value.
*/
public PersistentSearchControl(boolean isCritical,
ASN1OctetString controlValue)
throws ProtocolException
{
super(PERSISTENT_SEARCH_CONTROL_OID, isCritical, controlValue);
decodeValue();
}
/**
* Retrieves the types of changes for which to provide notification.
*
* @return The types of changes for which to provide notification.
*/
public int getChangeTypes()
{
return changeTypes;
}
/**
* Indicates whether only changes matching the search criteria should be
* returned, or whether existing entries matching the criteria should also be
* included.
*
* @return <CODE>false</CODE> if existing entries matching the search
* criteria should be returned as well as changes, or
* <CODE>true</CODE> if only changes should be returned.
*/
public boolean changesOnly()
{
return changesOnly;
}
/**
* Indicates whether entry change notification controls should be included
* with the change notifications.
*
* @return <CODE>true</CODE> if change notification controls should be
* included with the change notifications, or <CODE>false</CODE> if
* not.
*/
public boolean returnChangeControls()
{
return returnChangeControls;
}
/**
* Encodes the provided information to an ASN.1 element suitable for the value
* of this control.
*
* @param changeTypes The change types for which notification
* should be provided.
* @param changesOnly Indicates whether to only return changes
* matching the criteria.
* @param returnChangeControls Indicates whether entry change notification
* controls should be returned.
*
* @return The octet string containing the encoded control value.
*/
public static ASN1OctetString encodeValue(int changeTypes,
boolean changesOnly,
boolean returnChangeControls)
{
ASN1Element[] sequenceElements = new ASN1Element[]
{
new ASN1Integer(changeTypes),
new ASN1Boolean(changesOnly),
new ASN1Boolean(returnChangeControls)
};
ASN1Sequence valueSequence = new ASN1Sequence(sequenceElements);
return new ASN1OctetString(valueSequence.encode());
}
/**
* Decodes the value for this control to extract the appropriate information
* from it.
*
* @throws ProtocolException If the control value cannot be decoded
* appropriately for a persistent search.
*/
private void decodeValue()
throws ProtocolException
{
ASN1Element[] sequenceElements;
try
{
byte[] valueBytes = getValue().getValue();
sequenceElements = ASN1Element.decodeAsSequence(valueBytes).getElements();
}
catch (Exception e)
{
throw new ProtocolException("Unable to decode persistent search value",
e);
}
if (sequenceElements.length != 3)
{
throw new ProtocolException("There must be exactly 3 elements in a " +
"persistent search control value sequence");
}
try
{
changeTypes = sequenceElements[0].decodeAsInteger().getIntValue();
}
catch (Exception e)
{
throw new ProtocolException("Unable to decode changeTypes from " +
"persistent search control", e);
}
try
{
changesOnly = sequenceElements[1].decodeAsBoolean().getBooleanValue();
}
catch (Exception e)
{
throw new ProtocolException("Unable to decode changesOnly from " +
"persistent search control", e);
}
try
{
returnChangeControls =
sequenceElements[2].decodeAsBoolean().getBooleanValue();
}
catch (Exception e)
{
throw new ProtocolException("Unable to decode returnECs from " +
"persistent search control", e);
}
}
/**
* Retrieves a string representation of this control with the specified
* indent.
*
* @param indent The number of spaces to indent the output.
*
* @return A string representation of this control with the specified indent.
*/
public String toString(int indent)
{
StringBuilder indentBuf = new StringBuilder(indent);
for (int i=0; i < indent; i++)
{
indentBuf.append(' ');
}
StringBuilder buffer = new StringBuilder();
buffer.append(indentBuf).append("LDAP Persistent Search Control").
append(LDAPMessage.EOL);
buffer.append(indentBuf).append(" OID: ").append(getControlOID()).
append(LDAPMessage.EOL);
buffer.append(indentBuf).append(" Criticality: ").
append(isCritical()).append(LDAPMessage.EOL);
buffer.append(indentBuf).append(" Change Types: ").
append(changeTypes).append(" (");
String separator = "";
if (changeTypes == 0)
{
buffer.append("none").append(LDAPMessage.EOL);
}
else
{
if ((changeTypes & CHANGE_TYPE_ADD) == CHANGE_TYPE_ADD)
{
buffer.append("add");
separator = ", ";
}
if ((changeTypes & CHANGE_TYPE_DELETE) == CHANGE_TYPE_DELETE)
{
buffer.append(separator).append("delete");
separator = ", ";
}
if ((changeTypes & CHANGE_TYPE_MODIFY) == CHANGE_TYPE_MODIFY)
{
buffer.append(separator).append("modify");
separator = ", ";
}
if ((changeTypes & CHANGE_TYPE_MODIFY_DN) == CHANGE_TYPE_MODIFY_DN)
{
buffer.append(separator).append("modify DN");
separator = ", ";
}
buffer.append(')').append(LDAPMessage.EOL);
}
buffer.append(indentBuf).append(" Changes Only: ").
append(changesOnly).append(LDAPMessage.EOL);
buffer.append(indentBuf).
append(" Return Change Notification Controls: ").
append(returnChangeControls).append(LDAPMessage.EOL);
return buffer.toString();
}
}