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