001 /*
002 * Copyright 2007-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2013 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk.controls;
022
023
024
025 import java.util.ArrayList;
026 import java.util.Collection;
027
028 import com.unboundid.asn1.ASN1Element;
029 import com.unboundid.asn1.ASN1OctetString;
030 import com.unboundid.ldap.sdk.Attribute;
031 import com.unboundid.ldap.sdk.Control;
032 import com.unboundid.ldap.sdk.Entry;
033 import com.unboundid.ldap.sdk.Filter;
034 import com.unboundid.ldap.sdk.LDAPException;
035 import com.unboundid.ldap.sdk.ResultCode;
036 import com.unboundid.util.NotMutable;
037 import com.unboundid.util.ThreadSafety;
038 import com.unboundid.util.ThreadSafetyLevel;
039 import com.unboundid.util.Validator;
040
041 import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
042 import static com.unboundid.util.Debug.*;
043
044
045
046 /**
047 * This class provides an implementation of the LDAP assertion request control
048 * as defined in <A HREF="http://www.ietf.org/rfc/rfc4528.txt">RFC 4528</A>. It
049 * may be used in conjunction with an add, compare, delete, modify, modify DN,
050 * or search operation. The assertion control includes a search filter, and the
051 * associated operation should only be allowed to continue if the target entry
052 * matches the provided filter. If the filter does not match the target entry,
053 * then the operation should fail with an
054 * {@link ResultCode#ASSERTION_FAILED} result.
055 * <BR><BR>
056 * The behavior of the assertion request control makes it ideal for atomic
057 * "check and set" types of operations, particularly when modifying an entry.
058 * For example, it can be used to ensure that when changing the value of an
059 * attribute, the current value has not been modified since it was last
060 * retrieved.
061 * <BR><BR>
062 * <H2>Example</H2>
063 * The following example demonstrates the use of the assertion request control.
064 * It shows an attempt to modify an entry's "accountBalance" attribute to set
065 * the value to "543.21" only if the current value is "1234.56":
066 * <PRE>
067 * Modification mod = new Modification(ModificationType.REPLACE,
068 * "accountBalance", "543.21");
069 * ModifyRequest modifyRequest =
070 * new ModifyRequest("uid=john.doe,ou=People,dc=example,dc=com", mod);
071 * modifyRequest.addControl(
072 * new AssertionRequestControl("(accountBalance=1234.56)"));
073 *
074 * try
075 * {
076 * LDAPResult modifyResult = connection.modify(modifyRequest);
077 * // If we've gotten here, then the modification was successful.
078 * }
079 * catch (LDAPException le)
080 * {
081 * if (le.getResultCode() == ResultCode.ASSERTION_FAILED)
082 * {
083 * The modification failed because the accountBalance value wasn't what
084 * we thought it was.
085 * }
086 * else
087 * {
088 * The modification failed for some other reason.
089 * }
090 * }
091 * </PRE>
092 */
093 @NotMutable()
094 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
095 public final class AssertionRequestControl
096 extends Control
097 {
098 /**
099 * The OID (1.3.6.1.1.12) for the assertion request control.
100 */
101 public static final String ASSERTION_REQUEST_OID = "1.3.6.1.1.12";
102
103
104
105 /**
106 * The serial version UID for this serializable class.
107 */
108 private static final long serialVersionUID = 6592634203410511095L;
109
110
111
112 // The search filter for this assertion request control.
113 private final Filter filter;
114
115
116
117 /**
118 * Creates a new assertion request control with the provided filter. It will
119 * be marked as critical.
120 *
121 * @param filter The string representation of the filter for this assertion
122 * control. It must not be {@code null}.
123 *
124 * @throws LDAPException If the provided filter string cannot be decoded as
125 * a search filter.
126 */
127 public AssertionRequestControl(final String filter)
128 throws LDAPException
129 {
130 this(Filter.create(filter), true);
131 }
132
133
134
135 /**
136 * Creates a new assertion request control with the provided filter. It will
137 * be marked as critical.
138 *
139 * @param filter The filter for this assertion control. It must not be
140 * {@code null}.
141 */
142 public AssertionRequestControl(final Filter filter)
143 {
144 this(filter, true);
145 }
146
147
148
149 /**
150 * Creates a new assertion request control with the provided filter. It will
151 * be marked as critical.
152 *
153 * @param filter The string representation of the filter for this
154 * assertion control. It must not be {@code null}.
155 * @param isCritical Indicates whether this control should be marked
156 * critical.
157 *
158 * @throws LDAPException If the provided filter string cannot be decoded as
159 * a search filter.
160 */
161 public AssertionRequestControl(final String filter, final boolean isCritical)
162 throws LDAPException
163 {
164 this(Filter.create(filter), isCritical);
165 }
166
167
168
169 /**
170 * Creates a new assertion request control with the provided filter. It will
171 * be marked as critical.
172 *
173 * @param filter The filter for this assertion control. It must not be
174 * {@code null}.
175 * @param isCritical Indicates whether this control should be marked
176 * critical.
177 */
178 public AssertionRequestControl(final Filter filter, final boolean isCritical)
179 {
180 super(ASSERTION_REQUEST_OID, isCritical, encodeValue(filter));
181
182 this.filter = filter;
183 }
184
185
186
187 /**
188 * Creates a new assertion request control which is decoded from the provided
189 * generic control.
190 *
191 * @param control The generic control to be decoded as an assertion request
192 * control.
193 *
194 * @throws LDAPException If the provided control cannot be decoded as an
195 * assertion request control.
196 */
197 public AssertionRequestControl(final Control control)
198 throws LDAPException
199 {
200 super(control);
201
202 final ASN1OctetString value = control.getValue();
203 if (value == null)
204 {
205 throw new LDAPException(ResultCode.DECODING_ERROR,
206 ERR_ASSERT_NO_VALUE.get());
207 }
208
209
210 try
211 {
212 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
213 filter = Filter.decode(valueElement);
214 }
215 catch (Exception e)
216 {
217 debugException(e);
218 throw new LDAPException(ResultCode.DECODING_ERROR,
219 ERR_ASSERT_CANNOT_DECODE.get(e), e);
220 }
221 }
222
223
224
225 /**
226 * Generates an assertion request control that may be used to help ensure
227 * that some or all of the attributes in the specified entry have not changed
228 * since it was read from the server.
229 *
230 * @param sourceEntry The entry from which to take the attributes to include
231 * in the assertion request control. It must not be
232 * {@code null} and should have at least one attribute to
233 * be included in the generated filter.
234 * @param attributes The names of the attributes to include in the
235 * assertion request control. If this is empty or
236 * {@code null}, then all attributes in the provided
237 * entry will be used.
238 *
239 * @return The generated assertion request control.
240 */
241 public static AssertionRequestControl generate(final Entry sourceEntry,
242 final String... attributes)
243 {
244 Validator.ensureNotNull(sourceEntry);
245
246 final ArrayList<Filter> andComponents;
247
248 if ((attributes == null) || (attributes.length == 0))
249 {
250 final Collection<Attribute> entryAttrs = sourceEntry.getAttributes();
251 andComponents = new ArrayList<Filter>(entryAttrs.size());
252 for (final Attribute a : entryAttrs)
253 {
254 for (final ASN1OctetString v : a.getRawValues())
255 {
256 andComponents.add(Filter.createEqualityFilter(a.getName(),
257 v.getValue()));
258 }
259 }
260 }
261 else
262 {
263 andComponents = new ArrayList<Filter>(attributes.length);
264 for (final String name : attributes)
265 {
266 final Attribute a = sourceEntry.getAttribute(name);
267 if (a != null)
268 {
269 for (final ASN1OctetString v : a.getRawValues())
270 {
271 andComponents.add(Filter.createEqualityFilter(name, v.getValue()));
272 }
273 }
274 }
275 }
276
277 if (andComponents.size() == 1)
278 {
279 return new AssertionRequestControl(andComponents.get(0));
280 }
281 else
282 {
283 return new AssertionRequestControl(Filter.createANDFilter(andComponents));
284 }
285 }
286
287
288
289 /**
290 * Encodes the provided information into an octet string that can be used as
291 * the value for this control.
292 *
293 * @param filter The filter for this assertion control. It must not be
294 * {@code null}.
295 *
296 * @return An ASN.1 octet string that can be used as the value for this
297 * control.
298 */
299 private static ASN1OctetString encodeValue(final Filter filter)
300 {
301 return new ASN1OctetString(filter.encode().encode());
302 }
303
304
305
306 /**
307 * Retrieves the filter for this assertion control.
308 *
309 * @return The filter for this assertion control.
310 */
311 public Filter getFilter()
312 {
313 return filter;
314 }
315
316
317
318 /**
319 * {@inheritDoc}
320 */
321 @Override()
322 public String getControlName()
323 {
324 return INFO_CONTROL_NAME_ASSERTION_REQUEST.get();
325 }
326
327
328
329 /**
330 * {@inheritDoc}
331 */
332 @Override()
333 public void toString(final StringBuilder buffer)
334 {
335 buffer.append("AssertionRequestControl(filter='");
336 filter.toString(buffer);
337 buffer.append("', isCritical=");
338 buffer.append(isCritical());
339 buffer.append(')');
340 }
341 }