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