001/*
002 * Copyright 2011-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2018 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.extensions;
022
023
024
025import java.util.ArrayList;
026
027import com.unboundid.asn1.ASN1Boolean;
028import com.unboundid.asn1.ASN1Element;
029import com.unboundid.asn1.ASN1OctetString;
030import com.unboundid.asn1.ASN1Sequence;
031import com.unboundid.ldap.sdk.Control;
032import com.unboundid.ldap.sdk.ExtendedRequest;
033import com.unboundid.ldap.sdk.LDAPException;
034import com.unboundid.ldap.sdk.ResultCode;
035import com.unboundid.util.Debug;
036import com.unboundid.util.NotMutable;
037import com.unboundid.util.StaticUtils;
038import com.unboundid.util.ThreadSafety;
039import com.unboundid.util.ThreadSafetyLevel;
040
041import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
042
043
044
045/**
046 * This class provides an implementation of the start administrative session
047 * extended request, which clients may use to indicate that they are going to
048 * perform a set of administrative operations in the server.  It may be used
049 * to identify the client to the server and to indicate whether subsequent
050 * requests received on the connection should be processed using worker threads
051 * in a dedicated thread pool (subject to server configuration restrictions).
052 * <BR>
053 * <BLOCKQUOTE>
054 *   <B>NOTE:</B>  This class, and other classes within the
055 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
056 *   supported for use against Ping Identity, UnboundID, and
057 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
058 *   for proprietary functionality or for external specifications that are not
059 *   considered stable or mature enough to be guaranteed to work in an
060 *   interoperable way with other types of LDAP servers.
061 * </BLOCKQUOTE>
062 * <BR>
063 * This extended request has an OID of 1.3.6.1.4.1.30221.2.6.13, and it must
064 * have a value with the following encoding:
065 * <PRE>
066 *   StartAdminSessionValue ::= SEQUENCE {
067 *        clientName                 [0] OCTET STRING OPTIONAL,
068 *        useDedicatedThreadPool     [1] BOOLEAN DEFAULT FALSE,
069 *        ... }
070 * </PRE>
071 * <BR><BR>
072 * <H2>Example</H2>
073 * The following example demonstrates the process for creating an administrative
074 * session and using that session to request monitor information using a
075 * dedicated worker thread.
076 * <PRE>
077 * // Establish a connection to the server.
078 * LDAPConnection connection = new LDAPConnection(host, port);
079 *
080 * // Use the start administrative session operation to begin an administrative
081 * // session and request that operations in the session use the dedicated
082 * // thread pool.
083 * ExtendedResult extendedResult = connection.processExtendedOperation(
084 *      new StartAdministrativeSessionExtendedRequest("Test Client", true));
085 *
086 * // Authenticate the connection.  It is strongly recommended that the
087 * // administrative session be created before the connection is authenticated.
088 * // Attempting to authenticate the connection before creating the
089 * // administrative session may result in the bind using a "regular" worker
090 * // thread rather than an administrative session worker thread, and if all
091 * // normal worker threads are busy or stuck, then the bind request may be
092 * // blocked.
093 * BindResult bindResult = connection.bind(userDN, password);
094 *
095 * // Use the connection to perform operations that may benefit from using an
096 * // administrative session (e.g., operations that troubleshoot and attempt to
097 * // correct some problem with the server).  In this example, we'll just
098 * // request all monitor entries from the server.
099 * List&lt;MonitorEntry&gt; monitorEntries =
100 *      MonitorManager.getMonitorEntries(connection);
101 *
102 * // Use the end administrative session operation to end the administrative
103 * // session and resume using normal worker threads for subsequent operations.
104 * // This isn't strictly needed if we just want to close the connection.
105 * extendedResult = connection.processExtendedOperation(
106 *      new EndAdministrativeSessionExtendedRequest());
107 *
108 * // Do other operations that don't need an administrative session.
109 *
110 * connection.close();
111 * </PRE>
112 *
113 * @see  EndAdministrativeSessionExtendedRequest
114 */
115@NotMutable()
116@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
117public final class StartAdministrativeSessionExtendedRequest
118       extends ExtendedRequest
119{
120  /**
121   * The OID (1.3.6.1.4.1.30221.2.6.13) for the start administrative session
122   * extended request.
123   */
124  public static final String START_ADMIN_SESSION_REQUEST_OID =
125       "1.3.6.1.4.1.30221.2.6.13";
126
127
128
129  /**
130   * The BER type for the client name element of the extended request value.
131   */
132  private static final byte TYPE_CLIENT_NAME = (byte) 0x80;
133
134
135
136  /**
137   * The BER type for the use dedicated thread pool element of the extended
138   * request value.
139   */
140  private static final byte TYPE_USE_DEDICATED_THREAD_POOL = (byte) 0x81;
141
142
143
144  /**
145   * The serial version UID for this serializable class.
146   */
147  private static final long serialVersionUID = -2684374559100906505L;
148
149
150
151  // Indicates whether the client has requested that the server use a dedicated
152  // thread pool for processing operations during the administrative session.
153  private final boolean useDedicatedThreadPool;
154
155  // The name of the client application issuing this request.
156  private final String clientName;
157
158
159
160  /**
161   * Creates a new start administrative session extended request with the
162   * provided information.
163   *
164   * @param  clientName              The name of the client application issuing
165   *                                 this request.  It may be {@code null} if no
166   *                                 client name should be provided.
167   * @param  useDedicatedThreadPool  Indicates whether the server should use a
168   *                                 dedicated worker thread pool for requests
169   *                                 processed by this client.  Note that the
170   *                                 server may define restrictions around the
171   *                                 use of a dedicated thread pool.
172   * @param  controls                The set of controls to include in the
173   *                                 request.
174   */
175  public StartAdministrativeSessionExtendedRequest(final String clientName,
176              final boolean useDedicatedThreadPool, final Control... controls)
177  {
178    super(START_ADMIN_SESSION_REQUEST_OID,
179         encodeValue(clientName, useDedicatedThreadPool),
180         controls);
181
182    this.clientName             = clientName;
183    this.useDedicatedThreadPool = useDedicatedThreadPool;
184  }
185
186
187
188  /**
189   * Creates a new start administrative session extended request from the
190   * provided generic extended request.
191   *
192   * @param  extendedRequest  The generic extended request to use to create this
193   *                          start administrative session extended request.
194   *
195   * @throws  LDAPException  If a problem occurs while decoding the request.
196   */
197  public StartAdministrativeSessionExtendedRequest(
198              final ExtendedRequest extendedRequest)
199         throws LDAPException
200  {
201    super(extendedRequest);
202
203    final ASN1OctetString value = extendedRequest.getValue();
204    if (value == null)
205    {
206      throw new LDAPException(ResultCode.DECODING_ERROR,
207           ERR_START_ADMIN_SESSION_REQUEST_NO_VALUE.get());
208    }
209
210
211    String  appName       = null;
212    boolean dedicatedPool = false;
213
214    try
215    {
216      final ASN1Sequence valueSequence =
217           ASN1Sequence.decodeAsSequence(value.getValue());
218      for (final ASN1Element e : valueSequence.elements())
219      {
220        switch (e.getType())
221        {
222          case TYPE_CLIENT_NAME:
223            appName = ASN1OctetString.decodeAsOctetString(e).stringValue();
224            break;
225          case TYPE_USE_DEDICATED_THREAD_POOL:
226            dedicatedPool = ASN1Boolean.decodeAsBoolean(e).booleanValue();
227            break;
228          default:
229            throw new LDAPException(ResultCode.DECODING_ERROR,
230                 ERR_START_ADMIN_SESSION_REQUEST_UNKNOWN_VALUE_ELEMENT_TYPE.get(
231                      StaticUtils.toHex(e.getType())));
232        }
233      }
234    }
235    catch (final LDAPException le)
236    {
237      Debug.debugException(le);
238      throw le;
239    }
240    catch (final Exception e)
241    {
242      Debug.debugException(e);
243      throw new LDAPException(ResultCode.DECODING_ERROR,
244           ERR_START_ADMIN_SESSION_REQUEST_ERROR_DECODING_VALUE.get(
245                StaticUtils.getExceptionMessage(e)),
246           e);
247    }
248
249    clientName             = appName;
250    useDedicatedThreadPool = dedicatedPool;
251  }
252
253
254
255  /**
256   * Encodes the provided information into an ASN.1 octet string suitable for
257   * use as the value of this extended request.
258   *
259   * @param  clientName              The name of the client application issuing
260   *                                 this request.  It may be {@code null} if no
261   *                                 client name should be provided.
262   * @param  useDedicatedThreadPool  Indicates whether the server should use a
263   *                                 dedicated worker thread pool for requests
264   *                                 processed by this client.  Note that the
265   *                                 server may define restrictions around the
266   *                                 use of a dedicated thread pool.
267   *
268   * @return  The ASN.1 octet string containing the encoded value.
269   */
270  private static ASN1OctetString encodeValue(final String clientName,
271                                      final boolean useDedicatedThreadPool)
272  {
273    final ArrayList<ASN1Element> elements = new ArrayList<>(2);
274
275    if (clientName != null)
276    {
277      elements.add(new ASN1OctetString(TYPE_CLIENT_NAME, clientName));
278    }
279
280    if (useDedicatedThreadPool)
281    {
282      elements.add(new ASN1Boolean(TYPE_USE_DEDICATED_THREAD_POOL, true));
283    }
284
285    return new ASN1OctetString(new ASN1Sequence(elements).encode());
286  }
287
288
289
290  /**
291   * Retrieves the name of the client application issuing this request, if
292   * available.
293   *
294   * @return  The name of the client application issuing this request, or
295   *          {@code null} if it was not included in the request.
296   */
297  public String getClientName()
298  {
299    return clientName;
300  }
301
302
303
304  /**
305   * Indicates whether the server should attempt to use a dedicated worker
306   * thread pool for requests from this client.
307   *
308   * @return  {@code true} if the server should attempt to use a dedicated
309   *          worker thread pool for requests from this client, or {@code false}
310   *          if not.
311   */
312  public boolean useDedicatedThreadPool()
313  {
314    return useDedicatedThreadPool;
315  }
316
317
318
319  /**
320   * {@inheritDoc}
321   */
322  @Override()
323  public StartAdministrativeSessionExtendedRequest duplicate()
324  {
325    return duplicate(getControls());
326  }
327
328
329
330  /**
331   * {@inheritDoc}
332   */
333  @Override()
334  public StartAdministrativeSessionExtendedRequest duplicate(
335              final Control[] controls)
336  {
337    return new StartAdministrativeSessionExtendedRequest(clientName,
338         useDedicatedThreadPool, controls);
339  }
340
341
342
343  /**
344   * {@inheritDoc}
345   */
346  @Override()
347  public String getExtendedRequestName()
348  {
349    return INFO_EXTENDED_REQUEST_NAME_START_ADMIN_SESSION.get();
350  }
351
352
353
354  /**
355   * {@inheritDoc}
356   */
357  @Override()
358  public void toString(final StringBuilder buffer)
359  {
360    buffer.append("StartAdministrativeSessionExtendedRequest(");
361
362    if (clientName != null)
363    {
364      buffer.append("clientName='");
365      buffer.append(clientName);
366      buffer.append("', ");
367    }
368
369    buffer.append("useDedicatedThreadPool=");
370    buffer.append(useDedicatedThreadPool);
371
372    final Control[] controls = getControls();
373    if (controls.length > 0)
374    {
375      buffer.append(", controls={");
376      for (int i=0; i < controls.length; i++)
377      {
378        if (i > 0)
379        {
380          buffer.append(", ");
381        }
382
383        buffer.append(controls[i]);
384      }
385      buffer.append('}');
386    }
387
388    buffer.append(')');
389  }
390}