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