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