001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.controls; 018import org.forgerock.i18n.LocalizableMessage; 019 020 021import java.util.Set; 022import java.io.IOException; 023 024import org.forgerock.opendj.io.*; 025import org.forgerock.i18n.slf4j.LocalizedLogger; 026import org.opends.server.types.*; 027import org.forgerock.opendj.ldap.ResultCode; 028import org.forgerock.opendj.ldap.ByteString; 029import static org.opends.messages.ProtocolMessages.*; 030import static org.opends.server.util.ServerConstants.*; 031import static org.opends.server.util.StaticUtils.*; 032 033 034 035/** 036 * This class implements the persistent search control defined in 037 * draft-ietf-ldapext-psearch. It makes it possible for clients to be notified 038 * of changes to information in the Directory Server as they occur. 039 */ 040public class PersistentSearchControl 041 extends Control 042{ 043 /** ControlDecoder implementation to decode this control from a ByteString. */ 044 private static final class Decoder 045 implements ControlDecoder<PersistentSearchControl> 046 { 047 @Override 048 public PersistentSearchControl decode(boolean isCritical, ByteString value) 049 throws DirectoryException 050 { 051 if (value == null) 052 { 053 LocalizableMessage message = ERR_PSEARCH_NO_CONTROL_VALUE.get(); 054 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 055 } 056 057 ASN1Reader reader = ASN1.getReader(value); 058 boolean changesOnly; 059 boolean returnECs; 060 Set<PersistentSearchChangeType> changeTypes; 061 try 062 { 063 reader.readStartSequence(); 064 065 int changeTypesValue = (int)reader.readInteger(); 066 changeTypes = PersistentSearchChangeType.intToTypes(changeTypesValue); 067 changesOnly = reader.readBoolean(); 068 returnECs = reader.readBoolean(); 069 070 reader.readEndSequence(); 071 } 072 catch (LDAPException le) 073 { 074 throw new DirectoryException(ResultCode.valueOf(le.getResultCode()), le 075 .getMessageObject()); 076 } 077 catch (Exception e) 078 { 079 logger.traceException(e); 080 081 LocalizableMessage message = 082 ERR_PSEARCH_CANNOT_DECODE_VALUE.get(getExceptionMessage(e)); 083 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, e); 084 } 085 086 087 return new PersistentSearchControl(isCritical, 088 changeTypes, changesOnly, returnECs); 089 } 090 091 @Override 092 public String getOID() 093 { 094 return OID_PERSISTENT_SEARCH; 095 } 096 097 } 098 099 /** The Control Decoder that can be used to decode this control. */ 100 public static final ControlDecoder<PersistentSearchControl> DECODER = 101 new Decoder(); 102 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 103 104 105 106 107 /** 108 * Indicates whether to only return entries that have been updated since the 109 * beginning of the search. 110 */ 111 private boolean changesOnly; 112 113 /** 114 * Indicates whether entries returned as a result of changes to directory data 115 * should include the entry change notification control. 116 */ 117 private boolean returnECs; 118 119 /** The set of change types associated with this control. */ 120 private Set<PersistentSearchChangeType> changeTypes; 121 122 123 124 /** 125 * Creates a new persistent search control with the provided information. 126 * 127 * @param changeTypes The set of change types for which to provide 128 * notification to the client. 129 * @param changesOnly Indicates whether to only return changes that match 130 * the associated search criteria, or to also return all 131 * existing entries that match the filter. 132 * @param returnECs Indicates whether to include the entry change 133 * notification control in updated entries that match the 134 * associated search criteria. 135 */ 136 public PersistentSearchControl(Set<PersistentSearchChangeType> changeTypes, 137 boolean changesOnly, boolean returnECs) 138 { 139 this(true, changeTypes, changesOnly, returnECs); 140 } 141 142 143 144 /** 145 * Creates a new persistent search control with the provided information. 146 * 147 * @param isCritical Indicates whether the control should be considered 148 * critical for the operation processing. 149 * @param changeTypes The set of change types for which to provide 150 * notification to the client. 151 * @param changesOnly Indicates whether to only return changes that match 152 * the associated search criteria, or to also return all 153 * existing entries that match the filter. 154 * @param returnECs Indicates whether to include the entry change 155 * notification control in updated entries that match the 156 * associated search criteria. 157 */ 158 public PersistentSearchControl(boolean isCritical, 159 Set<PersistentSearchChangeType> changeTypes, 160 boolean changesOnly, boolean returnECs) 161 { 162 super(OID_PERSISTENT_SEARCH, isCritical); 163 164 165 this.changeTypes = changeTypes; 166 this.changesOnly = changesOnly; 167 this.returnECs = returnECs; 168 } 169 170 171 172 /** 173 * Writes this control's value to an ASN.1 writer. The value (if any) must be 174 * written as an ASN1OctetString. 175 * 176 * @param writer The ASN.1 writer to use. 177 * @throws IOException If a problem occurs while writing to the stream. 178 */ 179 @Override 180 protected void writeValue(ASN1Writer writer) throws IOException { 181 writer.writeStartSequence(ASN1.UNIVERSAL_OCTET_STRING_TYPE); 182 183 writer.writeStartSequence(); 184 writer.writeInteger( 185 PersistentSearchChangeType.changeTypesToInt(changeTypes)); 186 writer.writeBoolean(changesOnly); 187 writer.writeBoolean(returnECs); 188 writer.writeEndSequence(); 189 190 writer.writeEndSequence(); 191 } 192 193 194 195 /** 196 * Retrieves the set of change types for this persistent search control. 197 * 198 * @return The set of change types for this persistent search control. 199 */ 200 public Set<PersistentSearchChangeType> getChangeTypes() 201 { 202 return changeTypes; 203 } 204 205 206 207 /** 208 * Indicates whether to only return changes that match the associated search 209 * criteria, or to also return all existing entries that match the filter. 210 * 211 * @return <CODE>true</CODE> if only changes to matching entries should be 212 * returned, or <CODE>false</CODE> if existing matches should also be 213 * included. 214 */ 215 public boolean getChangesOnly() 216 { 217 return changesOnly; 218 } 219 220 221 222 /** 223 * Indicates whether to include the entry change notification control in 224 * entries returned to the client as the result of a change in the Directory 225 * Server data. 226 * 227 * @return <CODE>true</CODE> if entry change notification controls should be 228 * included in applicable entries, or <CODE>false</CODE> if not. 229 */ 230 public boolean getReturnECs() 231 { 232 return returnECs; 233 } 234 235 236 237 /** 238 * Appends a string representation of this persistent search control to the 239 * provided buffer. 240 * 241 * @param buffer The buffer to which the information should be appended. 242 */ 243 @Override 244 public void toString(StringBuilder buffer) 245 { 246 buffer.append("PersistentSearchControl(changeTypes=\""); 247 PersistentSearchChangeType.changeTypesToString(changeTypes, buffer); 248 buffer.append("\",changesOnly="); 249 buffer.append(changesOnly); 250 buffer.append(",returnECs="); 251 buffer.append(returnECs); 252 buffer.append(")"); 253 } 254} 255