001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.protocols.jmx;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.net.*;
033    import java.nio.*;
034    import java.util.*;
035    import java.util.concurrent.atomic.*;
036    import javax.management.Notification;
037    import javax.management.NotificationListener;
038    import javax.management.remote.JMXConnectionNotification;
039    import org.opends.server.api.*;
040    import org.opends.server.core.*;
041    import org.opends.server.extensions.*;
042    import org.opends.server.protocols.asn1.*;
043    import org.opends.server.protocols.ldap.*;
044    import org.opends.server.protocols.internal.InternalSearchOperation ;
045    import org.opends.server.protocols.internal.InternalSearchListener;
046    
047    import org.opends.server.types.AbstractOperation;
048    import org.opends.server.types.Attribute;
049    import org.opends.server.types.AttributeType;
050    import org.opends.server.types.DebugLogLevel;
051    import org.opends.server.types.AuthenticationInfo;
052    import org.opends.server.types.CancelRequest;
053    import org.opends.server.types.CancelResult;
054    import org.opends.server.types.Control;
055    import org.opends.server.types.DN;
056    import org.opends.server.types.DereferencePolicy;
057    import org.opends.server.types.DirectoryException;
058    import org.opends.server.types.DisconnectReason;
059    import org.opends.server.types.IntermediateResponse;
060    import org.opends.server.types.Modification;
061    import org.opends.server.types.ObjectClass;
062    import org.opends.server.types.Operation;
063    import org.opends.server.types.Privilege;
064    import org.opends.server.types.RDN;
065    import org.opends.server.types.RawAttribute;
066    import org.opends.server.types.RawModification;
067    import org.opends.server.types.ResultCode;
068    import org.opends.server.types.SearchResultEntry;
069    import org.opends.server.types.SearchResultReference;
070    import org.opends.server.types.SearchScope;
071    
072    import static org.opends.server.loggers.debug.DebugLogger.*;
073    import org.opends.server.loggers.debug.DebugTracer;
074    import static org.opends.messages.ProtocolMessages.*;
075    
076    import org.opends.messages.MessageBuilder;
077    
078    
079    /**
080     * This class defines the set of methods and structures that must be implemented
081     * by a Directory Server client connection.
082     *
083     */
084    public class JmxClientConnection
085           extends ClientConnection implements NotificationListener
086    {
087      /**
088       * The tracer object for the debug logger.
089       */
090      private static final DebugTracer TRACER = getTracer();
091    
092      // The message ID counter to use for jmx connections.
093      private AtomicInteger nextMessageID;
094    
095      // The operation ID counter to use for operations on this connection.
096      private AtomicLong nextOperationID;
097    
098      // The connection security provider for this client connection.
099      private ConnectionSecurityProvider securityProvider;
100    
101      // The empty operation list for this connection.
102      private LinkedList<AbstractOperation> operationList;
103    
104      // The connection ID for this client connection.
105      private long connectionID;
106    
107      /**
108       * The JMX connection ID for this client connection.
109       */
110      protected String jmxConnectionID = null;
111    
112      /**
113       * The reference to the connection handler that accepted this connection.
114       */
115      private JmxConnectionHandler jmxConnectionHandler;
116    
117      /**
118       * Indicate that the disconnect process is started.
119       */
120      private Boolean disconnectStarted = new Boolean(false);
121    
122    
123      /**
124       * Creates a new Jmx client connection that will be authenticated as
125       * as the specified user.
126       *
127       * @param jmxConnectionHandler
128       *        The connection handler on which we should be registered
129       * @param authInfo
130       *        the User authentication info
131       */
132      public JmxClientConnection(JmxConnectionHandler jmxConnectionHandler,
133          AuthenticationInfo authInfo)
134      {
135        super();
136    
137    
138        nextMessageID    = new AtomicInteger(1);
139        nextOperationID  = new AtomicLong(0);
140    
141        this.jmxConnectionHandler = jmxConnectionHandler;
142        jmxConnectionHandler.registerClientConnection(this);
143    
144        setAuthenticationInfo(authInfo);
145    
146        connectionID = DirectoryServer.newConnectionAccepted(this);
147        if (connectionID < 0)
148        {
149          //
150          // TODO Change Message to be JMX specific
151          disconnect(
152              DisconnectReason.ADMIN_LIMIT_EXCEEDED,
153              true,
154              ERR_LDAP_CONNHANDLER_REJECTED_BY_SERVER.get());
155        }
156        operationList = new LinkedList<AbstractOperation>();
157    
158        try
159        {
160          securityProvider = new NullConnectionSecurityProvider();
161          securityProvider.initializeConnectionSecurityProvider(null);
162        }
163        catch (Exception e)
164        {
165          if (debugEnabled())
166          {
167            TRACER.debugCaught(DebugLogLevel.ERROR, e);
168          }
169        }
170    
171        //
172        // Register the Jmx Notification listener (this)
173        jmxConnectionHandler.getRMIConnector().jmxRmiConnectorNoClientCertificate
174            .addNotificationListener(this, null, null);
175      }
176    
177    
178      /**
179       * {@inheritDoc}
180       */
181      public void handleNotification(Notification notif, Object handback)
182      {
183        JMXConnectionNotification jcn ;
184    
185        //
186        // We don't have the expected notification
187        if ( ! (notif instanceof JMXConnectionNotification))
188        {
189          return ;
190        }
191        else
192        {
193          jcn = (JMXConnectionNotification) notif ;
194        }
195    
196        //
197        // The only handled notifications are CLOSED and FAILED
198        if (! (
199            (jcn.getType().equals(JMXConnectionNotification.CLOSED))
200            ||
201            (jcn.getType().equals(JMXConnectionNotification.FAILED))
202            ))
203        {
204          return;
205        }
206    
207        //
208        // Check if the closed connection corresponds to the current connection
209        if (!(jcn.getConnectionId().equals(jmxConnectionID)))
210        {
211          return;
212        }
213    
214        //
215        // Ok, we can perform the unbind: call finalize
216        disconnect(DisconnectReason.CLIENT_DISCONNECT, false, null);
217      }
218    
219    
220      /**
221       * Retrieves the operation ID that should be used for the next Jmx
222       * operation.
223       *
224       * @return  The operation ID that should be used for the next Jmx
225       *          operation.
226       */
227      public long nextOperationID()
228      {
229        long opID = nextOperationID.getAndIncrement();
230        if (opID < 0)
231        {
232          synchronized (nextOperationID)
233          {
234            if (nextOperationID.get() < 0)
235            {
236              nextOperationID.set(1);
237              return 0;
238            }
239            else
240            {
241              return nextOperationID.getAndIncrement();
242            }
243          }
244        }
245    
246        return opID;
247      }
248    
249    
250    
251      /**
252       * Retrieves the message ID that should be used for the next Jmx
253       * operation.
254       *
255       * @return  The message ID that should be used for the next Jmx
256       *          operation.
257       */
258      public int nextMessageID()
259      {
260        int msgID = nextMessageID.getAndIncrement();
261        if (msgID < 0)
262        {
263          synchronized (nextMessageID)
264          {
265            if (nextMessageID.get() < 0)
266            {
267              nextMessageID.set(2);
268              return 1;
269            }
270            else
271            {
272              return nextMessageID.getAndIncrement();
273            }
274          }
275        }
276    
277        return msgID;
278      }
279    
280    
281    
282      /**
283       * Retrieves the unique identifier that has been assigned to this connection.
284       *
285       * @return  The unique identifier that has been assigned to this connection.
286       */
287      public long getConnectionID()
288      {
289        return connectionID;
290      }
291    
292      /**
293       * Retrieves the connection handler that accepted this client connection.
294       *
295       * @return  The connection handler that accepted this client connection.
296       */
297      public ConnectionHandler getConnectionHandler()
298      {
299        return jmxConnectionHandler;
300      }
301    
302      /**
303       * Retrieves the protocol that the client is using to communicate with the
304       * Directory Server.
305       *
306       * @return  The protocol that the client is using to communicate with the
307       *          Directory Server.
308       */
309      public String getProtocol()
310      {
311        return "jmx";
312      }
313    
314    
315    
316      /**
317       * Retrieves a string representation of the address of the client.
318       *
319       * @return  A string representation of the address of the client.
320       */
321      public String getClientAddress()
322      {
323        return "jmx";
324      }
325    
326    
327    
328      /**
329       * Retrieves a string representation of the address on the server to which the
330       * client connected.
331       *
332       * @return  A string representation of the address on the server to which the
333       *          client connected.
334       */
335      public String getServerAddress()
336      {
337        return "jmx";
338      }
339    
340    
341    
342      /**
343       * Retrieves the <CODE>java.net.InetAddress</CODE> associated with the remote
344       * client system.
345       *
346       * @return  The <CODE>java.net.InetAddress</CODE> associated with the remote
347       *          client system.  It may be <CODE>null</CODE> if the client is not
348       *          connected over an IP-based connection.
349       */
350      public InetAddress getRemoteAddress()
351      {
352        return null;
353      }
354    
355    
356    
357      /**
358       * Retrieves the <CODE>java.net.InetAddress</CODE> for the Directory Server
359       * system to which the client has established the connection.
360       *
361       * @return  The <CODE>java.net.InetAddress</CODE> for the Directory Server
362       *          system to which the client has established the connection.  It may
363       *          be <CODE>null</CODE> if the client is not connected over an
364       *          IP-based connection.
365       */
366      public InetAddress getLocalAddress()
367      {
368        return null;
369      }
370    
371    
372    
373      /**
374       * Indicates whether this client connection is currently using a secure
375       * mechanism to communicate with the server.  Note that this may change over
376       * time based on operations performed by the client or server (e.g., it may go
377       * from <CODE>false</CODE> to <CODE>true</CODE> if the client uses the
378       * StartTLS extended operation).
379       *
380       * @return  <CODE>true</CODE> if the client connection is currently using a
381       *          secure mechanism to communicate with the server, or
382       *          <CODE>false</CODE> if not.
383       */
384      public boolean isSecure()
385      {
386        return securityProvider.isSecure();
387      }
388    
389    
390    
391      /**
392       * Retrieves the connection security provider for this client connection.
393       *
394       * @return  The connection security provider for this client connection.
395       */
396      public ConnectionSecurityProvider getConnectionSecurityProvider()
397      {
398        return securityProvider;
399      }
400    
401    
402    
403      /**
404       * Specifies the connection security provider for this client connection.
405       *
406       * @param  securityProvider  The connection security provider to use for
407       *                           communication on this client connection.
408       */
409      public void setConnectionSecurityProvider(ConnectionSecurityProvider
410                                                     securityProvider)
411      {
412        this.securityProvider = securityProvider;
413      }
414    
415    
416    
417      /**
418       * Retrieves the human-readable name of the security mechanism that is used to
419       * protect communication with this client.
420       *
421       * @return  The human-readable name of the security mechanism that is used to
422       *          protect communication with this client, or <CODE>null</CODE> if no
423       *          security is in place.
424       */
425      public String getSecurityMechanism()
426      {
427        return securityProvider.getSecurityMechanismName();
428      }
429    
430    
431    
432      /**
433       * Indicates that the data in the provided buffer has been read from the
434       * client and should be processed.  The contents of the provided buffer will
435       * be in clear-text (the data may have been passed through a connection
436       * security provider to obtain the clear-text version), and may contain part
437       * or all of one or more client requests.
438       *
439       * @param  buffer  The byte buffer containing the data available for reading.
440       *
441       * @return  <CODE>true</CODE> if all the data in the provided buffer was
442       *          processed and the client connection can remain established, or
443       *          <CODE>false</CODE> if a decoding error occurred and requests from
444       *          this client should no longer be processed.  Note that if this
445       *          method does return <CODE>false</CODE>, then it must have already
446       *          disconnected the client.
447       */
448      public boolean processDataRead(ByteBuffer buffer)
449      {
450        // This method will not do anything with the data because there is no
451        // actual "connection" from which information can be read, nor any protocol
452        // to use to read it.
453        return false;
454      }
455    
456    
457    
458      /**
459       * Sends a response to the client based on the information in the provided
460       * operation.
461       *
462       * @param  operation  The operation for which to send the response.
463       */
464      public void sendResponse(Operation operation)
465      {
466        // There will not be any response sent by this method, since there is not an
467        // actual connection.
468      }
469    
470    
471    
472      /**
473       * Processes an Jmx add operation with the provided information.
474       *
475       * @param  rawEntryDN     The DN to use for the entry to add.
476       * @param  rawAttributes  The set of attributes to include in the entry to
477       *                        add.
478       *
479       * @return  A reference to the add operation that was processed and contains
480       *          information about the result of the processing.
481       */
482      public AddOperation processAdd(ASN1OctetString rawEntryDN,
483                                     ArrayList<RawAttribute> rawAttributes)
484      {
485        AddOperationBasis addOperation =
486             new AddOperationBasis(this, nextOperationID(), nextMessageID(),
487                              new ArrayList<Control>(0), rawEntryDN, rawAttributes);
488    
489        // Check if we have enough privilege
490        if (! hasPrivilege(Privilege.JMX_WRITE, null))
491        {
492          Message message = ERR_JMX_ADD_INSUFFICIENT_PRIVILEGES.get();
493          addOperation.setErrorMessage(new MessageBuilder(message));
494          addOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
495        }
496        else
497        {
498          addOperation.run();
499        }
500        return addOperation;
501      }
502    
503      /**
504       * Processes an internal add operation with the provided
505       * information.
506       *
507       * @param  entryDN                The entry DN for the add
508       *                                operation.
509       * @param  objectClasses          The set of objectclasses for the
510       *                                add operation.
511       * @param  userAttributes         The set of user attributes for the
512       *                                add operation.
513       * @param  operationalAttributes  The set of operational attributes
514       *                                for the add operation.
515       *
516       * @return  A reference to the add operation that was processed and
517       *          contains information about the result of the processing.
518       */
519      public AddOperation processAdd(DN entryDN,
520                               Map<ObjectClass,String> objectClasses,
521                               Map<AttributeType,List<Attribute>>
522                                    userAttributes,
523                               Map<AttributeType,List<Attribute>>
524                                    operationalAttributes)
525      {
526        AddOperationBasis addOperation =
527             new AddOperationBasis(this, nextOperationID(),
528                              nextMessageID(),
529                              new ArrayList<Control>(0), entryDN,
530                              objectClasses, userAttributes,
531                              operationalAttributes);
532        // Check if we have enough privilege
533        if (! hasPrivilege(Privilege.JMX_WRITE, null))
534        {
535          Message message = ERR_JMX_ADD_INSUFFICIENT_PRIVILEGES.get();
536          addOperation.setErrorMessage(new MessageBuilder(message));
537          addOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
538        }
539        else
540        {
541          addOperation.run();
542        }
543        return addOperation;
544      }
545    
546      /**
547       * Processes an internal delete operation with the provided
548       * information.
549       *
550       * @param  entryDN  The entry DN for the delete operation.
551       *
552       * @return  A reference to the delete operation that was processed
553       *          and contains information about the result of the
554       *          processing.
555       */
556      public DeleteOperation processDelete(DN entryDN)
557      {
558        DeleteOperationBasis deleteOperation =
559             new DeleteOperationBasis(this, nextOperationID(),
560                                 nextMessageID(),
561                                 new ArrayList<Control>(0), entryDN);
562        // Check if we have enough privilege
563        if (! hasPrivilege(Privilege.JMX_WRITE, null))
564        {
565          Message message = ERR_JMX_DELETE_INSUFFICIENT_PRIVILEGES.get();
566          deleteOperation.setErrorMessage(new MessageBuilder(message));
567          deleteOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
568        }
569        else
570        {
571          deleteOperation.run();
572        }
573        return deleteOperation;
574      }
575    
576    
577      /**
578       * Processes an Jmx compare operation with the provided information.
579       *
580       * @param  rawEntryDN      The entry DN for the compare operation.
581       * @param  attributeType   The attribute type for the compare operation.
582       * @param  assertionValue  The assertion value for the compare operation.
583       *
584       * @return  A reference to the compare operation that was processed and
585       *          contains information about the result of the processing.
586       */
587      public CompareOperation processCompare(ASN1OctetString rawEntryDN,
588                                            String attributeType,
589                                            ASN1OctetString assertionValue)
590      {
591        CompareOperationBasis compareOperation =
592             new CompareOperationBasis(this, nextOperationID(), nextMessageID(),
593                                  new ArrayList<Control>(0), rawEntryDN,
594                                  attributeType, assertionValue);
595    
596        // Check if we have enough privilege
597        if (! hasPrivilege(Privilege.JMX_READ, null))
598        {
599          Message message = ERR_JMX_SEARCH_INSUFFICIENT_PRIVILEGES.get();
600          compareOperation.setErrorMessage(new MessageBuilder(message));
601          compareOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
602        }
603        else
604        {
605          compareOperation.run();
606        }
607        return compareOperation;
608      }
609    
610    
611    
612      /**
613       * Processes an Jmx delete operation with the provided information.
614       *
615       * @param  rawEntryDN  The entry DN for the delete operation.
616       *
617       * @return  A reference to the delete operation that was processed and
618       *          contains information about the result of the processing.
619       */
620      public DeleteOperation processDelete(ASN1OctetString rawEntryDN)
621      {
622        DeleteOperationBasis deleteOperation =
623             new DeleteOperationBasis(this, nextOperationID(), nextMessageID(),
624                                 new ArrayList<Control>(0), rawEntryDN);
625    
626        // Check if we have enough privilege
627        if (! hasPrivilege(Privilege.JMX_WRITE, null))
628        {
629          Message message = ERR_JMX_DELETE_INSUFFICIENT_PRIVILEGES.get();
630          deleteOperation.setErrorMessage(new MessageBuilder(message));
631          deleteOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
632        }
633        else
634        {
635          deleteOperation.run();
636        }
637        return deleteOperation;
638      }
639    
640    
641    
642      /**
643       * Processes an Jmx extended operation with the provided information.
644       *
645       * @param  requestOID    The OID for the extended request.
646       * @param  requestValue  The encoded +value for the extended operation, or
647       *                       <CODE>null</CODE> if there is no value.
648       *
649       * @return  A reference to the extended operation that was processed and
650       *          contains information about the result of the processing.
651       */
652      public ExtendedOperation processExtendedOperation(String requestOID,
653                                    ASN1OctetString requestValue)
654      {
655        ExtendedOperationBasis extendedOperation =
656             new ExtendedOperationBasis(this, nextOperationID(), nextMessageID(),
657                                   new ArrayList<Control>(0), requestOID,
658                                   requestValue);
659    
660        extendedOperation.run();
661        return extendedOperation;
662      }
663    
664    
665    
666      /**
667       * Processes an Jmx modify operation with the provided information.
668       *
669       * @param  rawEntryDN        The raw entry DN for this modify operation.
670       * @param  rawModifications  The set of modifications for this modify
671       *                           operation.
672       *
673       * @return  A reference to the modify operation that was processed and
674       *          contains information about the result of the processing
675       */
676      public ModifyOperation processModify(ASN1OctetString rawEntryDN,
677                                  ArrayList<RawModification> rawModifications)
678      {
679        ModifyOperationBasis modifyOperation =
680             new ModifyOperationBasis(this, nextOperationID(), nextMessageID(),
681                                 new ArrayList<Control>(0), rawEntryDN,
682                                 rawModifications);
683    
684        if (! hasPrivilege(Privilege.JMX_WRITE, null))
685        {
686          Message message = ERR_JMX_MODIFY_INSUFFICIENT_PRIVILEGES.get();
687          modifyOperation.setErrorMessage(new MessageBuilder(message));
688          modifyOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
689        }
690        else
691        {
692          modifyOperation.run();
693        }
694        return modifyOperation;
695      }
696    
697    
698      /**
699       * Processes an internal modify operation with the provided
700       * information.
701       *
702       * @param  entryDN        The entry DN for this modify operation.
703       * @param  modifications  The set of modifications for this modify
704       *                        operation.
705       *
706       * @return  A reference to the modify operation that was processed
707       *          and contains information about the result of the
708       *          processing.
709       */
710      public ModifyOperation processModify(DN entryDN,
711                                  List<Modification> modifications)
712      {
713        ModifyOperationBasis modifyOperation =
714             new ModifyOperationBasis(this, nextOperationID(),
715                                 nextMessageID(),
716                                 new ArrayList<Control>(0), entryDN,
717                                 modifications);
718        if (! hasPrivilege(Privilege.JMX_WRITE, null))
719        {
720          Message message = ERR_JMX_MODIFY_INSUFFICIENT_PRIVILEGES.get();
721          modifyOperation.setErrorMessage(new MessageBuilder(message));
722          modifyOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
723        }
724        else
725        {
726          modifyOperation.run();
727        }
728        return modifyOperation;
729      }
730    
731      /**
732       * Processes an Jmx modify DN operation with the provided information.
733       *
734       * @param  rawEntryDN    The current DN of the entry to rename.
735       * @param  rawNewRDN     The new RDN to use for the entry.
736       * @param  deleteOldRDN  The flag indicating whether the old RDN value is to
737       *                       be removed from the entry.
738       *
739       * @return  A reference to the modify DN operation that was processed and
740       *          contains information about the result of the processing.
741       */
742      public ModifyDNOperation processModifyDN(ASN1OctetString rawEntryDN,
743                                               ASN1OctetString rawNewRDN,
744                                               boolean deleteOldRDN)
745      {
746        return processModifyDN(rawEntryDN, rawNewRDN, deleteOldRDN, null);
747      }
748    
749    
750    
751      /**
752       * Processes an Jmx modify DN operation with the provided information.
753       *
754       * @param  rawEntryDN      The current DN of the entry to rename.
755       * @param  rawNewRDN       The new RDN to use for the entry.
756       * @param  deleteOldRDN    The flag indicating whether the old RDN value is to
757       *                         be removed from the entry.
758       * @param  rawNewSuperior  The new superior for the modify DN operation, or
759       *                         <CODE>null</CODE> if the entry will remain below
760       *                         the same parent.
761       *
762       * @return  A reference to the modify DN operation that was processed and
763       *          contains information about the result of the processing.
764       */
765      public ModifyDNOperation processModifyDN(ASN1OctetString rawEntryDN,
766                                               ASN1OctetString rawNewRDN,
767                                               boolean deleteOldRDN,
768                                               ASN1OctetString rawNewSuperior)
769      {
770        ModifyDNOperationBasis modifyDNOperation =
771             new ModifyDNOperationBasis(this, nextOperationID(), nextMessageID(),
772                                   new ArrayList<Control>(0), rawEntryDN, rawNewRDN,
773                                   deleteOldRDN, rawNewSuperior);
774    
775        if (! hasPrivilege(Privilege.JMX_WRITE, null))
776        {
777          Message message = ERR_JMX_MODDN_INSUFFICIENT_PRIVILEGES.get();
778          modifyDNOperation.setErrorMessage(new MessageBuilder(message));
779          modifyDNOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
780        }
781        else
782        {
783          modifyDNOperation.run();
784        }
785        return modifyDNOperation;
786      }
787    
788      /**
789       * Processes an internal modify DN operation with the provided
790       * information.
791       *
792       * @param  entryDN       The current DN of the entry to rename.
793       * @param  newRDN        The new RDN to use for the entry.
794       * @param  deleteOldRDN  The flag indicating whether the old RDN
795       *                       value is to be removed from the entry.
796       * @param  newSuperior   The new superior for the modify DN
797       *                       operation, or <CODE>null</CODE> if the
798       *                       entry will remain below the same parent.
799       *
800       * @return  A reference to the modify DN operation that was
801       *          processed and contains information about the result of
802       *          the processing.
803       */
804      public ModifyDNOperation processModifyDN(DN entryDN, RDN newRDN,
805                                               boolean deleteOldRDN,
806                                               DN newSuperior)
807      {
808        ModifyDNOperationBasis modifyDNOperation =
809             new ModifyDNOperationBasis(this, nextOperationID(),
810                                   nextMessageID(),
811                                   new ArrayList<Control>(0), entryDN,
812                                   newRDN, deleteOldRDN, newSuperior);
813    
814        if (! hasPrivilege(Privilege.JMX_WRITE, null))
815        {
816          Message message = ERR_JMX_MODDN_INSUFFICIENT_PRIVILEGES.get();
817          modifyDNOperation.setErrorMessage(new MessageBuilder(message));
818          modifyDNOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
819        }
820        else
821        {
822          modifyDNOperation.run();
823        }
824        return modifyDNOperation;
825      }
826    
827      /**
828       * Processes an Jmx search operation with the provided information.
829       * It will not dereference any aliases, will not request a size or time limit,
830       * and will retrieve all user attributes.
831       *
832       * @param  rawBaseDN  The base DN for the search.
833       * @param  scope      The scope for the search.
834       * @param  filter     The filter for the search.
835       *
836       * @return  A reference to the internal search operation that was processed
837       *          and contains information about the result of the processing as
838       *          well as lists of the matching entries and search references.
839       */
840      public InternalSearchOperation processSearch(ASN1OctetString rawBaseDN,
841                                          SearchScope scope, LDAPFilter filter)
842      {
843        return processSearch(rawBaseDN, scope,
844                             DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
845                             filter, new LinkedHashSet<String>(0));
846      }
847    
848    
849    
850      /**
851       * Processes an Jmx search operation with the provided information.
852       *
853       * @param  rawBaseDN    The base DN for the search.
854       * @param  scope        The scope for the search.
855       * @param  derefPolicy  The alias dereferencing policy for the search.
856       * @param  sizeLimit    The size limit for the search.
857       * @param  timeLimit    The time limit for the search.
858       * @param  typesOnly    The typesOnly flag for the search.
859       * @param  filter       The filter for the search.
860       * @param  attributes   The set of requested attributes for the search.
861       *
862       * @return  A reference to the internal search operation that was processed
863       *          and contains information about the result of the processing as
864       *          well as lists of the matching entries and search references.
865       */
866      public InternalSearchOperation processSearch(ASN1OctetString rawBaseDN,
867                                          SearchScope scope,
868                                          DereferencePolicy derefPolicy,
869                                          int sizeLimit, int timeLimit,
870                                          boolean typesOnly, LDAPFilter filter,
871                                          LinkedHashSet<String> attributes)
872      {
873        InternalSearchOperation searchOperation =
874             new InternalSearchOperation(this, nextOperationID(), nextMessageID(),
875                                         new ArrayList<Control>(0), rawBaseDN,
876                                         scope, derefPolicy, sizeLimit, timeLimit,
877                                         typesOnly, filter, attributes, null);
878    
879        if (! hasPrivilege(Privilege.JMX_READ, null))
880        {
881          Message message = ERR_JMX_SEARCH_INSUFFICIENT_PRIVILEGES.get();
882          searchOperation.setErrorMessage(new MessageBuilder(message));
883          searchOperation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS) ;
884        }
885        else
886        {
887          searchOperation.run();
888        }
889        return searchOperation;
890      }
891    
892    
893    
894      /**
895       * Processes an Jmx search operation with the provided information.
896       *
897       * @param  rawBaseDN       The base DN for the search.
898       * @param  scope           The scope for the search.
899       * @param  derefPolicy     The alias dereferencing policy for the search.
900       * @param  sizeLimit       The size limit for the search.
901       * @param  timeLimit       The time limit for the search.
902       * @param  typesOnly       The typesOnly flag for the search.
903       * @param  filter          The filter for the search.
904       * @param  attributes      The set of requested attributes for the search.
905       * @param  searchListener  The internal search listener that should be used to
906       *                         handle the matching entries and references.
907       *
908       * @return  A reference to the internal search operation that was processed
909       *          and contains information about the result of the processing.
910       */
911      public InternalSearchOperation processSearch(ASN1OctetString rawBaseDN,
912                                          SearchScope scope,
913                                          DereferencePolicy derefPolicy,
914                                          int sizeLimit, int timeLimit,
915                                          boolean typesOnly, LDAPFilter filter,
916                                          LinkedHashSet<String> attributes,
917                                          InternalSearchListener searchListener)
918      {
919        InternalSearchOperation searchOperation =
920             new InternalSearchOperation(this, nextOperationID(), nextMessageID(),
921                                         new ArrayList<Control>(0), rawBaseDN,
922                                         scope, derefPolicy, sizeLimit, timeLimit,
923                                         typesOnly, filter, attributes,
924                                         searchListener);
925    
926        searchOperation.run();
927        return searchOperation;
928      }
929    
930    
931    
932      /**
933       * Sends the provided search result entry to the client.
934       *
935       * @param  searchOperation  The search operation with which the entry is
936       *                          associated.
937       * @param  searchEntry      The search result entry to be sent to the client.
938       *
939       * @throws  DirectoryException  If a problem occurs while attempting to send
940       *                              the entry to the client and the search should
941       *                              be terminated.
942       */
943      public void sendSearchEntry(SearchOperation searchOperation,
944                                  SearchResultEntry searchEntry)
945             throws DirectoryException
946      {
947        ((InternalSearchOperation) searchOperation).addSearchEntry(searchEntry);
948      }
949    
950    
951    
952      /**
953       * Sends the provided search result reference to the client.
954       *
955       * @param  searchOperation  The search operation with which the reference is
956       *                          associated.
957       * @param  searchReference  The search result reference to be sent to the
958       *                          client.
959       *
960       * @return  <CODE>true</CODE> if the client is able to accept referrals, or
961       *          <CODE>false</CODE> if the client cannot handle referrals and no
962       *          more attempts should be made to send them for the associated
963       *          search operation.
964       *
965       * @throws  DirectoryException  If a problem occurs while attempting to send
966       *                              the reference to the client and the search
967       *                              should be terminated.
968       */
969      public boolean sendSearchReference(SearchOperation searchOperation,
970                                         SearchResultReference searchReference)
971             throws DirectoryException
972      {
973        ((InternalSearchOperation)
974         searchOperation).addSearchReference(searchReference);
975        return true;
976      }
977    
978    
979    
980    
981      /**
982       * Sends the provided intermediate response message to the client.
983       *
984       * @param  intermediateResponse  The intermediate response message to be sent.
985       *
986       * @return  <CODE>true</CODE> if processing on the associated operation should
987       *          continue, or <CODE>false</CODE> if not.
988       */
989      protected boolean sendIntermediateResponseMessage(
990                             IntermediateResponse intermediateResponse)
991      {
992        // FIXME -- Do we need to support Jmx intermediate responses?  If so,
993        // then implement this.
994        return false;
995      }
996    
997    
998    
999    
1000      /**
1001       * Closes the connection to the client, optionally sending it a message
1002       * indicating the reason for the closure.  Note that the ability to send a
1003       * notice of disconnection may not be available for all protocols or under all
1004       * circumstances.
1005       *
1006       * @param  disconnectReason  The disconnect reason that provides the generic
1007       *                           cause for the disconnect.
1008       * @param  sendNotification  Indicates whether to try to provide notification
1009       *                           to the client that the connection will be closed.
1010       * @param  message           The message to send to the client.  It may be
1011       *                           <CODE>null</CODE> if no notification is to be
1012       *                           sent.
1013       */
1014      public void disconnect(DisconnectReason disconnectReason,
1015                             boolean sendNotification,
1016                             Message message)
1017      {
1018        // we are already performing a disconnect
1019        if (disconnectStarted)
1020        {
1021          return;
1022        }
1023        disconnectStarted = true ;
1024        finalizeConnectionInternal();
1025    
1026    
1027    
1028        // unbind the underlying connection
1029        try
1030        {
1031          UnbindOperationBasis unbindOp = new UnbindOperationBasis(
1032              (ClientConnection) this,
1033              this.nextOperationID(),
1034              this.nextMessageID(), null);
1035    
1036          unbindOp.run();
1037        }
1038        catch (Exception e)
1039        {
1040          // TODO print a message ?
1041          if (debugEnabled())
1042          {
1043            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1044          }
1045        }
1046    
1047        // Call postDisconnectPlugins
1048        try
1049        {
1050          PluginConfigManager pluginManager =
1051               DirectoryServer.getPluginConfigManager();
1052          pluginManager.invokePostDisconnectPlugins(this, disconnectReason,
1053                                                    message);
1054        }
1055        catch (Exception e)
1056        {
1057          if (debugEnabled())
1058          {
1059            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1060          }
1061        }
1062      }
1063    
1064    
1065    
1066      /**
1067       * Indicates whether a bind operation is in progress on this client
1068       * connection.  If so, then no new operations should be allowed until the bind
1069       * has completed.
1070       *
1071       * @return  <CODE>true</CODE> if a bind operation is in progress on this
1072       *          connection, or <CODE>false</CODE> if not.
1073       */
1074      public boolean bindInProgress()
1075      {
1076        // For Jmx operations, we don't care if there are any binds in
1077        // progress.
1078        return false;
1079      }
1080    
1081    
1082    
1083      /**
1084       * Specifies whether a bind operation is in progress on this client
1085       * connection.  If so, then no new operations should be allowed until the bind
1086       * has completed.
1087       *
1088       * @param  bindInProgress  Specifies whether a bind operation is in progress
1089       *                         on this client connection.
1090       */
1091      public void setBindInProgress(boolean bindInProgress)
1092      {
1093        // No implementation is required.
1094      }
1095    
1096    
1097    
1098      /**
1099       * Retrieves the set of operations in progress for this client connection.
1100       * This list must not be altered by any caller.
1101       *
1102       * @return  The set of operations in progress for this client connection.
1103       */
1104      public Collection<AbstractOperation> getOperationsInProgress()
1105      {
1106        return operationList;
1107      }
1108    
1109    
1110    
1111      /**
1112       * Retrieves the operation in progress with the specified message ID.
1113       *
1114       * @param  messageID  The message ID of the operation to retrieve.
1115       *
1116       * @return  The operation in progress with the specified message ID, or
1117       *          <CODE>null</CODE> if no such operation could be found.
1118       */
1119      public AbstractOperation getOperationInProgress(int messageID)
1120      {
1121        // Jmx operations will not be tracked.
1122        return null;
1123      }
1124    
1125    
1126    
1127      /**
1128       * Removes the provided operation from the set of operations in progress for
1129       * this client connection.  Note that this does not make any attempt to
1130       * cancel any processing that may already be in progress for the operation.
1131       *
1132       * @param  messageID  The message ID of the operation to remove from the set
1133       *                    of operations in progress.
1134       *
1135       * @return  <CODE>true</CODE> if the operation was found and removed from the
1136       *          set of operations in progress, or <CODE>false</CODE> if not.
1137       */
1138      public boolean removeOperationInProgress(int messageID)
1139      {
1140        // No implementation is required, since Jmx operations will not be
1141        // tracked.
1142        return false;
1143      }
1144    
1145    
1146    
1147      /**
1148       * Attempts to cancel the specified operation.
1149       *
1150       * @param  messageID      The message ID of the operation to cancel.
1151       * @param  cancelRequest  An object providing additional information about how
1152       *                        the cancel should be processed.
1153       *
1154       * @return  A cancel result that either indicates that the cancel was
1155       *          successful or provides a reason that it was not.
1156       */
1157      public CancelResult cancelOperation(int messageID,
1158                                          CancelRequest cancelRequest)
1159      {
1160        // Jmx operations cannot be cancelled.
1161        // TODO: i18n
1162        return new CancelResult(ResultCode.CANNOT_CANCEL,
1163            Message.raw("Jmx operations cannot be cancelled"));
1164      }
1165    
1166    
1167    
1168      /**
1169       * Attempts to cancel all operations in progress on this connection.
1170       *
1171       * @param  cancelRequest  An object providing additional information about how
1172       *                        the cancel should be processed.
1173       */
1174      public void cancelAllOperations(CancelRequest cancelRequest)
1175      {
1176        // No implementation is required since Jmx operations cannot be
1177        // cancelled.
1178      }
1179    
1180    
1181    
1182      /**
1183       * Attempts to cancel all operations in progress on this connection except the
1184       * operation with the specified message ID.
1185       *
1186       * @param  cancelRequest  An object providing additional information about how
1187       *                        the cancel should be processed.
1188       * @param  messageID      The message ID of the operation that should not be
1189       *                        canceled.
1190       */
1191      public void cancelAllOperationsExcept(CancelRequest cancelRequest,
1192                                            int messageID)
1193      {
1194        // No implementation is required since Jmx operations cannot be
1195        // cancelled.
1196      }
1197    
1198    
1199    
1200      /**
1201       * {@inheritDoc}
1202       */
1203      public String getMonitorSummary()
1204      {
1205        StringBuilder buffer = new StringBuilder();
1206        buffer.append("connID=\"");
1207        buffer.append(connectionID);
1208        buffer.append("\" connectTime=\"");
1209        buffer.append(getConnectTimeString());
1210        buffer.append("\" jmxConnID=\"");
1211        buffer.append(jmxConnectionID);
1212        buffer.append("\" authDN=\"");
1213    
1214        DN authDN = getAuthenticationInfo().getAuthenticationDN();
1215        if (authDN != null)
1216        {
1217          authDN.toString(buffer);
1218        }
1219    
1220        buffer.append("\" security=\"");
1221        if (securityProvider.isSecure())
1222        {
1223          buffer.append(securityProvider.getSecurityMechanismName());
1224        }
1225        else
1226        {
1227          buffer.append("none");
1228        }
1229    
1230        buffer.append("\"");
1231    
1232        return buffer.toString();
1233      }
1234    
1235    
1236    
1237      /**
1238       * Appends a string representation of this client connection to the provided
1239       * buffer.
1240       *
1241       * @param  buffer  The buffer to which the information should be appended.
1242       */
1243      public void toString(StringBuilder buffer)
1244      {
1245        buffer.append("JmxClientConnection(connID=");
1246        buffer.append(connectionID);
1247        buffer.append(", authDN=\"");
1248        buffer.append(getAuthenticationInfo().getAuthenticationDN());
1249        buffer.append("\")");
1250      }
1251    
1252      /**
1253       * Called by the Gc when the object is garbage collected
1254       * Release the cursor in case the iterator was badly used and releaseCursor
1255       * was never called.
1256       */
1257      protected void finalize()
1258      {
1259        super.finalize();
1260        disconnect(DisconnectReason.OTHER, false, null);
1261      }
1262    }
1263