001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.controls; 028 import org.opends.messages.Message; 029 030 031 032 import java.util.ArrayList; 033 import java.util.Set; 034 035 import org.opends.server.protocols.asn1.ASN1Boolean; 036 import org.opends.server.protocols.asn1.ASN1Element; 037 import org.opends.server.protocols.asn1.ASN1Integer; 038 import org.opends.server.protocols.asn1.ASN1OctetString; 039 import org.opends.server.protocols.asn1.ASN1Sequence; 040 import org.opends.server.protocols.ldap.LDAPResultCode; 041 import org.opends.server.types.Control; 042 import org.opends.server.types.LDAPException; 043 044 import static org.opends.server.loggers.debug.DebugLogger.*; 045 import org.opends.server.loggers.debug.DebugTracer; 046 import org.opends.server.types.DebugLogLevel; 047 import static org.opends.messages.ProtocolMessages.*; 048 import static org.opends.server.util.ServerConstants.*; 049 import static org.opends.server.util.StaticUtils.*; 050 051 052 053 /** 054 * This class implements the persistent search control defined in 055 * draft-ietf-ldapext-psearch. It makes it possible for clients to be notified 056 * of changes to information in the Directory Server as they occur. 057 */ 058 public class PersistentSearchControl 059 extends Control 060 { 061 /** 062 * The tracer object for the debug logger. 063 */ 064 private static final DebugTracer TRACER = getTracer(); 065 066 067 068 069 // Indicates whether to only return entries that have been updated since the 070 // beginning of the search. 071 private boolean changesOnly; 072 073 // Indicates whether entries returned as a result of changes to directory data 074 // should include the entry change notification control. 075 private boolean returnECs; 076 077 // The set of change types associated with this control. 078 private Set<PersistentSearchChangeType> changeTypes; 079 080 081 082 /** 083 * Creates a new persistent search control with the provided information. 084 * 085 * @param changeTypes The set of change types for which to provide 086 * notification to the client. 087 * @param changesOnly Indicates whether to only return changes that match 088 * the associated search criteria, or to also return all 089 * existing entries that match the filter. 090 * @param returnECs Indicates whether to include the entry change 091 * notification control in updated entries that match the 092 * associated search criteria. 093 */ 094 public PersistentSearchControl(Set<PersistentSearchChangeType> changeTypes, 095 boolean changesOnly, boolean returnECs) 096 { 097 super(OID_PERSISTENT_SEARCH, true, 098 encodeValue(changeTypes, changesOnly, returnECs)); 099 100 101 this.changeTypes = changeTypes; 102 this.changesOnly = changesOnly; 103 this.returnECs = returnECs; 104 } 105 106 107 108 /** 109 * Creates a new persistent search control with the provided information. 110 * 111 * @param oid The OID to use for the control. 112 * @param isCritical Indicates whether the control should be considered 113 * critical for the operation processing. 114 * @param changeTypes The set of change types for which to provide 115 * notification to the client. 116 * @param changesOnly Indicates whether to only return changes that match 117 * the associated search criteria, or to also return all 118 * existing entries that match the filter. 119 * @param returnECs Indicates whether to include the entry change 120 * notification control in updated entries that match the 121 * associated search criteria. 122 */ 123 public PersistentSearchControl(String oid, boolean isCritical, 124 Set<PersistentSearchChangeType> changeTypes, 125 boolean changesOnly, boolean returnECs) 126 { 127 super(oid, isCritical, encodeValue(changeTypes, changesOnly, returnECs)); 128 129 130 this.changeTypes = changeTypes; 131 this.changesOnly = changesOnly; 132 this.returnECs = returnECs; 133 } 134 135 136 137 /** 138 * Creates a new persistent search control with the provided information. 139 * 140 * @param oid The OID to use for the control. 141 * @param isCritical Indicates whether the control should be considered 142 * critical for the operation processing. 143 * @param changeTypes The set of change types for which to provide 144 * notification to the client. 145 * @param changesOnly Indicates whether to only return changes that match 146 * the associated search criteria, or to also return all 147 * existing entries that match the filter. 148 * @param returnECs Indicates whether to include the entry change 149 * notification control in updated entries that match 150 * the associated search criteria. 151 * @param encodedValue The pre-encoded value for the control. 152 */ 153 private PersistentSearchControl(String oid, boolean isCritical, 154 Set<PersistentSearchChangeType> changeTypes, 155 boolean changesOnly, boolean returnECs, 156 ASN1OctetString encodedValue) 157 { 158 super(oid, isCritical, encodedValue); 159 160 161 this.changeTypes = changeTypes; 162 this.changesOnly = changesOnly; 163 this.returnECs = returnECs; 164 } 165 166 167 168 /** 169 * Encodes the provided information into an ASN.1 octet string suitable for 170 * use as the control value. 171 * 172 * @param changeTypes The set of change types for which to provide 173 * notification to the client. 174 * @param changesOnly Indicates whether to only return changes that match 175 * the associated search criteria, or to also return all 176 * existing entries that match the filter. 177 * @param returnECs Indicates whether to include the entry change 178 * notification control in updated entries that match the 179 * associated search criteria. 180 * 181 * @return An ASN.1 octet string containing the encoded information. 182 */ 183 private static ASN1OctetString encodeValue(Set<PersistentSearchChangeType> 184 changeTypes, 185 boolean changesOnly, 186 boolean returnECs) 187 { 188 ArrayList<ASN1Element> elements = 189 new ArrayList<ASN1Element>(3); 190 elements.add(new ASN1Integer( 191 PersistentSearchChangeType.changeTypesToInt(changeTypes))); 192 elements.add(new ASN1Boolean(changesOnly)); 193 elements.add(new ASN1Boolean(returnECs)); 194 195 196 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 197 } 198 199 200 201 /** 202 * Creates a new persistent search control from the contents of the provided 203 * control. 204 * 205 * @param control The generic control containing the information to use to 206 * create this persistent search control. 207 * 208 * @return The persistent search control decoded from the provided control. 209 * 210 * @throws LDAPException If this control cannot be decoded as a valid 211 * persistent search control. 212 */ 213 public static PersistentSearchControl decodeControl(Control control) 214 throws LDAPException 215 { 216 if (! control.hasValue()) 217 { 218 Message message = ERR_PSEARCH_NO_CONTROL_VALUE.get(); 219 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 220 } 221 222 223 boolean changesOnly; 224 boolean returnECs; 225 Set<PersistentSearchChangeType> changeTypes; 226 try 227 { 228 ArrayList<ASN1Element> elements = 229 ASN1Sequence.decodeAsSequence(control.getValue().value()).elements(); 230 if (elements.size() != 3) 231 { 232 Message message = 233 ERR_PSEARCH_INVALID_ELEMENT_COUNT.get(elements.size()); 234 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 235 } 236 237 int changeTypesValue = elements.get(0).decodeAsInteger().intValue(); 238 changeTypes = PersistentSearchChangeType.intToTypes(changeTypesValue); 239 changesOnly = elements.get(1).decodeAsBoolean().booleanValue(); 240 returnECs = elements.get(2).decodeAsBoolean().booleanValue(); 241 } 242 catch (LDAPException le) 243 { 244 throw le; 245 } 246 catch (Exception e) 247 { 248 if (debugEnabled()) 249 { 250 TRACER.debugCaught(DebugLogLevel.ERROR, e); 251 } 252 253 Message message = 254 ERR_PSEARCH_CANNOT_DECODE_VALUE.get(getExceptionMessage(e)); 255 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e); 256 } 257 258 259 return new PersistentSearchControl(control.getOID(), control.isCritical(), 260 changeTypes, changesOnly, returnECs, 261 control.getValue()); 262 } 263 264 265 266 /** 267 * Retrieves the set of change types for this persistent search control. 268 * 269 * @return The set of change types for this persistent search control. 270 */ 271 public Set<PersistentSearchChangeType> getChangeTypes() 272 { 273 return changeTypes; 274 } 275 276 277 278 /** 279 * Specifies the set of change types for this persistent search control. 280 * 281 * @param changeTypes The set of change types for this persistent search 282 * control. 283 */ 284 public void setChangeTypes(Set<PersistentSearchChangeType> changeTypes) 285 { 286 this.changeTypes = changeTypes; 287 288 setValue(encodeValue(changeTypes, changesOnly, returnECs)); 289 } 290 291 292 293 /** 294 * Indicates whether to only return changes that match the associated search 295 * criteria, or to also return all existing entries that match the filter. 296 * 297 * @return <CODE>true</CODE> if only changes to matching entries should be 298 * returned, or <CODE>false</CODE> if existing matches should also be 299 * included. 300 */ 301 public boolean getChangesOnly() 302 { 303 return changesOnly; 304 } 305 306 307 308 /** 309 * Specifies whether to only return changes that match teh associated search 310 * criteria, or to also return all existing entries that match the filter. 311 * 312 * @param changesOnly Indicates whether to only return changes that match 313 * the associated search criteria, or to also return all 314 * existing entries that match the filter. 315 */ 316 public void setChangesOnly(boolean changesOnly) 317 { 318 this.changesOnly = changesOnly; 319 320 setValue(encodeValue(changeTypes, changesOnly, returnECs)); 321 } 322 323 324 325 /** 326 * Indicates whether to include the entry change notification control in 327 * entries returned to the client as the result of a change in the Directory 328 * Server data. 329 * 330 * @return <CODE>true</CODE> if entry change notification controls should be 331 * included in applicable entries, or <CODE>false</CODE> if not. 332 */ 333 public boolean getReturnECs() 334 { 335 return returnECs; 336 } 337 338 339 340 /** 341 * Specifies whether to include the entry change notification control in 342 * entries returned to the client as a result of a change in the Directory 343 * Server data. 344 * 345 * @param returnECs Indicates whether to include the entry change 346 * notification control in updated entries that match the 347 * associated search criteria. 348 */ 349 public void setReturnECs(boolean returnECs) 350 { 351 this.returnECs = returnECs; 352 353 setValue(encodeValue(changeTypes, changesOnly, returnECs)); 354 } 355 356 357 358 /** 359 * Retrieves a string representation of this persistent search control. 360 * 361 * @return A string representation of this persistent search control. 362 */ 363 public String toString() 364 { 365 StringBuilder buffer = new StringBuilder(); 366 toString(buffer); 367 return buffer.toString(); 368 } 369 370 371 372 /** 373 * Appends a string representation of this persistent search control to the 374 * provided buffer. 375 * 376 * @param buffer The buffer to which the information should be appended. 377 */ 378 public void toString(StringBuilder buffer) 379 { 380 buffer.append("PersistentSearchControl(changeTypes=\""); 381 PersistentSearchChangeType.changeTypesToString(changeTypes, buffer); 382 buffer.append("\",changesOnly="); 383 buffer.append(changesOnly); 384 buffer.append(",returnECs="); 385 buffer.append(returnECs); 386 buffer.append(")"); 387 } 388 } 389