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    }