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