001 /*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 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 com.unboundid.asn1.ASN1Element;
026 import com.unboundid.asn1.ASN1OctetString;
027 import com.unboundid.asn1.ASN1Sequence;
028 import com.unboundid.ldap.sdk.Control;
029 import com.unboundid.ldap.sdk.LDAPException;
030 import com.unboundid.ldap.sdk.ResultCode;
031 import com.unboundid.util.NotMutable;
032 import com.unboundid.util.StaticUtils;
033 import com.unboundid.util.ThreadSafety;
034 import com.unboundid.util.ThreadSafetyLevel;
035
036 import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
037 import static com.unboundid.util.Debug.*;
038
039
040
041 /**
042 * This class provides an implementation of the LDAP post-read request control
043 * as defined in <A HREF="http://www.ietf.org/rfc/rfc4527.txt">RFC 4527</A>. It
044 * may be used to request that the server retrieve a copy of the target entry as
045 * it appeared immediately after processing an add, modify, or modify DN
046 * operation.
047 * <BR><BR>
048 * If this control is included in an add, modify, or modify DN request, then the
049 * corresponding response may include a {@link PostReadResponseControl}
050 * containing a version of the entry as it appeared after applying that change.
051 * Note that this response control will only be included if the operation was
052 * successful, so it will not be provided if the operation failed for some
053 * reason (e.g., if the change would have violated the server schema, or if the
054 * requester did not have sufficient permission to perform that operation).
055 * <BR><BR>
056 * The value of this control should contain a set of requested attributes to
057 * include in the entry that is returned. The server should treat this set of
058 * requested attributes exactly as it treats the requested attributes from a
059 * {@link com.unboundid.ldap.sdk.SearchRequest}. As is the case with a search
060 * request, if no attributes are specified, then all user attributes will be
061 * included.
062 * <BR><BR>
063 * <H2>Example</H2>
064 * The following example demonstrates the use of the pre-read and post-read
065 * controls. It will modify an entry to increment the value of the
066 * {@code test-counter} attribute by one, and will use the pre-read and
067 * post-read controls to determine what the previous and updated values are:
068 * <PRE>
069 * // Create a modify request that we can use to increment the value of a
070 * // custom attribute named "test-counter".
071 * ModifyRequest modifyRequest = new ModifyRequest(
072 * "uid=test.user,ou=People,dc=example,dc=com",
073 * new Modification(ModificationType.INCREMENT,
074 * "test-counter", // The attribute to increment.
075 * "1")); // The amount by which to increment the value.
076 *
077 * // Update the modify request to add both pre-read and post-read request
078 * // controls to see what the entry value was before and after the change.
079 * // We only care about getting the test-counter attribute.
080 * modifyRequest.setControls(
081 * new PreReadRequestControl("test-counter"),
082 * new PostReadRequestControl("test-counter"));
083 *
084 * // Process the modify operation in the server.
085 * LDAPResult modifyResult;
086 * try
087 * {
088 * modifyResult = connection.modify(modifyRequest);
089 * // If we got here, then the modification should have been successful.
090 * }
091 * catch (LDAPException le)
092 * {
093 * // This indicates that the operation did not complete successfully.
094 * modifyResult = le.toLDAPResult();
095 * ResultCode resultCode = le.getResultCode();
096 * String errorMessageFromServer = le.getDiagnosticMessage();
097 * }
098 * LDAPTestUtils.assertResultCodeEquals(modifyResult, ResultCode.SUCCESS);
099 *
100 * // Get the pre-read and post-read response controls from the server and
101 * // retrieve the before and after values for the test-counter attribute.
102 * LDAPTestUtils.assertHasControl(modifyResult,
103 * PreReadResponseControl.PRE_READ_RESPONSE_OID);
104 * PreReadResponseControl preReadResponse =
105 * PreReadResponseControl.get(modifyResult);
106 * Integer beforeValue =
107 * preReadResponse.getEntry().getAttributeValueAsInteger("test-counter");
108 *
109 * LDAPTestUtils.assertHasControl(modifyResult,
110 * PostReadResponseControl.POST_READ_RESPONSE_OID);
111 * PostReadResponseControl postReadResponse =
112 * PostReadResponseControl.get(modifyResult);
113 * Integer afterValue =
114 * postReadResponse.getEntry().getAttributeValueAsInteger("test-counter");
115 * </PRE>
116 */
117 @NotMutable()
118 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
119 public final class PostReadRequestControl
120 extends Control
121 {
122 /**
123 * The OID (1.3.6.1.1.13.2) for the post-read request control.
124 */
125 public static final String POST_READ_REQUEST_OID = "1.3.6.1.1.13.2";
126
127
128
129 /**
130 * The set of requested attributes that will be used if none are provided.
131 */
132 private static final String[] NO_ATTRIBUTES = StaticUtils.NO_STRINGS;
133
134
135
136 /**
137 * The serial version UID for this serializable class.
138 */
139 private static final long serialVersionUID = -4210061989410209462L;
140
141
142
143 // The set of requested attributes to retrieve from the target entry.
144 private final String[] attributes;
145
146
147
148 /**
149 * Creates a new post-read request control that will retrieve the specified
150 * set of attributes from the target entry. It will be marked critical.
151 *
152 * @param attributes The set of attributes to retrieve from the target
153 * entry. It behaves in the same way as the set of
154 * requested attributes for a search operation. If this
155 * is empty or {@code null}, then all user attributes
156 * will be returned.
157 */
158 public PostReadRequestControl(final String... attributes)
159 {
160 this(true, attributes);
161 }
162
163
164
165 /**
166 * Creates a new post-read request control that will retrieve the specified
167 * set of attributes from the target entry.
168 *
169 * @param isCritical Indicates whether this control should be marked
170 * critical.
171 * @param attributes The set of attributes to retrieve from the target
172 * entry. It behaves in the same way as the set of
173 * requested attributes for a search operation. If this
174 * is empty or {@code null}, then all user attributes
175 * will be returned.
176 */
177 public PostReadRequestControl(final boolean isCritical,
178 final String... attributes)
179 {
180 super(POST_READ_REQUEST_OID, isCritical, encodeValue(attributes));
181
182 if (attributes == null)
183 {
184 this.attributes = NO_ATTRIBUTES;
185 }
186 else
187 {
188 this.attributes = attributes;
189 }
190 }
191
192
193
194 /**
195 * Creates a new post-read request control which is decoded from the provided
196 * generic control.
197 *
198 * @param control The generic control to be decoded as a post-read request
199 * control.
200 *
201 * @throws LDAPException If the provided control cannot be decoded as a
202 * post-read request control.
203 */
204 public PostReadRequestControl(final Control control)
205 throws LDAPException
206 {
207 super(control);
208
209 final ASN1OctetString value = control.getValue();
210 if (value == null)
211 {
212 throw new LDAPException(ResultCode.DECODING_ERROR,
213 ERR_POST_READ_REQUEST_NO_VALUE.get());
214 }
215
216 try
217 {
218 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
219 final ASN1Element[] attrElements =
220 ASN1Sequence.decodeAsSequence(valueElement).elements();
221 attributes = new String[attrElements.length];
222 for (int i=0; i < attrElements.length; i++)
223 {
224 attributes[i] =
225 ASN1OctetString.decodeAsOctetString(attrElements[i]).stringValue();
226 }
227 }
228 catch (Exception e)
229 {
230 debugException(e);
231 throw new LDAPException(ResultCode.DECODING_ERROR,
232 ERR_POST_READ_REQUEST_CANNOT_DECODE.get(e), e);
233 }
234 }
235
236
237
238 /**
239 * Encodes the provided information into an octet string that can be used as
240 * the value for this control.
241 *
242 * @param attributes The set of attributes to retrieve from the target
243 * entry. It behaves in the same way as the set of
244 * requested attributes for a search operation. If this
245 * is empty or {@code null}, then all user attributes
246 * will be returned.
247 *
248 * @return An ASN.1 octet string that can be used as the value for this
249 * control.
250 */
251 private static ASN1OctetString encodeValue(final String[] attributes)
252 {
253 if ((attributes == null) || (attributes.length == 0))
254 {
255 return new ASN1OctetString(new ASN1Sequence().encode());
256 }
257
258 final ASN1OctetString[] elements = new ASN1OctetString[attributes.length];
259 for (int i=0; i < attributes.length; i++)
260 {
261 elements[i] = new ASN1OctetString(attributes[i]);
262 }
263
264 return new ASN1OctetString(new ASN1Sequence(elements).encode());
265 }
266
267
268
269 /**
270 * Retrieves the set of attributes that will be requested for inclusion in the
271 * entry returned in the response control.
272 *
273 * @return The set of attributes that will be requested for inclusion in the
274 * entry returned in the response control, or an empty array if all
275 * user attributes should be returned.
276 */
277 public String[] getAttributes()
278 {
279 return attributes;
280 }
281
282
283
284 /**
285 * {@inheritDoc}
286 */
287 @Override()
288 public String getControlName()
289 {
290 return INFO_CONTROL_NAME_POST_READ_REQUEST.get();
291 }
292
293
294
295 /**
296 * {@inheritDoc}
297 */
298 @Override()
299 public void toString(final StringBuilder buffer)
300 {
301 buffer.append("PostReadRequestControl(attributes={");
302 for (int i=0; i < attributes.length; i++)
303 {
304 if (i > 0)
305 {
306 buffer.append(", ");
307 }
308 buffer.append('\'');
309 buffer.append(attributes[i]);
310 buffer.append('\'');
311 }
312 buffer.append("}, isCritical=");
313 buffer.append(isCritical());
314 buffer.append(')');
315 }
316 }