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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2007-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.tools.status; 028 029import static com.forgerock.opendj.cli.ArgumentConstants.*; 030import static com.forgerock.opendj.cli.CliMessages.*; 031import static com.forgerock.opendj.cli.Utils.*; 032import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*; 033import static org.forgerock.util.Utils.*; 034 035import static org.opends.messages.AdminToolMessages.*; 036import static org.opends.messages.QuickSetupMessages.INFO_ERROR_READING_SERVER_CONFIGURATION; 037import static org.opends.messages.QuickSetupMessages.INFO_NOT_AVAILABLE_LABEL; 038 039import java.io.File; 040import java.io.InputStream; 041import java.io.OutputStream; 042import java.io.PrintStream; 043import java.net.URI; 044import java.security.GeneralSecurityException; 045import java.security.cert.CertificateException; 046import java.security.cert.X509Certificate; 047import java.util.HashSet; 048import java.util.Set; 049import java.util.TreeSet; 050import java.util.concurrent.TimeUnit; 051 052import javax.naming.AuthenticationException; 053import javax.naming.NamingException; 054import javax.naming.ldap.InitialLdapContext; 055import javax.net.ssl.KeyManager; 056import javax.net.ssl.SSLException; 057import javax.net.ssl.TrustManager; 058 059import org.forgerock.i18n.LocalizableMessage; 060import org.forgerock.i18n.LocalizableMessageBuilder; 061import org.forgerock.i18n.slf4j.LocalizedLogger; 062import org.forgerock.opendj.config.LDAPProfile; 063import org.forgerock.opendj.config.client.ManagementContext; 064import org.forgerock.opendj.config.client.ldap.LDAPManagementContext; 065import org.forgerock.opendj.config.server.ConfigException; 066import org.forgerock.opendj.ldap.AuthorizationException; 067import org.forgerock.opendj.ldap.Connection; 068import org.forgerock.opendj.ldap.LDAPConnectionFactory; 069import org.forgerock.opendj.ldap.LdapException; 070import org.forgerock.opendj.ldap.ResultCode; 071import org.forgerock.opendj.ldap.SSLContextBuilder; 072import org.forgerock.opendj.ldap.TrustManagers; 073import org.forgerock.util.Options; 074import org.forgerock.util.time.Duration; 075import org.opends.admin.ads.util.ApplicationTrustManager; 076import org.opends.guitools.controlpanel.datamodel.BackendDescriptor; 077import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor; 078import org.opends.guitools.controlpanel.datamodel.BaseDNTableModel; 079import org.opends.guitools.controlpanel.datamodel.ConfigReadException; 080import org.opends.guitools.controlpanel.datamodel.ConnectionHandlerDescriptor; 081import org.opends.guitools.controlpanel.datamodel.ConnectionHandlerTableModel; 082import org.opends.guitools.controlpanel.datamodel.ConnectionProtocolPolicy; 083import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 084import org.opends.guitools.controlpanel.datamodel.ServerDescriptor; 085import org.opends.guitools.controlpanel.util.ControlPanelLog; 086import org.opends.guitools.controlpanel.util.Utilities; 087import org.opends.server.admin.client.cli.SecureConnectionCliArgs; 088import org.opends.server.types.DN; 089import org.opends.server.types.InitializationException; 090import org.opends.server.types.NullOutputStream; 091import org.opends.server.types.OpenDsException; 092import org.opends.server.util.BuildVersion; 093import org.opends.server.util.StaticUtils; 094import org.opends.server.util.cli.LDAPConnectionConsoleInteraction; 095 096import com.forgerock.opendj.cli.ArgumentException; 097import com.forgerock.opendj.cli.CliConstants; 098import com.forgerock.opendj.cli.ClientException; 099import com.forgerock.opendj.cli.ConsoleApplication; 100import com.forgerock.opendj.cli.ReturnCode; 101import com.forgerock.opendj.cli.TableBuilder; 102import com.forgerock.opendj.cli.TextTablePrinter; 103 104/** 105 * The class used to provide some CLI interface to display status. 106 * This class basically is in charge of parsing the data provided by the 107 * user in the command line. 108 */ 109public class StatusCli extends ConsoleApplication 110{ 111 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 112 113 private boolean displayMustAuthenticateLegend; 114 private boolean displayMustStartLegend; 115 116 /** Prefix for log files. */ 117 public static final String LOG_FILE_PREFIX = "opendj-status-"; 118 /** Suffix for log files. */ 119 public static final String LOG_FILE_SUFFIX = ".log"; 120 121 private ApplicationTrustManager interactiveTrustManager; 122 private boolean useInteractiveTrustManager; 123 124 /** The argument parser. */ 125 private StatusCliArgumentParser argParser; 126 127 /** 128 * Constructor for the status cli object. 129 * 130 * @param out 131 * The print stream to use for standard output. 132 * @param err 133 * The print stream to use for standard error. 134 * @param in 135 * The input stream to use for standard input. 136 */ 137 public StatusCli(PrintStream out, PrintStream err, InputStream in) 138 { 139 super(out, err); 140 } 141 142 /** 143 * The main method for the status CLI tool. 144 * 145 * @param args The command-line arguments provided to this program. 146 */ 147 148 public static void main(String[] args) 149 { 150 int retCode = mainCLI(args, true, System.out, System.err, System.in); 151 if(retCode != 0) 152 { 153 System.exit(retCode); 154 } 155 } 156 157 /** 158 * Parses the provided command-line arguments and uses that information to 159 * run the status tool. 160 * 161 * @param args the command-line arguments provided to this program. 162 * 163 * @return The return code. 164 */ 165 166 public static int mainCLI(String[] args) 167 { 168 return mainCLI(args, true, System.out, System.err, System.in); 169 } 170 171 /** 172 * Parses the provided command-line arguments and uses that information to run 173 * the status tool. 174 * 175 * @param args 176 * The command-line arguments provided to this program. 177 * @param initializeServer 178 * Indicates whether to initialize the server. 179 * @param outStream 180 * The output stream to use for standard output, or {@code null} 181 * if standard output is not needed. 182 * @param errStream 183 * The output stream to use for standard error, or {@code null} 184 * if standard error is not needed. 185 * @param inStream 186 * The input stream to use for standard input. 187 * @return The return code. 188 */ 189 public static int mainCLI(String[] args, boolean initializeServer, 190 OutputStream outStream, OutputStream errStream, InputStream inStream) 191 { 192 PrintStream out = NullOutputStream.wrapOrNullStream(outStream); 193 PrintStream err = NullOutputStream.wrapOrNullStream(errStream); 194 195 try { 196 ControlPanelLog.initLogFileHandler( 197 File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX)); 198 ControlPanelLog.initPackage("org.opends.server.tools.status"); 199 } catch (Throwable t) { 200 System.err.println("Unable to initialize log"); 201 t.printStackTrace(); 202 } 203 204 final StatusCli statusCli = new StatusCli(out, err, inStream); 205 int retCode = statusCli.execute(args); 206 if (retCode == 0) 207 { 208 ControlPanelLog.closeAndDeleteLogFile(); 209 } 210 return retCode; 211 } 212 213 /** 214 * Parses the provided command-line arguments and uses that information to run 215 * the status CLI. 216 * 217 * @param args 218 * The command-line arguments provided to this program. 219 * @return The return code of the process. 220 */ 221 public int execute(String[] args) { 222 argParser = new StatusCliArgumentParser(StatusCli.class.getName()); 223 try { 224 argParser.initializeGlobalArguments(getOutputStream()); 225 } catch (ArgumentException ae) { 226 println(ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 227 return ReturnCode.CLIENT_SIDE_PARAM_ERROR.get(); 228 } 229 230 try 231 { 232 argParser.getSecureArgsList().initArgumentsWithConfiguration(); 233 } 234 catch (ConfigException ce) 235 { 236 // Ignore. 237 } 238 239 // Validate user provided data 240 try { 241 argParser.parseArguments(args); 242 } catch (ArgumentException ae) { 243 argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 244 return ReturnCode.CLIENT_SIDE_PARAM_ERROR.get(); 245 } 246 247 // If we should just display usage or version information, 248 // then print it and exit. 249 if (argParser.usageOrVersionDisplayed()) { 250 return ReturnCode.SUCCESS.get(); 251 } 252 253 // Checks the version - if upgrade required, the tool is unusable 254 try 255 { 256 BuildVersion.checkVersionMismatch(); 257 } 258 catch (InitializationException e) 259 { 260 println(e.getMessageObject()); 261 return 1; 262 } 263 264 int v = argParser.validateGlobalOptions(getErrorStream()); 265 if (v != ReturnCode.SUCCESS.get()) { 266 println(LocalizableMessage.raw(argParser.getUsage())); 267 return v; 268 } 269 270 final ControlPanelInfo controlInfo = ControlPanelInfo.getInstance(); 271 controlInfo.setTrustManager(getTrustManager()); 272 controlInfo.setConnectTimeout(argParser.getConnectTimeout()); 273 controlInfo.regenerateDescriptor(); 274 275 if (controlInfo.getServerDescriptor().getStatus() == ServerDescriptor.ServerStatus.STARTED) 276 { 277 String bindDn = null; 278 String bindPwd = null; 279 280 ManagementContext mContext = null; 281 282 // This is done because we do not need to ask the user about these 283 // parameters. We force their presence in the 284 // LDAPConnectionConsoleInteraction, this done, it will not prompt 285 // the user for them. 286 final SecureConnectionCliArgs secureArgsList = argParser.getSecureArgsList(); 287 controlInfo.setConnectionPolicy(ConnectionProtocolPolicy.USE_ADMIN); 288 int port = CliConstants.DEFAULT_ADMINISTRATION_CONNECTOR_PORT; 289 controlInfo.setConnectionPolicy(ConnectionProtocolPolicy.USE_ADMIN); 290 String ldapUrl = controlInfo.getURLToConnect(); 291 try 292 { 293 final URI uri = new URI(ldapUrl); 294 port = uri.getPort(); 295 } 296 catch (Throwable t) 297 { 298 logger.error(LocalizableMessage.raw("Error parsing url: " + ldapUrl)); 299 } 300 secureArgsList.hostNameArg.setPresent(true); 301 secureArgsList.portArg.setPresent(true); 302 secureArgsList.hostNameArg.addValue(secureArgsList.hostNameArg.getDefaultValue()); 303 secureArgsList.portArg.addValue(Integer.toString(port)); 304 // We already know if SSL or StartTLS can be used. If we cannot 305 // use them we will not propose them in the connection parameters 306 // and if none of them can be used we will just not ask for the 307 // protocol to be used. 308 final LDAPConnectionConsoleInteraction ci = 309 new LDAPConnectionConsoleInteraction(this, argParser.getSecureArgsList()); 310 try 311 { 312 ci.run(false); 313 } 314 catch (ArgumentException e) 315 { 316 argParser.displayMessageAndUsageReference(getErrStream(), e.getMessageObject()); 317 return ReturnCode.CLIENT_SIDE_PARAM_ERROR.get(); 318 } 319 try 320 { 321 if (argParser.isInteractive()) 322 { 323 bindDn = ci.getBindDN(); 324 bindPwd = ci.getBindPassword(); 325 } 326 else 327 { 328 bindDn = argParser.getBindDN(); 329 bindPwd = argParser.getBindPassword(); 330 } 331 if (bindPwd != null && !bindPwd.isEmpty()) 332 { 333 mContext = getManagementContextFromConnection(ci); 334 interactiveTrustManager = ci.getTrustManager(); 335 controlInfo.setTrustManager(interactiveTrustManager); 336 useInteractiveTrustManager = true; 337 } 338 } catch (ClientException e) { 339 println(e.getMessageObject()); 340 return ReturnCode.CLIENT_SIDE_PARAM_ERROR.get(); 341 } finally { 342 closeSilently(mContext); 343 } 344 345 if (mContext != null) 346 { 347 InitialLdapContext ctx = null; 348 try { 349 ctx = Utilities.getAdminDirContext(controlInfo, bindDn, bindPwd); 350 controlInfo.setDirContext(ctx); 351 controlInfo.regenerateDescriptor(); 352 writeStatus(controlInfo); 353 354 if (!controlInfo.getServerDescriptor().getExceptions().isEmpty()) { 355 return ReturnCode.ERROR_INITIALIZING_SERVER.get(); 356 } 357 } catch (NamingException ne) { 358 // This should not happen but this is useful information to 359 // diagnose the error. 360 println(); 361 println(INFO_ERROR_READING_SERVER_CONFIGURATION.get(ne)); 362 return ReturnCode.ERROR_INITIALIZING_SERVER.get(); 363 } catch (ConfigReadException cre) { 364 // This should not happen but this is useful information to 365 // diagnose the error. 366 println(); 367 println(cre.getMessageObject()); 368 return ReturnCode.ERROR_INITIALIZING_SERVER.get(); 369 } finally { 370 StaticUtils.close(ctx); 371 } 372 } else { 373 // The user did not provide authentication: just display the 374 // information we can get reading the config file. 375 writeStatus(controlInfo); 376 return ReturnCode.ERROR_USER_CANCELLED.get(); 377 } 378 } else { 379 writeStatus(controlInfo); 380 } 381 382 return ReturnCode.SUCCESS.get(); 383 } 384 385 private void writeStatus(ControlPanelInfo controlInfo) 386 { 387 if (controlInfo.getServerDescriptor() == null) 388 { 389 controlInfo.regenerateDescriptor(); 390 } 391 writeStatus(controlInfo.getServerDescriptor()); 392 int period = argParser.getRefreshPeriod(); 393 boolean first = true; 394 while (period > 0) 395 { 396 long timeToSleep = period * 1000; 397 if (!first) 398 { 399 long t1 = System.currentTimeMillis(); 400 controlInfo.regenerateDescriptor(); 401 long t2 = System.currentTimeMillis(); 402 403 timeToSleep = timeToSleep - t2 + t1; 404 } 405 406 if (timeToSleep > 0) 407 { 408 StaticUtils.sleep(timeToSleep); 409 } 410 println(); 411 println(LocalizableMessage.raw(" ---------------------")); 412 println(); 413 writeStatus(controlInfo.getServerDescriptor()); 414 first = false; 415 } 416 } 417 418 private void writeStatus(ServerDescriptor desc) 419 { 420 LocalizableMessage[] labels = 421 { 422 INFO_SERVER_STATUS_LABEL.get(), 423 INFO_CONNECTIONS_LABEL.get(), 424 INFO_HOSTNAME_LABEL.get(), 425 INFO_ADMINISTRATIVE_USERS_LABEL.get(), 426 INFO_INSTALLATION_PATH_LABEL.get(), 427 INFO_OPENDS_VERSION_LABEL.get(), 428 INFO_JAVA_VERSION_LABEL.get(), 429 INFO_CTRL_PANEL_ADMIN_CONNECTOR_LABEL.get() 430 }; 431 int labelWidth = 0; 432 LocalizableMessage title = INFO_SERVER_STATUS_TITLE.get(); 433 if (!isScriptFriendly()) 434 { 435 for (LocalizableMessage label : labels) 436 { 437 labelWidth = Math.max(labelWidth, label.length()); 438 } 439 println(); 440 println(centerTitle(title)); 441 } 442 writeStatusContents(desc, labelWidth); 443 writeCurrentConnectionContents(desc, labelWidth); 444 if (!isScriptFriendly()) 445 { 446 println(); 447 } 448 449 title = INFO_SERVER_DETAILS_TITLE.get(); 450 if (!isScriptFriendly()) 451 { 452 println(centerTitle(title)); 453 } 454 writeHostnameContents(desc, labelWidth); 455 writeAdministrativeUserContents(desc, labelWidth); 456 writeInstallPathContents(desc, labelWidth); 457 boolean sameInstallAndInstance = desc.sameInstallAndInstance(); 458 if (!sameInstallAndInstance) 459 { 460 writeInstancePathContents(desc, labelWidth); 461 } 462 writeVersionContents(desc, labelWidth); 463 writeJavaVersionContents(desc, labelWidth); 464 writeAdminConnectorContents(desc, labelWidth); 465 if (!isScriptFriendly()) 466 { 467 println(); 468 } 469 470 writeListenerContents(desc); 471 if (!isScriptFriendly()) 472 { 473 println(); 474 } 475 476 writeBaseDNContents(desc); 477 478 writeErrorContents(desc); 479 480 if (!isScriptFriendly()) 481 { 482 if (displayMustStartLegend) 483 { 484 println(); 485 println(INFO_NOT_AVAILABLE_SERVER_DOWN_CLI_LEGEND.get()); 486 } 487 else if (displayMustAuthenticateLegend) 488 { 489 println(); 490 println(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_CLI_LEGEND.get()); 491 } 492 } 493 println(); 494 } 495 496 /** 497 * Writes the status contents displaying with what is specified in the 498 * provided ServerDescriptor object. 499 * 500 * @param desc 501 * The ServerStatusDescriptor object. 502 */ 503 private void writeStatusContents(ServerDescriptor desc, int maxLabelWidth) 504 { 505 writeLabelValue(INFO_SERVER_STATUS_LABEL.get(), getStatus(desc).toString(), maxLabelWidth); 506 } 507 508 private LocalizableMessage getStatus(ServerDescriptor desc) 509 { 510 switch (desc.getStatus()) 511 { 512 case STARTED: 513 return INFO_SERVER_STARTED_LABEL.get(); 514 case STOPPED: 515 return INFO_SERVER_STOPPED_LABEL.get(); 516 case STARTING: 517 return INFO_SERVER_STARTING_LABEL.get(); 518 case STOPPING: 519 return INFO_SERVER_STOPPING_LABEL.get(); 520 case NOT_CONNECTED_TO_REMOTE: 521 return INFO_SERVER_NOT_CONNECTED_TO_REMOTE_STATUS_LABEL.get(); 522 case UNKNOWN: 523 return INFO_SERVER_UNKNOWN_STATUS_LABEL.get(); 524 default: 525 throw new IllegalStateException("Unknown status: "+desc.getStatus()); 526 } 527 } 528 529 /** 530 * Writes the current connection contents displaying with what is specified in 531 * the provided ServerDescriptor object. 532 * 533 * @param desc 534 * The ServerDescriptor object. 535 */ 536 private void writeCurrentConnectionContents(ServerDescriptor desc, int maxLabelWidth) 537 { 538 writeLabelValue(INFO_CONNECTIONS_LABEL.get(), getNbConnection(desc), maxLabelWidth); 539 } 540 541 private String getNbConnection(ServerDescriptor desc) 542 { 543 if (desc.getStatus() == ServerDescriptor.ServerStatus.STARTED) 544 { 545 final int nConn = desc.getOpenConnections(); 546 if (nConn >= 0) 547 { 548 return String.valueOf(nConn); 549 } 550 else if (!desc.isAuthenticated() || !desc.getExceptions().isEmpty()) 551 { 552 return getNotAvailableBecauseAuthenticationIsRequiredText(); 553 } 554 else 555 { 556 return getNotAvailableText(); 557 } 558 } 559 return getNotAvailableBecauseServerIsDownText(); 560 } 561 562 /** 563 * Writes the host name contents. 564 * 565 * @param desc 566 * The ServerDescriptor object. 567 * @param maxLabelWidth 568 * The maximum label width of the left label. 569 */ 570 private void writeHostnameContents(ServerDescriptor desc, int maxLabelWidth) 571 { 572 writeLabelValue(INFO_HOSTNAME_LABEL.get(), desc.getHostname(), maxLabelWidth); 573 } 574 575 /** 576 * Writes the administrative user contents displaying with what is specified 577 * in the provided ServerStatusDescriptor object. 578 * 579 * @param desc 580 * The ServerStatusDescriptor object. 581 * @param maxLabelWidth 582 * The maximum label width of the left label. 583 */ 584 private void writeAdministrativeUserContents(ServerDescriptor desc, int maxLabelWidth) 585 { 586 Set<DN> administrators = desc.getAdministrativeUsers(); 587 if (!administrators.isEmpty()) 588 { 589 TreeSet<DN> ordered = new TreeSet<>(administrators); 590 for (DN dn : ordered) 591 { 592 writeLabelValue(INFO_ADMINISTRATIVE_USERS_LABEL.get(), dn.toString(), maxLabelWidth); 593 } 594 } 595 else 596 { 597 writeLabelValue(INFO_ADMINISTRATIVE_USERS_LABEL.get(), getErrorText(desc), maxLabelWidth); 598 } 599 } 600 601 private String getErrorText(ServerDescriptor desc) 602 { 603 if (desc.getStatus() == ServerDescriptor.ServerStatus.STARTED 604 && (!desc.isAuthenticated() || !desc.getExceptions().isEmpty())) 605 { 606 return getNotAvailableBecauseAuthenticationIsRequiredText(); 607 } 608 return getNotAvailableText(); 609 } 610 611 /** 612 * Writes the install path contents displaying with what is specified in the 613 * provided ServerDescriptor object. 614 * 615 * @param desc 616 * The ServerDescriptor object. 617 * @param maxLabelWidth 618 * The maximum label width of the left label. 619 */ 620 private void writeInstallPathContents(ServerDescriptor desc, int maxLabelWidth) 621 { 622 writeLabelValue(INFO_INSTALLATION_PATH_LABEL.get(), desc.getInstallPath(), maxLabelWidth); 623 } 624 625 /** 626 * Writes the instance path contents displaying with what is specified in the 627 * provided ServerDescriptor object. 628 * 629 * @param desc 630 * The ServerDescriptor object. 631 * @param maxLabelWidth 632 * The maximum label width of the left label. 633 */ 634 private void writeInstancePathContents(ServerDescriptor desc, int maxLabelWidth) 635 { 636 writeLabelValue(INFO_CTRL_PANEL_INSTANCE_PATH_LABEL.get(), desc.getInstancePath(), maxLabelWidth); 637 } 638 639 /** 640 * Updates the server version contents displaying with what is specified in 641 * the provided ServerDescriptor object. This method must be called from the 642 * event thread. 643 * 644 * @param desc 645 * The ServerDescriptor object. 646 */ 647 private void writeVersionContents(ServerDescriptor desc, int maxLabelWidth) 648 { 649 writeLabelValue(INFO_OPENDS_VERSION_LABEL.get(), desc.getOpenDSVersion(), maxLabelWidth); 650 } 651 652 /** 653 * Updates the java version contents displaying with what is specified in the 654 * provided ServerDescriptor object. This method must be called from the event 655 * thread. 656 * 657 * @param desc 658 * The ServerDescriptor object. 659 * @param maxLabelWidth 660 * The maximum label width of the left label. 661 */ 662 private void writeJavaVersionContents(ServerDescriptor desc, int maxLabelWidth) 663 { 664 writeLabelValue(INFO_JAVA_VERSION_LABEL.get(), getJavaVersion(desc), maxLabelWidth); 665 } 666 667 private String getJavaVersion(ServerDescriptor desc) 668 { 669 if (desc.getStatus() == ServerDescriptor.ServerStatus.STARTED) 670 { 671 if (!desc.isAuthenticated() || !desc.getExceptions().isEmpty()) 672 { 673 return getNotAvailableBecauseAuthenticationIsRequiredText(); 674 } 675 return desc.getJavaVersion(); 676 } 677 return getNotAvailableBecauseServerIsDownText(); 678 } 679 680 /** 681 * Updates the admin connector contents displaying with what is specified in 682 * the provided ServerDescriptor object. This method must be called from the 683 * event thread. 684 * 685 * @param desc 686 * The ServerDescriptor object. 687 * @param maxLabelWidth 688 * The maximum label width of the left label. 689 */ 690 private void writeAdminConnectorContents(ServerDescriptor desc, int maxLabelWidth) 691 { 692 ConnectionHandlerDescriptor adminConnector = desc.getAdminConnector(); 693 LocalizableMessage text = adminConnector != null 694 ? INFO_CTRL_PANEL_ADMIN_CONNECTOR_DESCRIPTION.get(adminConnector.getPort()) 695 : INFO_NOT_AVAILABLE_SHORT_LABEL.get(); 696 writeLabelValue(INFO_CTRL_PANEL_ADMIN_CONNECTOR_LABEL.get(), text.toString(), maxLabelWidth); 697 } 698 699 /** 700 * Writes the listeners contents displaying with what is specified in the 701 * provided ServerDescriptor object. 702 * 703 * @param desc 704 * The ServerDescriptor object. 705 */ 706 private void writeListenerContents(ServerDescriptor desc) 707 { 708 if (!isScriptFriendly()) 709 { 710 LocalizableMessage title = INFO_LISTENERS_TITLE.get(); 711 println(centerTitle(title)); 712 } 713 714 Set<ConnectionHandlerDescriptor> allHandlers = desc.getConnectionHandlers(); 715 if (allHandlers.isEmpty()) 716 { 717 if (desc.getStatus() == ServerDescriptor.ServerStatus.STARTED) 718 { 719 if (!desc.isAuthenticated()) 720 { 721 println(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_CLI_LABEL.get()); 722 } 723 else 724 { 725 println(INFO_NO_LISTENERS_FOUND.get()); 726 } 727 } 728 else 729 { 730 println(INFO_NO_LISTENERS_FOUND.get()); 731 } 732 } 733 else 734 { 735 ConnectionHandlerTableModel connHandlersTableModel = 736 new ConnectionHandlerTableModel(false); 737 connHandlersTableModel.setData(allHandlers); 738 writeConnectionHandlersTableModel(connHandlersTableModel, desc); 739 } 740 } 741 742 /** 743 * Writes the base DN contents displaying with what is specified in the 744 * provided ServerDescriptor object. 745 * 746 * @param desc 747 * The ServerDescriptor object. 748 */ 749 private void writeBaseDNContents(ServerDescriptor desc) 750 { 751 LocalizableMessage title = INFO_DATABASES_TITLE.get(); 752 if (!isScriptFriendly()) 753 { 754 println(centerTitle(title)); 755 } 756 757 Set<BaseDNDescriptor> replicas = new HashSet<>(); 758 Set<BackendDescriptor> bs = desc.getBackends(); 759 for (BackendDescriptor backend: bs) 760 { 761 if (!backend.isConfigBackend()) 762 { 763 replicas.addAll(backend.getBaseDns()); 764 } 765 } 766 if (replicas.isEmpty()) 767 { 768 if (desc.getStatus() == ServerDescriptor.ServerStatus.STARTED) 769 { 770 if (!desc.isAuthenticated()) 771 { 772 println(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_CLI_LABEL.get()); 773 } 774 else 775 { 776 println(INFO_NO_DBS_FOUND.get()); 777 } 778 } 779 else 780 { 781 println(INFO_NO_DBS_FOUND.get()); 782 } 783 } 784 else 785 { 786 BaseDNTableModel baseDNTableModel = new BaseDNTableModel(true, false); 787 baseDNTableModel.setData(replicas, desc.getStatus(), desc.isAuthenticated()); 788 789 writeBaseDNTableModel(baseDNTableModel, desc); 790 } 791 } 792 793 /** 794 * Writes the error label contents displaying with what is specified in the 795 * provided ServerDescriptor object. 796 * 797 * @param desc 798 * The ServerDescriptor object. 799 */ 800 private void writeErrorContents(ServerDescriptor desc) 801 { 802 for (OpenDsException ex : desc.getExceptions()) 803 { 804 LocalizableMessage errorMsg = ex.getMessageObject(); 805 if (errorMsg != null) 806 { 807 println(); 808 println(errorMsg); 809 } 810 } 811 } 812 813 /** 814 * Returns the not available text explaining that the data is not available 815 * because the server is down. 816 * 817 * @return the text. 818 */ 819 private String getNotAvailableBecauseServerIsDownText() 820 { 821 displayMustStartLegend = true; 822 return INFO_NOT_AVAILABLE_SERVER_DOWN_CLI_LABEL.get().toString(); 823 } 824 825 /** 826 * Returns the not available text explaining that the data is not available 827 * because authentication is required. 828 * 829 * @return the text. 830 */ 831 private String getNotAvailableBecauseAuthenticationIsRequiredText() 832 { 833 displayMustAuthenticateLegend = true; 834 return INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_CLI_LABEL.get().toString(); 835 } 836 837 /** 838 * Returns the not available text explaining that the data is not available. 839 * 840 * @return the text. 841 */ 842 private String getNotAvailableText() 843 { 844 return INFO_NOT_AVAILABLE_LABEL.get().toString(); 845 } 846 847 /** 848 * Writes the contents of the provided table model simulating a table layout 849 * using text. 850 * 851 * @param tableModel 852 * The connection handler table model. 853 * @param desc 854 * The Server Status descriptor. 855 */ 856 private void writeConnectionHandlersTableModel( 857 ConnectionHandlerTableModel tableModel, 858 ServerDescriptor desc) 859 { 860 if (isScriptFriendly()) 861 { 862 for (int i=0; i<tableModel.getRowCount(); i++) 863 { 864 // Get the host name, it can be multivalued. 865 String[] hostNames = getHostNames(tableModel, i); 866 for (String hostName : hostNames) 867 { 868 println(LocalizableMessage.raw("-")); 869 for (int j=0; j<tableModel.getColumnCount(); j++) 870 { 871 LocalizableMessageBuilder line = new LocalizableMessageBuilder(); 872 line.append(tableModel.getColumnName(j)).append(": "); 873 if (j == 0) 874 { 875 // It is the hostName 876 line.append(getCellValue(hostName, desc)); 877 } 878 else 879 { 880 line.append(getCellValue(tableModel.getValueAt(i, j), desc)); 881 } 882 println(line.toMessage()); 883 } 884 } 885 } 886 } 887 else 888 { 889 TableBuilder table = new TableBuilder(); 890 for (int i=0; i< tableModel.getColumnCount(); i++) 891 { 892 table.appendHeading(LocalizableMessage.raw(tableModel.getColumnName(i))); 893 } 894 for (int i=0; i<tableModel.getRowCount(); i++) 895 { 896 // Get the host name, it can be multivalued. 897 String[] hostNames = getHostNames(tableModel, i); 898 for (String hostName : hostNames) 899 { 900 table.startRow(); 901 for (int j=0; j<tableModel.getColumnCount(); j++) 902 { 903 if (j == 0) 904 { 905 // It is the hostName 906 table.appendCell(getCellValue(hostName, desc)); 907 } 908 else 909 { 910 table.appendCell(getCellValue(tableModel.getValueAt(i, j), desc)); 911 } 912 } 913 } 914 } 915 TextTablePrinter printer = new TextTablePrinter(getOutputStream()); 916 printer.setColumnSeparator(LIST_TABLE_SEPARATOR); 917 table.print(printer); 918 } 919 } 920 921 private String[] getHostNames(ConnectionHandlerTableModel tableModel, int row) 922 { 923 String v = (String)tableModel.getValueAt(row, 0); 924 String htmlTag = "<html>"; 925 if (v.toLowerCase().startsWith(htmlTag)) 926 { 927 v = v.substring(htmlTag.length()); 928 } 929 return v.split("<br>"); 930 } 931 932 private String getCellValue(Object v, ServerDescriptor desc) 933 { 934 if (v != null) 935 { 936 if (v instanceof String) 937 { 938 return (String) v; 939 } 940 else if (v instanceof Integer) 941 { 942 int nEntries = ((Integer)v).intValue(); 943 if (nEntries >= 0) 944 { 945 return String.valueOf(nEntries); 946 } 947 else if (!desc.isAuthenticated() || !desc.getExceptions().isEmpty()) 948 { 949 return getNotAvailableBecauseAuthenticationIsRequiredText(); 950 } 951 else 952 { 953 return getNotAvailableText(); 954 } 955 } 956 else 957 { 958 throw new IllegalStateException("Unknown object type: "+v); 959 } 960 } 961 return getNotAvailableText(); 962 } 963 964 /** 965 * Writes the contents of the provided base DN table model. Every base DN is 966 * written in a block containing pairs of labels and values. 967 * 968 * @param tableModel 969 * The TableModel. 970 * @param desc 971 * The Server Status descriptor. 972 */ 973 private void writeBaseDNTableModel(BaseDNTableModel tableModel, ServerDescriptor desc) 974 { 975 boolean isRunning = desc.getStatus() == ServerDescriptor.ServerStatus.STARTED; 976 977 int labelWidth = 0; 978 int labelWidthWithoutReplicated = 0; 979 LocalizableMessage[] labels = new LocalizableMessage[tableModel.getColumnCount()]; 980 for (int i=0; i<tableModel.getColumnCount(); i++) 981 { 982 LocalizableMessage header = LocalizableMessage.raw(tableModel.getColumnName(i)); 983 labels[i] = new LocalizableMessageBuilder(header).append(":").toMessage(); 984 labelWidth = Math.max(labelWidth, labels[i].length()); 985 if (i != 4 && i != 5) 986 { 987 labelWidthWithoutReplicated = 988 Math.max(labelWidthWithoutReplicated, labels[i].length()); 989 } 990 } 991 992 LocalizableMessage replicatedLabel = INFO_BASEDN_REPLICATED_LABEL.get(); 993 for (int i=0; i<tableModel.getRowCount(); i++) 994 { 995 if (isScriptFriendly()) 996 { 997 println(LocalizableMessage.raw("-")); 998 } 999 else if (i > 0) 1000 { 1001 println(); 1002 } 1003 for (int j=0; j<tableModel.getColumnCount(); j++) 1004 { 1005 Object v = tableModel.getValueAt(i, j); 1006 String value = getValue(desc, isRunning, v); 1007 1008 boolean doWrite = true; 1009 boolean isReplicated = 1010 replicatedLabel.toString().equals( 1011 String.valueOf(tableModel.getValueAt(i, 3))); 1012 if (j == 4 || j == 5) 1013 { 1014 // If the suffix is not replicated we do not have to display these lines 1015 doWrite = isReplicated; 1016 } 1017 if (doWrite) 1018 { 1019 writeLabelValue(labels[j], value, 1020 isReplicated?labelWidth:labelWidthWithoutReplicated); 1021 } 1022 } 1023 } 1024 } 1025 1026 private String getValue(ServerDescriptor desc, boolean isRunning, Object v) 1027 { 1028 if (v != null) 1029 { 1030 if (v == BaseDNTableModel.NOT_AVAILABLE_SERVER_DOWN) 1031 { 1032 return getNotAvailableBecauseServerIsDownText(); 1033 } 1034 else if (v == BaseDNTableModel.NOT_AVAILABLE_AUTHENTICATION_REQUIRED) 1035 { 1036 return getNotAvailableBecauseAuthenticationIsRequiredText(); 1037 } 1038 else if (v == BaseDNTableModel.NOT_AVAILABLE) 1039 { 1040 return getNotAvailableText(desc, isRunning); 1041 } 1042 else if (v instanceof String) 1043 { 1044 return (String) v; 1045 } 1046 else if (v instanceof LocalizableMessage) 1047 { 1048 return ((LocalizableMessage) v).toString(); 1049 } 1050 else if (v instanceof Integer) 1051 { 1052 final int nEntries = ((Integer) v).intValue(); 1053 if (nEntries >= 0) 1054 { 1055 return String.valueOf(nEntries); 1056 } 1057 return getNotAvailableText(desc, isRunning); 1058 } 1059 else 1060 { 1061 throw new IllegalStateException("Unknown object type: " + v); 1062 } 1063 } 1064 return ""; 1065 } 1066 1067 private String getNotAvailableText(ServerDescriptor desc, boolean isRunning) 1068 { 1069 if (!isRunning) 1070 { 1071 return getNotAvailableBecauseServerIsDownText(); 1072 } 1073 if (!desc.isAuthenticated() || !desc.getExceptions().isEmpty()) 1074 { 1075 return getNotAvailableBecauseAuthenticationIsRequiredText(); 1076 } 1077 return getNotAvailableText(); 1078 } 1079 1080 private void writeLabelValue(final LocalizableMessage label, final String value, final int maxLabelWidth) 1081 { 1082 final LocalizableMessageBuilder buf = new LocalizableMessageBuilder(); 1083 buf.append(label); 1084 1085 int extra = maxLabelWidth - label.length(); 1086 for (int i = 0; i<extra; i++) 1087 { 1088 buf.append(" "); 1089 } 1090 buf.append(" ").append(value); 1091 println(buf.toMessage()); 1092 } 1093 1094 private LocalizableMessage centerTitle(final LocalizableMessage text) 1095 { 1096 if (text.length() <= MAX_LINE_WIDTH - 8) 1097 { 1098 final LocalizableMessageBuilder buf = new LocalizableMessageBuilder(); 1099 int extra = Math.min(10, 1100 (MAX_LINE_WIDTH - 8 - text.length()) / 2); 1101 for (int i=0; i<extra; i++) 1102 { 1103 buf.append(" "); 1104 } 1105 buf.append("--- ").append(text).append(" ---"); 1106 return buf.toMessage(); 1107 } 1108 return text; 1109 } 1110 1111 /** 1112 * Returns the trust manager to be used by this application. 1113 * 1114 * @return the trust manager to be used by this application. 1115 */ 1116 private ApplicationTrustManager getTrustManager() 1117 { 1118 if (useInteractiveTrustManager) 1119 { 1120 return interactiveTrustManager; 1121 } 1122 return argParser.getTrustManager(); 1123 } 1124 1125 /** {@inheritDoc} */ 1126 @Override 1127 public boolean isAdvancedMode() 1128 { 1129 return false; 1130 } 1131 1132 /** {@inheritDoc} */ 1133 @Override 1134 public boolean isInteractive() { 1135 return argParser.isInteractive(); 1136 } 1137 1138 /** {@inheritDoc} */ 1139 @Override 1140 public boolean isMenuDrivenMode() { 1141 return true; 1142 } 1143 1144 /** {@inheritDoc} */ 1145 @Override 1146 public boolean isQuiet() { 1147 return false; 1148 } 1149 1150 /** {@inheritDoc} */ 1151 @Override 1152 public boolean isScriptFriendly() { 1153 return argParser.isScriptFriendly(); 1154 } 1155 1156 /** {@inheritDoc} */ 1157 @Override 1158 public boolean isVerbose() { 1159 return true; 1160 } 1161 1162 /** FIXME Common code with DSConfigand tools*. This method needs to be moved. */ 1163 private ManagementContext getManagementContextFromConnection( 1164 final LDAPConnectionConsoleInteraction ci) throws ClientException 1165 { 1166 // Interact with the user though the console to get 1167 // LDAP connection information 1168 final String hostName = getHostNameForLdapUrl(ci.getHostName()); 1169 final Integer portNumber = ci.getPortNumber(); 1170 final String bindDN = ci.getBindDN(); 1171 final String bindPassword = ci.getBindPassword(); 1172 TrustManager trustManager = ci.getTrustManager(); 1173 final KeyManager keyManager = ci.getKeyManager(); 1174 1175 // This connection should always be secure. useSSL = true. 1176 Connection connection = null; 1177 final Options options = Options.defaultOptions(); 1178 options.set(CONNECT_TIMEOUT, new Duration((long) ci.getConnectTimeout(), TimeUnit.MILLISECONDS)); 1179 LDAPConnectionFactory factory = null; 1180 while (true) 1181 { 1182 try 1183 { 1184 final SSLContextBuilder sslBuilder = new SSLContextBuilder(); 1185 sslBuilder.setTrustManager(trustManager == null ? TrustManagers.trustAll() : trustManager); 1186 sslBuilder.setKeyManager(keyManager); 1187 options.set(SSL_USE_STARTTLS, ci.useStartTLS()); 1188 options.set(SSL_CONTEXT, sslBuilder.getSSLContext()); 1189 1190 factory = new LDAPConnectionFactory(hostName, portNumber, options); 1191 connection = factory.getConnection(); 1192 connection.bind(bindDN, bindPassword.toCharArray()); 1193 break; 1194 } 1195 catch (LdapException e) 1196 { 1197 if (ci.isTrustStoreInMemory() && e.getCause() instanceof SSLException 1198 && e.getCause().getCause() instanceof CertificateException) 1199 { 1200 String authType = null; 1201 if (trustManager instanceof ApplicationTrustManager) 1202 { // FIXME use PromptingTrustManager 1203 ApplicationTrustManager appTrustManager = 1204 (ApplicationTrustManager) trustManager; 1205 authType = appTrustManager.getLastRefusedAuthType(); 1206 X509Certificate[] cert = appTrustManager.getLastRefusedChain(); 1207 1208 if (ci.checkServerCertificate(cert, authType, hostName)) 1209 { 1210 // If the certificate is trusted, update the trust manager. 1211 trustManager = ci.getTrustManager(); 1212 // Try to connect again. 1213 continue; 1214 } 1215 } 1216 } 1217 if (e.getCause() instanceof SSLException) 1218 { 1219 LocalizableMessage message = 1220 ERR_FAILED_TO_CONNECT_NOT_TRUSTED.get(hostName, portNumber); 1221 throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, 1222 message); 1223 } 1224 if (e.getCause() instanceof AuthorizationException) 1225 { 1226 throw new ClientException(ReturnCode.AUTH_METHOD_NOT_SUPPORTED, 1227 ERR_SIMPLE_BIND_NOT_SUPPORTED.get()); 1228 } 1229 else if (e.getCause() instanceof AuthenticationException 1230 || e.getResult().getResultCode() == ResultCode.INVALID_CREDENTIALS) 1231 { 1232 // Status Cli must not fail when un-authenticated. 1233 return null; 1234 } 1235 throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, 1236 ERR_FAILED_TO_CONNECT.get(hostName, portNumber)); 1237 } 1238 catch (GeneralSecurityException e) 1239 { 1240 LocalizableMessage message = 1241 ERR_FAILED_TO_CONNECT.get(hostName, portNumber); 1242 throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message); 1243 } 1244 finally 1245 { 1246 closeSilently(factory, connection); 1247 } 1248 } 1249 1250 return LDAPManagementContext.newManagementContext(connection, LDAPProfile.getInstance()); 1251 } 1252}