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