001/*
002 * Copyright 2012-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2019 Ping Identity Corporation
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.unboundidds.controls;
022
023
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.EnumSet;
030import java.util.Iterator;
031import java.util.Set;
032
033import com.unboundid.asn1.ASN1Element;
034import com.unboundid.asn1.ASN1Enumerated;
035import com.unboundid.asn1.ASN1OctetString;
036import com.unboundid.asn1.ASN1Sequence;
037import com.unboundid.ldap.sdk.Control;
038import com.unboundid.ldap.sdk.LDAPException;
039import com.unboundid.ldap.sdk.ResultCode;
040import com.unboundid.util.Debug;
041import com.unboundid.util.NotMutable;
042import com.unboundid.util.StaticUtils;
043import com.unboundid.util.ThreadSafety;
044import com.unboundid.util.ThreadSafetyLevel;
045import com.unboundid.util.Validator;
046
047import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
048
049
050
051/**
052 * This class provides an implementation of a control that can be used to
053 * indicate that the server should suppress the update to one or more
054 * operational attributes for the associated request.
055 * <BR>
056 * <BLOCKQUOTE>
057 *   <B>NOTE:</B>  This class, and other classes within the
058 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
059 *   supported for use against Ping Identity, UnboundID, and
060 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
061 *   for proprietary functionality or for external specifications that are not
062 *   considered stable or mature enough to be guaranteed to work in an
063 *   interoperable way with other types of LDAP servers.
064 * </BLOCKQUOTE>
065 * <BR>
066 * The request control has an OID of 1.3.6.1.4.1.30221.2.5.27, and the
067 * criticality may be either {@code true} or {@code false}.  The control must
068 * have a value with the following encoding:
069 * <PRE>
070 *   SuppressOperationalAttributeUpdateRequestValue ::= SEQUENCE {
071 *        suppressTypes     [0] SEQUENCE OF ENUMERATED {
072 *             last-access-time     (0),
073 *             last-login-time      (1),
074 *             last-login-ip        (2),
075 *             lastmod              (3),
076 *             ... },
077 *        ... }
078 * </PRE>
079 */
080@NotMutable()
081@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
082public final class SuppressOperationalAttributeUpdateRequestControl
083       extends Control
084{
085  /**
086   * The OID (1.3.6.1.4.1.30221.2.5.27) for the suppress operational attribute
087   * update request control.
088   */
089  public static final  String SUPPRESS_OP_ATTR_UPDATE_REQUEST_OID =
090       "1.3.6.1.4.1.30221.2.5.27";
091
092
093
094  /**
095   * The BER type to use for the set of suppress types.
096   */
097  private static final byte TYPE_SUPPRESS_TYPES = (byte) 0x80;
098
099
100  /**
101   * The serial version UID for this serializable class.
102   */
103  private static final long serialVersionUID = 4603958484615351672L;
104
105
106
107  // The set of suppress types to include in the control.
108  private final Set<SuppressType> suppressTypes;
109
110
111
112  /**
113   * Creates a new instance of this control that will suppress updates to the
114   * specified kinds of operational attributes.  It will not be critical.
115   *
116   * @param  suppressTypes  The set of suppress types to include in the control.
117   *                        It must not be {@code null} or empty.
118   */
119  public SuppressOperationalAttributeUpdateRequestControl(
120              final SuppressType... suppressTypes)
121  {
122    this(false, suppressTypes);
123  }
124
125
126
127  /**
128   * Creates a new instance of this control that will suppress updates to the
129   * specified kinds of operational attributes.  It will not be critical.
130   *
131   * @param  suppressTypes  The set of suppress types to include in the control.
132   *                        It must not be {@code null} or empty.
133   */
134  public SuppressOperationalAttributeUpdateRequestControl(
135              final Collection<SuppressType> suppressTypes)
136  {
137    this(false, suppressTypes);
138  }
139
140
141
142  /**
143   * Creates a new instance of this control that will suppress updates to the
144   * specified kinds of operational attributes.
145   *
146   * @param  isCritical     Indicates whether the control should be considered
147   *                        critical.
148   * @param  suppressTypes  The set of suppress types to include in the control.
149   *                        It must not be {@code null} or empty.
150   */
151  public SuppressOperationalAttributeUpdateRequestControl(
152              final boolean isCritical, final SuppressType... suppressTypes)
153  {
154    this(isCritical, Arrays.asList(suppressTypes));
155  }
156
157
158
159  /**
160   * Creates a new instance of this control that will suppress updates to the
161   * specified kinds of operational attributes.
162   *
163   * @param  isCritical     Indicates whether the control should be considered
164   *                        critical.
165   * @param  suppressTypes  The set of suppress types to include in the control.
166   *                        It must not be {@code null} or empty.
167   */
168  public SuppressOperationalAttributeUpdateRequestControl(
169              final boolean isCritical,
170              final Collection<SuppressType> suppressTypes)
171  {
172    super(SUPPRESS_OP_ATTR_UPDATE_REQUEST_OID, isCritical,
173         encodeValue(suppressTypes));
174
175    Validator.ensureFalse(suppressTypes.isEmpty());
176
177    final EnumSet<SuppressType> s = EnumSet.noneOf(SuppressType.class);
178    for (final SuppressType t : suppressTypes)
179    {
180      s.add(t);
181    }
182
183    this.suppressTypes = Collections.unmodifiableSet(s);
184  }
185
186
187
188  /**
189   * Decodes the provided generic control as a suppress operational attribute
190   * update request control.
191   *
192   * @param  control  The generic control to be decoded as a suppress
193   *                  operational attribute update request control.
194   *
195   * @throws  LDAPException  If a problem is encountered while attempting to
196   *                         decode the provided control.
197   */
198  public SuppressOperationalAttributeUpdateRequestControl(final Control control)
199         throws LDAPException
200  {
201    super(control);
202
203    final ASN1OctetString value = control.getValue();
204    if (value == null)
205    {
206      throw new LDAPException(ResultCode.DECODING_ERROR,
207           ERR_SUPPRESS_OP_ATTR_UPDATE_REQUEST_MISSING_VALUE.get());
208    }
209
210    try
211    {
212      final ASN1Sequence valueSequence =
213           ASN1Sequence.decodeAsSequence(value.getValue());
214      final ASN1Sequence suppressTypesSequence =
215           ASN1Sequence.decodeAsSequence(valueSequence.elements()[0]);
216
217      final EnumSet<SuppressType> s = EnumSet.noneOf(SuppressType.class);
218      for (final ASN1Element e : suppressTypesSequence.elements())
219      {
220        final ASN1Enumerated ae = ASN1Enumerated.decodeAsEnumerated(e);
221        final SuppressType t = SuppressType.valueOf(ae.intValue());
222        if (t == null)
223        {
224          throw new LDAPException(ResultCode.DECODING_ERROR,
225               ERR_SUPPRESS_OP_ATTR_UNRECOGNIZED_SUPPRESS_TYPE.get(
226                    ae.intValue()));
227        }
228        else
229        {
230          s.add(t);
231        }
232      }
233
234      suppressTypes = Collections.unmodifiableSet(s);
235    }
236    catch (final LDAPException le)
237    {
238      Debug.debugException(le);
239      throw le;
240    }
241    catch (final Exception e)
242    {
243      Debug.debugException(e);
244      throw new LDAPException(ResultCode.DECODING_ERROR,
245           ERR_SUPPRESS_OP_ATTR_UPDATE_REQUEST_CANNOT_DECODE.get(
246                StaticUtils.getExceptionMessage(e)),
247           e);
248    }
249  }
250
251
252
253  /**
254   * Encodes the provided information into an octet string suitable for use as
255   * the value of this control.
256   *
257   * @param  suppressTypes  The set of suppress types to include in the control.
258   *                        It must not be {@code null} or empty.
259   *
260   * @return  The ASN.1 octet string containing the encoded value.
261   */
262  private static ASN1OctetString encodeValue(
263                      final Collection<SuppressType> suppressTypes)
264  {
265    final ArrayList<ASN1Element> suppressTypeElements =
266         new ArrayList<>(suppressTypes.size());
267    for (final SuppressType t : suppressTypes)
268    {
269      suppressTypeElements.add(new ASN1Enumerated(t.intValue()));
270    }
271
272    final ASN1Sequence valueSequence = new ASN1Sequence(
273         new ASN1Sequence(TYPE_SUPPRESS_TYPES, suppressTypeElements));
274    return new ASN1OctetString(valueSequence.encode());
275  }
276
277
278
279  /**
280   * Retrieves the set of suppress types for this control.
281   *
282   * @return  The set of suppress types for this control.
283   */
284  public Set<SuppressType> getSuppressTypes()
285  {
286    return suppressTypes;
287  }
288
289
290
291  /**
292   * {@inheritDoc}
293   */
294  @Override()
295  public String getControlName()
296  {
297    return INFO_CONTROL_NAME_SUPPRESS_OP_ATTR_UPDATE_REQUEST.get();
298  }
299
300
301
302  /**
303   * {@inheritDoc}
304   */
305  @Override()
306  public void toString(final StringBuilder buffer)
307  {
308    buffer.append("SuppressOperationalAttributeUpdateRequestControl(" +
309         "isCritical=");
310    buffer.append(isCritical());
311    buffer.append(", suppressTypes={");
312
313    final Iterator<SuppressType> iterator = suppressTypes.iterator();
314    while (iterator.hasNext())
315    {
316      buffer.append(iterator.next().name());
317      if (iterator.hasNext())
318      {
319        buffer.append(',');
320      }
321    }
322
323    buffer.append("})");
324  }
325}