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 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 post-read controls. It
065 * will modify an entry to increment the value of the {@code testCounter}
066 * attribute by one, and will use the post-read controls to determine what the
067 * new value is:
068 * <PRE>
069 * Modification mod =
070 * new Modification(ModificationType.INCREMENT, "testCounter", "1");
071 * ModifyRequest modifyRequest =
072 * new ModifyRequest("uid=john.doe,ou=People,dc=example,dc=com", mod);
073 * modifyRequest.addControl(new PostReadRequestControl("testCounter"));
074 * LDAPResult modifyResult = connection.modify(modifyRequest);
075 *
076 * Integer newValue = null;
077 * PostReadResponseControl c = PostReadResponseControl.get(modifyResult);
078 * if (c != null)
079 * {
080 * newValue = c.getEntry().getAttributeValueAsInteger("testCounter");
081 * }
082 * </PRE>
083 */
084 @NotMutable()
085 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
086 public final class PostReadRequestControl
087 extends Control
088 {
089 /**
090 * The OID (1.3.6.1.1.13.2) for the post-read request control.
091 */
092 public static final String POST_READ_REQUEST_OID = "1.3.6.1.1.13.2";
093
094
095
096 /**
097 * The set of requested attributes that will be used if none are provided.
098 */
099 private static final String[] NO_ATTRIBUTES = StaticUtils.NO_STRINGS;
100
101
102
103 /**
104 * The serial version UID for this serializable class.
105 */
106 private static final long serialVersionUID = -4210061989410209462L;
107
108
109
110 // The set of requested attributes to retrieve from the target entry.
111 private final String[] attributes;
112
113
114
115 /**
116 * Creates a new post-read request control that will retrieve the specified
117 * set of attributes from the target entry. It will be marked critical.
118 *
119 * @param attributes The set of attributes to retrieve from the target
120 * entry. It behaves in the same way as the set of
121 * requested attributes for a search operation. If this
122 * is empty or {@code null}, then all user attributes
123 * will be returned.
124 */
125 public PostReadRequestControl(final String... attributes)
126 {
127 this(true, attributes);
128 }
129
130
131
132 /**
133 * Creates a new post-read request control that will retrieve the specified
134 * set of attributes from the target entry.
135 *
136 * @param isCritical Indicates whether this control should be marked
137 * critical.
138 * @param attributes The set of attributes to retrieve from the target
139 * entry. It behaves in the same way as the set of
140 * requested attributes for a search operation. If this
141 * is empty or {@code null}, then all user attributes
142 * will be returned.
143 */
144 public PostReadRequestControl(final boolean isCritical,
145 final String... attributes)
146 {
147 super(POST_READ_REQUEST_OID, isCritical, encodeValue(attributes));
148
149 if (attributes == null)
150 {
151 this.attributes = NO_ATTRIBUTES;
152 }
153 else
154 {
155 this.attributes = attributes;
156 }
157 }
158
159
160
161 /**
162 * Creates a new post-read request control which is decoded from the provided
163 * generic control.
164 *
165 * @param control The generic control to be decoded as a post-read request
166 * control.
167 *
168 * @throws LDAPException If the provided control cannot be decoded as a
169 * post-read request control.
170 */
171 public PostReadRequestControl(final Control control)
172 throws LDAPException
173 {
174 super(control);
175
176 final ASN1OctetString value = control.getValue();
177 if (value == null)
178 {
179 throw new LDAPException(ResultCode.DECODING_ERROR,
180 ERR_POST_READ_REQUEST_NO_VALUE.get());
181 }
182
183 try
184 {
185 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
186 final ASN1Element[] attrElements =
187 ASN1Sequence.decodeAsSequence(valueElement).elements();
188 attributes = new String[attrElements.length];
189 for (int i=0; i < attrElements.length; i++)
190 {
191 attributes[i] =
192 ASN1OctetString.decodeAsOctetString(attrElements[i]).stringValue();
193 }
194 }
195 catch (Exception e)
196 {
197 debugException(e);
198 throw new LDAPException(ResultCode.DECODING_ERROR,
199 ERR_POST_READ_REQUEST_CANNOT_DECODE.get(e), e);
200 }
201 }
202
203
204
205 /**
206 * Encodes the provided information into an octet string that can be used as
207 * the value for this control.
208 *
209 * @param attributes The set of attributes to retrieve from the target
210 * entry. It behaves in the same way as the set of
211 * requested attributes for a search operation. If this
212 * is empty or {@code null}, then all user attributes
213 * will be returned.
214 *
215 * @return An ASN.1 octet string that can be used as the value for this
216 * control.
217 */
218 private static ASN1OctetString encodeValue(final String[] attributes)
219 {
220 if ((attributes == null) || (attributes.length == 0))
221 {
222 return new ASN1OctetString(new ASN1Sequence().encode());
223 }
224
225 final ASN1OctetString[] elements = new ASN1OctetString[attributes.length];
226 for (int i=0; i < attributes.length; i++)
227 {
228 elements[i] = new ASN1OctetString(attributes[i]);
229 }
230
231 return new ASN1OctetString(new ASN1Sequence(elements).encode());
232 }
233
234
235
236 /**
237 * Retrieves the set of attributes that will be requested for inclusion in the
238 * entry returned in the response control.
239 *
240 * @return The set of attributes that will be requested for inclusion in the
241 * entry returned in the response control, or an empty array if all
242 * user attributes should be returned.
243 */
244 public String[] getAttributes()
245 {
246 return attributes;
247 }
248
249
250
251 /**
252 * {@inheritDoc}
253 */
254 @Override()
255 public String getControlName()
256 {
257 return INFO_CONTROL_NAME_POST_READ_REQUEST.get();
258 }
259
260
261
262 /**
263 * {@inheritDoc}
264 */
265 @Override()
266 public void toString(final StringBuilder buffer)
267 {
268 buffer.append("PostReadRequestControl(attributes={");
269 for (int i=0; i < attributes.length; i++)
270 {
271 if (i > 0)
272 {
273 buffer.append(", ");
274 }
275 buffer.append('\'');
276 buffer.append(attributes[i]);
277 buffer.append('\'');
278 }
279 buffer.append("}, isCritical=");
280 buffer.append(isCritical());
281 buffer.append(')');
282 }
283 }