001    /*
002     * Copyright 2007-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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 pre-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 before processing a delete, modify, or modify DN
046     * operation.
047     * <BR><BR>
048     * If this control is included in a delete, modify, or modify DN request, then
049     * the corresponding response may include a {@link PreReadResponseControl}
050     * containing a version of the entry as it before 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     * The use of the LDAP pre-read request control is virtually identical to the
064     * use of the LDAP post-read request control.  See the documentation for the
065     * {@link PostReadRequestControl} for an example that illustrates its use.
066     */
067    @NotMutable()
068    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
069    public final class PreReadRequestControl
070           extends Control
071    {
072      /**
073       * The OID (1.3.6.1.1.13.1) for the pre-read request control.
074       */
075      public static final String PRE_READ_REQUEST_OID = "1.3.6.1.1.13.1";
076    
077    
078    
079      /**
080       * The set of requested attributes that will be used if none are provided.
081       */
082      private static final String[] NO_ATTRIBUTES = StaticUtils.NO_STRINGS;
083    
084    
085    
086      /**
087       * The serial version UID for this serializable class.
088       */
089      private static final long serialVersionUID = 1205235290978028739L;
090    
091    
092    
093      // The set of requested attributes to retrieve from the target entry.
094      private final String[] attributes;
095    
096    
097    
098      /**
099       * Creates a new pre-read request control that will retrieve the specified set
100       * of attributes from the target entry.  It will be marked critical.
101       *
102       * @param  attributes  The set of attributes to retrieve from the target
103       *                     entry.  It behaves in the same way as the set of
104       *                     requested attributes for a search operation.  If this
105       *                     is empty or {@code null}, then all user attributes
106       *                     will be returned.
107       */
108      public PreReadRequestControl(final String... attributes)
109      {
110        this(true, attributes);
111      }
112    
113    
114    
115      /**
116       * Creates a new pre-read request control that will retrieve the specified set
117       * of attributes from the target entry.
118       *
119       * @param  isCritical  Indicates whether this control should be marked
120       *                     critical.
121       * @param  attributes  The set of attributes to retrieve from the target
122       *                     entry.  It behaves in the same way as the set of
123       *                     requested attributes for a search operation.  If this
124       *                     is empty or {@code null}, then all user attributes
125       *                     will be returned.
126       */
127      public PreReadRequestControl(final boolean isCritical,
128                                   final String... attributes)
129      {
130        super(PRE_READ_REQUEST_OID, isCritical, encodeValue(attributes));
131    
132        if (attributes == null)
133        {
134          this.attributes = NO_ATTRIBUTES;
135        }
136        else
137        {
138          this.attributes = attributes;
139        }
140      }
141    
142    
143    
144      /**
145       * Creates a new pre-read request control which is decoded from the provided
146       * generic control.
147       *
148       * @param  control  The generic control to be decoded as a pre-read request
149       *                  control.
150       *
151       * @throws  LDAPException  If the provided control cannot be decoded as a
152       *                         pre-read request control.
153       */
154      public PreReadRequestControl(final Control control)
155             throws LDAPException
156      {
157        super(control);
158    
159        final ASN1OctetString value = control.getValue();
160        if (value == null)
161        {
162          throw new LDAPException(ResultCode.DECODING_ERROR,
163                                  ERR_PRE_READ_REQUEST_NO_VALUE.get());
164        }
165    
166        try
167        {
168          final ASN1Element valueElement = ASN1Element.decode(value.getValue());
169          final ASN1Element[] attrElements =
170               ASN1Sequence.decodeAsSequence(valueElement).elements();
171          attributes = new String[attrElements.length];
172          for (int i=0; i < attrElements.length; i++)
173          {
174            attributes[i] =
175                 ASN1OctetString.decodeAsOctetString(attrElements[i]).stringValue();
176          }
177        }
178        catch (Exception e)
179        {
180          debugException(e);
181          throw new LDAPException(ResultCode.DECODING_ERROR,
182                                  ERR_PRE_READ_REQUEST_CANNOT_DECODE.get(e), e);
183        }
184      }
185    
186    
187    
188      /**
189       * Encodes the provided information into an octet string that can be used as
190       * the value for this control.
191       *
192       * @param  attributes  The set of attributes to retrieve from the target
193       *                     entry.  It behaves in the same way as the set of
194       *                     requested attributes for a search operation.  If this
195       *                     is empty or {@code null}, then all user attributes
196       *                     will be returned.
197       *
198       * @return  An ASN.1 octet string that can be used as the value for this
199       *          control.
200       */
201      private static ASN1OctetString encodeValue(final String[] attributes)
202      {
203        if ((attributes == null) || (attributes.length == 0))
204        {
205          return new ASN1OctetString(new ASN1Sequence().encode());
206        }
207    
208        final ASN1OctetString[] elements = new ASN1OctetString[attributes.length];
209        for (int i=0; i < attributes.length; i++)
210        {
211          elements[i] = new ASN1OctetString(attributes[i]);
212        }
213    
214        return new ASN1OctetString(new ASN1Sequence(elements).encode());
215      }
216    
217    
218    
219      /**
220       * Retrieves the set of attributes that will be requested for inclusion in the
221       * entry returned in the response control.
222       *
223       * @return  The set of attributes that will be requested for inclusion in the
224       *          entry returned in the response control, or an empty array if all
225       *          user attributes should be returned.
226       */
227      public String[] getAttributes()
228      {
229        return attributes;
230      }
231    
232    
233    
234      /**
235       * {@inheritDoc}
236       */
237      @Override()
238      public String getControlName()
239      {
240        return INFO_CONTROL_NAME_PRE_READ_REQUEST.get();
241      }
242    
243    
244    
245      /**
246       * {@inheritDoc}
247       */
248      @Override()
249      public void toString(final StringBuilder buffer)
250      {
251        buffer.append("PreReadRequestControl(attributes={");
252        for (int i=0; i < attributes.length; i++)
253        {
254          if (i > 0)
255          {
256            buffer.append(", ");
257          }
258          buffer.append('\'');
259          buffer.append(attributes[i]);
260          buffer.append('\'');
261        }
262        buffer.append("}, isCritical=");
263        buffer.append(isCritical());
264        buffer.append(')');
265      }
266    }