/* * 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.unboundid.util.Base64; import com.slamd.asn1.ASN1Element; import com.slamd.asn1.ASN1Integer; import com.slamd.asn1.ASN1OctetString; import com.slamd.asn1.ASN1Sequence; /** * This class defines the virtual list view (VLV) request control, which is used * to indicate that only a particular range of the results should be returned * and that potentially other ranges may be requested. Note that if this * control is included in a request, then the server-side sort control must also * be included. * * * @author Neil A. Wilson */ public class VLVRequestControl extends LDAPControl { /** * The OID of the VLV request control. */ public static final String VLV_REQUEST_CONTROL_OID = "2.16.840.1.113730.3.4.9"; /** * The selection type that indicates that the target entry will be selected by * its offset in the list of search results. */ public static final byte SELECT_TYPE_BY_OFFSET = (byte) 0xA0; /** * The selection type that indicates that the target entry will be selected as * the first entry greater than or equal to the provided assertion value. The * comparison will be made against the primary sort attribute defined in the * server-side sort control. */ public static final byte SELECT_TYPE_BY_ASSERTION_VALUE = (byte) 0x81; // The assertion value to use to find the target entry. private ASN1OctetString assertionValue; // The method that will be used to select the entry that is the starting point // for the results. private byte selectType; // The number of entries after the requested entry that should be returned. private int afterCount; // The number of entries before the requested entry that should be returned. private int beforeCount; // The total number of entries matching the search criteria. private int contentCount; // The offset of the target entry in the list of matches. private int entryOffset; /** * Creates a new virtual list view request control. * * @param isCritical Indicates whether this control should be marked * critical. * @param beforeCount The number of entries before the requested target * entry that should be returned. * @param afterCount The number of entries after the requested target * entry that should be returned. * @param entryOffset The offset of the target entry in the list of search * results. * @param contentCount The total number of entries that the client believes * match the search criteria. If this is not known * (e.g., if this is the first request in a series of * VLV retrievals), then this should be zero. */ public VLVRequestControl(boolean isCritical, int beforeCount, int afterCount, int entryOffset, int contentCount) { super(VLV_REQUEST_CONTROL_OID, isCritical, encodeValue(beforeCount, afterCount, entryOffset, contentCount)); this.selectType = SELECT_TYPE_BY_OFFSET; this.beforeCount = beforeCount; this.afterCount = afterCount; this.entryOffset = entryOffset; this.contentCount = contentCount; } /** * Creates a new virtual list view request control. * * @param isCritical Indicates whether this control should be marked * critical. * @param beforeCount The number of entries before the requested target * entry that should be returned. * @param afterCount The number of entries after the requested target * entry that should be returned. * @param assertionValue The assertion value that should be used to locate * the target entry. */ public VLVRequestControl(boolean isCritical, int beforeCount, int afterCount, ASN1OctetString assertionValue) { super(VLV_REQUEST_CONTROL_OID, isCritical, encodeValue(beforeCount, afterCount, assertionValue)); this.selectType = SELECT_TYPE_BY_ASSERTION_VALUE; this.beforeCount = beforeCount; this.afterCount = afterCount; this.assertionValue = assertionValue; } /** * Creates a new virtual list view request control. * * @param isCritical Indicates whether this control should be marked * critical. * @param controlValue The encoded value for the VLV request control. * * @throws ProtocolException If the provided control value cannot be * decoded appropriately for a VLV request * control. */ public VLVRequestControl(boolean isCritical, ASN1OctetString controlValue) throws ProtocolException { super(VLV_REQUEST_CONTROL_OID, isCritical, controlValue); ASN1Element[] sequenceElements; try { byte[] valueBytes = controlValue.getValue(); sequenceElements = ASN1Element.decodeAsSequence(valueBytes).getElements(); } catch (Exception e) { throw new ProtocolException("Unable to decode VLV request control " + "sequence", e); } if (sequenceElements.length != 3) { throw new ProtocolException("There must be exactly 3 elements in a VLV " + "request control sequence"); } try { beforeCount = sequenceElements[0].decodeAsInteger().getIntValue(); } catch (Exception e) { throw new ProtocolException("Unable to decode beforeCount from VLV " + "request control sequence", e); } try { afterCount = sequenceElements[1].decodeAsInteger().getIntValue(); } catch (Exception e) { throw new ProtocolException("Unable to decode afterCount from VLV " + "request control sequence", e); } selectType = sequenceElements[2].getType(); if (selectType == SELECT_TYPE_BY_OFFSET) { ASN1Element[] offsetElements; try { offsetElements = sequenceElements[2].decodeAsSequence().getElements(); } catch (Exception e) { throw new ProtocolException("Unable to decode byOffset sequence " + "elements in VLV request control", e); } if (offsetElements.length != 2) { throw new ProtocolException("There must be exactly 2 elements in the " + "byOffset sequence of a VLV request " + "control"); } try { entryOffset = offsetElements[0].decodeAsInteger().getIntValue(); } catch (Exception e) { throw new ProtocolException("Unable to decode entry offset from the " + "VLV request control", e); } try { contentCount = offsetElements[1].decodeAsInteger().getIntValue(); } catch (Exception e) { throw new ProtocolException("Unable to decode content count from the " + "VLV request control", e); } } else if (selectType == SELECT_TYPE_BY_ASSERTION_VALUE) { try { assertionValue = sequenceElements[2].decodeAsOctetString(); } catch (Exception e) { throw new ProtocolException("Unable to decode greaterThanOrEqual " + "assertion value from VLV request control", e); } } else { throw new ProtocolException("Invalid entry selection type (" + selectType + ')'); } } /** * Encodes the provided information into a VLV request control value. * * @param beforeCount The number of entries before the requested target * entry that should be returned. * @param afterCount The number of entries after the requested target * entry that should be returned. * @param entryOffset The offset of the target entry in the list of search * results. * @param contentCount The total number of entries that the client believes * match the search criteria. If this is not known * (e.g., if this is the first request in a series of * VLV retrievals), then this should be zero. * * @return The encoded VLV request control value. */ public static ASN1OctetString encodeValue(int beforeCount, int afterCount, int entryOffset, int contentCount) { ASN1Element[] offsetElements = new ASN1Element[] { new ASN1Integer(entryOffset), new ASN1Integer(contentCount) }; ASN1Element[] sequenceElements = new ASN1Element[] { new ASN1Integer(beforeCount), new ASN1Integer(afterCount), new ASN1Sequence(SELECT_TYPE_BY_OFFSET, offsetElements) }; return new ASN1OctetString(new ASN1Sequence(sequenceElements).encode()); } /** * Encodes the provided information into a VLV request control value. * * @param beforeCount The number of entries before the requested target * entry that should be returned. * @param afterCount The number of entries after the requested target * entry that should be returned. * @param assertionValue The assertion value that should be used to locate * the target entry. * * @return The encoded VLV request control value. */ public static ASN1OctetString encodeValue(int beforeCount, int afterCount, ASN1OctetString assertionValue) { assertionValue.setType(SELECT_TYPE_BY_ASSERTION_VALUE); ASN1Element[] sequenceElements = new ASN1Element[] { new ASN1Integer(beforeCount), new ASN1Integer(afterCount), assertionValue }; return new ASN1OctetString(new ASN1Sequence(sequenceElements).encode()); } /** * Retrieves the number of entries before the target entry that should be * retrieved. * * @return The number of entries before the target entry that should be * retrieved. */ public int getBeforeCount() { return beforeCount; } /** * Retrieves the number of entries after the target entry that should be * retrieved. * * @return The number of entries after the target entry that should be * retrieved. */ public int getAfterCount() { return afterCount; } /** * Retrieves the flag that indicates the means of selecting the target entry. * * @return The flag that indicates the means of selecting the target entry. */ public byte getSelectionType() { return selectType; } /** * Retrieves the offset of the target entry in the result set. * * @return The offset of the target entry in the result set. */ public int getEntryOffset() { return entryOffset; } /** * Retrieves the number of entries the client believes are in the result set. * * @return The number of entries the client believes are in the result set. */ public int getContentCount() { return contentCount; } /** * Retrieves the assertion value that should be used to locate the target * entry in the result set. * * @return The assertion value that should be used to locate the target entry * in the result set. */ public ASN1OctetString getAssertionValue() { return assertionValue; } /** * 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 VLV Request 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(" Before Count: "). append(beforeCount).append(LDAPMessage.EOL); buffer.append(indentBuf).append(" After Count: "). append(afterCount).append(LDAPMessage.EOL); if (selectType == SELECT_TYPE_BY_OFFSET) { buffer.append(indentBuf).append(" Selection Type: By Entry Offset"). append(LDAPMessage.EOL); buffer.append(indentBuf).append(" Entry Offset: "). append(entryOffset).append(LDAPMessage.EOL); buffer.append(indentBuf).append(" Content Count: "). append(contentCount).append(LDAPMessage.EOL); } else if (selectType == SELECT_TYPE_BY_ASSERTION_VALUE) { buffer.append(indentBuf). append(" Selection Type: By Assertion Value"). append(LDAPMessage.EOL); if (LDAPAttribute.valueNeedsBase64Encoding(assertionValue)) { buffer.append(indentBuf).append(" Assertion Value:: "). append(Base64.encode(assertionValue.getValue())). append(LDAPMessage.EOL); } else { buffer.append(indentBuf).append(" Assertion Value: "). append(assertionValue.getStringValue()).append(LDAPMessage.EOL); } } else { buffer.append(indentBuf). append(" Selection Type: Invalid Type (").append(selectType). append(')').append(LDAPMessage.EOL); } return buffer.toString(); } }