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