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