001/*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2014 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 */
021package com.unboundid.ldap.sdk.controls;
022
023
024
025import com.unboundid.asn1.ASN1Element;
026import com.unboundid.asn1.ASN1OctetString;
027import com.unboundid.asn1.ASN1Sequence;
028import com.unboundid.ldap.sdk.Control;
029import com.unboundid.ldap.sdk.LDAPException;
030import com.unboundid.ldap.sdk.ResultCode;
031import com.unboundid.util.NotMutable;
032import com.unboundid.util.StaticUtils;
033import com.unboundid.util.ThreadSafety;
034import com.unboundid.util.ThreadSafetyLevel;
035
036import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
037import 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)
119public 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}