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 2008-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.guitools.controlpanel.task; 028 029import static com.forgerock.opendj.cli.Utils.*; 030import static com.forgerock.opendj.util.OperatingSystem.*; 031 032import static org.opends.messages.AdminToolMessages.*; 033 034import java.io.File; 035import java.util.ArrayList; 036import java.util.Collection; 037import java.util.List; 038import java.util.Map; 039import java.util.Objects; 040import java.util.Set; 041 042import javax.naming.NamingException; 043import javax.naming.directory.Attribute; 044import javax.naming.directory.DirContext; 045import javax.naming.directory.ModificationItem; 046import javax.naming.ldap.InitialLdapContext; 047 048import org.forgerock.i18n.LocalizableMessage; 049import org.forgerock.opendj.ldap.ByteString; 050import org.opends.admin.ads.util.ConnectionUtils; 051import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 052import org.opends.guitools.controlpanel.datamodel.ServerDescriptor; 053import org.opends.guitools.controlpanel.event.ConfigurationElementCreatedEvent; 054import org.opends.guitools.controlpanel.event.ConfigurationElementCreatedListener; 055import org.opends.guitools.controlpanel.event.PrintStreamListener; 056import org.opends.guitools.controlpanel.ui.ColorAndFontConstants; 057import org.opends.guitools.controlpanel.ui.ProgressDialog; 058import org.opends.guitools.controlpanel.util.ApplicationPrintStream; 059import org.opends.guitools.controlpanel.util.ConfigReader; 060import org.opends.guitools.controlpanel.util.ProcessReader; 061import org.opends.guitools.controlpanel.util.Utilities; 062import org.opends.quicksetup.Installation; 063import org.opends.quicksetup.UserData; 064import org.opends.server.types.DN; 065import org.opends.server.types.Schema; 066import org.opends.server.util.Base64; 067import org.opends.server.util.SetupUtils; 068 069import com.forgerock.opendj.cli.CommandBuilder; 070 071/** 072 * The class used to define a number of common methods and mechanisms for the 073 * tasks that are run in the Control Panel. 074 */ 075public abstract class Task 076{ 077 private static String localHostName = UserData.getDefaultHostName(); 078 private String binDir; 079 /** 080 * The different task types. 081 */ 082 public enum Type 083 { 084 /** 085 * New Base DN creation. 086 */ 087 NEW_BASEDN, 088 /** 089 * New index creation. 090 */ 091 NEW_INDEX, 092 /** 093 * Modification of indexes. 094 */ 095 MODIFY_INDEX, 096 /** 097 * Deletion of indexes. 098 */ 099 DELETE_INDEX, 100 /** 101 * Creation of VLV indexes. 102 */ 103 NEW_VLV_INDEX, 104 /** 105 * Modification of VLV indexes. 106 */ 107 MODIFY_VLV_INDEX, 108 /** 109 * Deletion of VLV indexes. 110 */ 111 DELETE_VLV_INDEX, 112 /** 113 * Import of an LDIF file. 114 */ 115 IMPORT_LDIF, 116 /** 117 * Export of an LDIF file. 118 */ 119 EXPORT_LDIF, 120 /** 121 * Backup. 122 */ 123 BACKUP, 124 /** 125 * Restore. 126 */ 127 RESTORE, 128 /** 129 * Verification of indexes. 130 */ 131 VERIFY_INDEXES, 132 /** 133 * Rebuild of indexes. 134 */ 135 REBUILD_INDEXES, 136 /** 137 * Enabling of Windows Service. 138 */ 139 ENABLE_WINDOWS_SERVICE, 140 /** 141 * Disabling of Windows Service. 142 */ 143 DISABLE_WINDOWS_SERVICE, 144 /** 145 * Starting the server. 146 */ 147 START_SERVER, 148 /** 149 * Stopping the server. 150 */ 151 STOP_SERVER, 152 /** 153 * Updating the java settings for the different command-lines. 154 */ 155 JAVA_SETTINGS_UPDATE, 156 /** 157 * Creating a new element in the schema. 158 */ 159 NEW_SCHEMA_ELEMENT, 160 /** 161 * Deleting an schema element. 162 */ 163 DELETE_SCHEMA_ELEMENT, 164 /** 165 * Modify an schema element. 166 */ 167 MODIFY_SCHEMA_ELEMENT, 168 /** 169 * Modifying an entry. 170 */ 171 MODIFY_ENTRY, 172 /** 173 * Creating an entry. 174 */ 175 NEW_ENTRY, 176 /** 177 * Deleting an entry. 178 */ 179 DELETE_ENTRY, 180 /** 181 * Deleting a base DN. 182 */ 183 DELETE_BASEDN, 184 /** 185 * Deleting a backend. 186 */ 187 DELETE_BACKEND, 188 /** 189 * Other task. 190 */ 191 OTHER 192 } 193 194 /** 195 * The state on which the task can be. 196 */ 197 public enum State 198 { 199 /** 200 * The task is not started. 201 */ 202 NOT_STARTED, 203 /** 204 * The task is running. 205 */ 206 RUNNING, 207 /** 208 * The task finished successfully. 209 */ 210 FINISHED_SUCCESSFULLY, 211 /** 212 * The task finished with error. 213 */ 214 FINISHED_WITH_ERROR 215 } 216 217 /** 218 * Returns the names of the backends that are affected by the task. 219 * @return the names of the backends that are affected by the task. 220 */ 221 public abstract Set<String> getBackends(); 222 223 /** 224 * The current state of the task. 225 */ 226 protected State state = State.NOT_STARTED; 227 /** 228 * The return code of the task. 229 */ 230 protected Integer returnCode; 231 /** 232 * The last exception encountered during the task execution. 233 */ 234 protected Throwable lastException; 235 /** 236 * The progress logs of the task. Note that the user of StringBuffer is not 237 * a bug, because of the way the contents of logs is updated, using 238 * StringBuffer instead of StringBuilder is required. 239 */ 240 protected StringBuffer logs = new StringBuffer(); 241 /** 242 * The error logs of the task. 243 */ 244 protected StringBuilder errorLogs = new StringBuilder(); 245 /** 246 * The standard output logs of the task. 247 */ 248 protected StringBuilder outputLogs = new StringBuilder(); 249 /** 250 * The print stream for the error logs. 251 */ 252 protected ApplicationPrintStream errorPrintStream = 253 new ApplicationPrintStream(); 254 /** 255 * The print stream for the standard output logs. 256 */ 257 protected ApplicationPrintStream outPrintStream = 258 new ApplicationPrintStream(); 259 260 /** 261 * The process (if any) that the task launched. For instance if this is a 262 * start server task, the process generated executing the start-ds 263 * command-line. 264 */ 265 protected Process process; 266 private ControlPanelInfo info; 267 268 private ServerDescriptor server; 269 270 private ProgressDialog progressDialog; 271 272 private ArrayList<ConfigurationElementCreatedListener> confListeners = new ArrayList<>(); 273 274 private static int MAX_BINARY_LENGTH_TO_DISPLAY = 1024; 275 276 /** 277 * Constructor of the task. 278 * @param info the control panel information. 279 * @param progressDialog the progress dialog where the task progress will be 280 * displayed. 281 */ 282 protected Task(ControlPanelInfo info, ProgressDialog progressDialog) 283 { 284 this.info = info; 285 this.progressDialog = progressDialog; 286 outPrintStream.addListener(new PrintStreamListener() 287 { 288 /** 289 * Add a new line to the logs. 290 * @param msg the new line. 291 */ 292 @Override 293 public void newLine(String msg) 294 { 295 outputLogs.append(msg).append("\n"); 296 logs.append(msg).append("\n"); 297 } 298 }); 299 errorPrintStream.addListener(new PrintStreamListener() 300 { 301 /** 302 * Add a new line to the error logs. 303 * @param msg the new line. 304 */ 305 @Override 306 public void newLine(String msg) 307 { 308 errorLogs.append(msg).append("\n"); 309 logs.append(msg).append("\n"); 310 } 311 }); 312 server = info.getServerDescriptor(); 313 } 314 315 /** 316 * Returns the ControlPanelInfo object. 317 * @return the ControlPanelInfo object. 318 */ 319 public ControlPanelInfo getInfo() 320 { 321 return info; 322 } 323 324 /** 325 * Returns the logs of the task. 326 * @return the logs of the task. 327 */ 328 public String getLogs() 329 { 330 return logs.toString(); 331 } 332 333 /** 334 * Returns the error logs of the task. 335 * @return the error logs of the task. 336 */ 337 public String getErrorLogs() 338 { 339 return errorLogs.toString(); 340 } 341 342 /** 343 * Returns the output logs of the task. 344 * @return the output logs of the task. 345 */ 346 public String getOutputLogs() 347 { 348 return outputLogs.toString(); 349 } 350 351 /** 352 * Returns the state of the task. 353 * @return the state of the task. 354 */ 355 public State getState() 356 { 357 return state; 358 } 359 360 /** 361 * Returns last exception encountered during the task execution. 362 * Returns <CODE>null</CODE> if no exception was found. 363 * @return last exception encountered during the task execution. 364 */ 365 public Throwable getLastException() 366 { 367 return lastException; 368 } 369 370 /** 371 * Returns the return code (this makes sense when the task launches a 372 * command-line, it will return the error code returned by the command-line). 373 * @return the return code. 374 */ 375 public Integer getReturnCode() 376 { 377 return returnCode; 378 } 379 380 /** 381 * Returns the process that the task launched. 382 * Returns <CODE>null</CODE> if not process was launched. 383 * @return the process that the task launched. 384 */ 385 public Process getProcess() 386 { 387 return process; 388 } 389 390 /** 391 * Returns the progress dialog. 392 * @return the progress dialog. 393 */ 394 protected ProgressDialog getProgressDialog() 395 { 396 return progressDialog; 397 } 398 399 /** 400 * Tells whether a new server descriptor should be regenerated when the task 401 * is over. If the task has an influence in the configuration or state of 402 * the server (for instance the creation of a base DN) this method should 403 * return <CODE>true</CODE> so that the configuration will be re-read and 404 * all the ConfigChangeListeners will receive a notification with the new 405 * configuration. 406 * @return <CODE>true</CODE> if a new server descriptor must be regenerated 407 * when the task is over and <CODE>false</CODE> otherwise. 408 */ 409 public boolean regenerateDescriptor() 410 { 411 return true; 412 } 413 414 /** 415 * Method that is called when everything is finished after updating the 416 * progress dialog. It is called from the event thread. 417 */ 418 public void postOperation() 419 { 420 } 421 422 /** 423 * The description of the task. It is used in both the incompatibility 424 * messages and in the warning message displayed when the user wants to 425 * quit and there are tasks running. 426 * @return the description of the task. 427 */ 428 public abstract LocalizableMessage getTaskDescription(); 429 430 /** 431 * Adds a configuration element created listener. 432 * @param listener the listener. 433 */ 434 public void addConfigurationElementCreatedListener( 435 ConfigurationElementCreatedListener listener) 436 { 437 confListeners.add(listener); 438 } 439 440 /** 441 * Removes a configuration element created listener. 442 * @param listener the listener. 443 */ 444 public void removeConfigurationElementCreatedListener( 445 ConfigurationElementCreatedListener listener) 446 { 447 confListeners.remove(listener); 448 } 449 450 /** 451 * Notifies the configuration element created listener that a new object has 452 * been created. 453 * @param configObject the created object. 454 */ 455 protected void notifyConfigurationElementCreated(Object configObject) 456 { 457 for (ConfigurationElementCreatedListener listener : confListeners) 458 { 459 listener.elementCreated( 460 new ConfigurationElementCreatedEvent(this, configObject)); 461 } 462 } 463 464 /** 465 * Returns a String representation of a value. In general this is called 466 * to display the command-line equivalent when we do a modification in an 467 * entry. But since some attributes must be obfuscated (like the user 468 * password) we pass through this method. 469 * @param attrName the attribute name. 470 * @param o the attribute value. 471 * @return the obfuscated String representing the attribute value to be 472 * displayed in the logs of the user. 473 */ 474 protected String obfuscateAttributeStringValue(String attrName, Object o) 475 { 476 if (Utilities.mustObfuscate(attrName, 477 getInfo().getServerDescriptor().getSchema())) 478 { 479 return OBFUSCATED_VALUE; 480 } 481 else if (o instanceof byte[]) 482 { 483 byte[] bytes = (byte[])o; 484 if (displayBase64(attrName)) 485 { 486 if (bytes.length > MAX_BINARY_LENGTH_TO_DISPLAY) 487 { 488 return INFO_CTRL_PANEL_VALUE_IN_BASE64.get().toString(); 489 } 490 else 491 { 492 return Base64.encode(bytes); 493 } 494 } 495 else 496 { 497 if (bytes.length > MAX_BINARY_LENGTH_TO_DISPLAY) 498 { 499 return INFO_CTRL_PANEL_BINARY_VALUE.get().toString(); 500 } 501 else 502 { 503 // Get the String value 504 ByteString v = ByteString.wrap(bytes); 505 return v.toString(); 506 } 507 } 508 } 509 else 510 { 511 return String.valueOf(o); 512 } 513 } 514 515 /** 516 * Obfuscates (if required) the attribute value in an LDIF line. 517 * @param line the line of the LDIF file that must be treated. 518 * @return the line obfuscated. 519 */ 520 protected String obfuscateLDIFLine(String line) 521 { 522 int index = line.indexOf(":"); 523 if (index != -1) 524 { 525 String attrName = line.substring(0, index).trim(); 526 if (Utilities.mustObfuscate(attrName, 527 getInfo().getServerDescriptor().getSchema())) 528 { 529 return attrName + ": " + OBFUSCATED_VALUE; 530 } 531 } 532 return line; 533 } 534 535 /** 536 * Executes a command-line synchronously. 537 * @param commandLineName the command line full path. 538 * @param args the arguments for the command-line. 539 * @return the error code returned by the command-line. 540 */ 541 protected int executeCommandLine(String commandLineName, String[] args) 542 { 543 returnCode = -1; 544 String[] cmd = new String[args.length + 1]; 545 cmd[0] = commandLineName; 546 System.arraycopy(args, 0, cmd, 1, args.length); 547 548 ProcessBuilder pb = new ProcessBuilder(cmd); 549 // Use the java args in the script. 550 Map<String, String> env = pb.environment(); 551 //env.put(SetupUtils.OPENDJ_JAVA_ARGS, ""); 552 env.remove(SetupUtils.OPENDJ_JAVA_ARGS); 553 env.remove("CLASSPATH"); 554 ProcessReader outReader = null; 555 ProcessReader errReader = null; 556 try { 557 process = pb.start(); 558 559 outReader = new ProcessReader(process, outPrintStream, false); 560 errReader = new ProcessReader(process, errorPrintStream, true); 561 562 outReader.startReading(); 563 errReader.startReading(); 564 565 returnCode = process.waitFor(); 566 } catch (Throwable t) 567 { 568 lastException = t; 569 } 570 finally 571 { 572 if (outReader != null) 573 { 574 outReader.interrupt(); 575 } 576 if (errReader != null) 577 { 578 errReader.interrupt(); 579 } 580 } 581 return returnCode; 582 } 583 584 /** 585 * Informs of whether the task to be launched can be launched or not. Every 586 * task must implement this method so that we avoid launching in paralel two 587 * tasks that are not compatible. Note that in general if the current task 588 * is not running this method will return <CODE>true</CODE>. 589 * 590 * @param taskToBeLaunched the Task that we are trying to launch. 591 * @param incompatibilityReasons the list of incompatibility reasons that 592 * must be updated. 593 * @return <CODE>true</CODE> if the task that we are trying to launch can be 594 * launched in parallel with this task and <CODE>false</CODE> otherwise. 595 */ 596 public abstract boolean canLaunch(Task taskToBeLaunched, 597 Collection<LocalizableMessage> incompatibilityReasons); 598 599 /** 600 * Execute the task. This method is synchronous. 601 * 602 */ 603 public abstract void runTask(); 604 605 /** 606 * Returns the type of the task. 607 * @return the type of the task. 608 */ 609 public abstract Type getType(); 610 611 612 /** 613 * Returns the binary/script directory. 614 * @return the binary/script directory. 615 */ 616 protected String getBinaryDir() 617 { 618 if (binDir == null) 619 { 620 File f = Installation.getLocal().getBinariesDirectory(); 621 try 622 { 623 binDir = f.getCanonicalPath(); 624 } 625 catch (Throwable t) 626 { 627 binDir = f.getAbsolutePath(); 628 } 629 if (binDir.lastIndexOf(File.separatorChar) != binDir.length() - 1) 630 { 631 binDir += File.separatorChar; 632 } 633 } 634 635 return binDir; 636 } 637 638 /** 639 * Check whether the provided task and this task run on the same server. 640 * @param task the task the task to be analyzed. 641 * @return <CODE>true</CODE> if both tasks run on the same server and 642 * <CODE>false</CODE> otherwise. 643 */ 644 protected boolean runningOnSameServer(Task task) 645 { 646 if (getServer().isLocal() && task.getServer().isLocal()) 647 { 648 return true; 649 } 650 651 // Compare the host name and the instance path. This is safer than 652 // comparing ports: we might be running locally on a stopped instance with 653 // the same configuration as a "remote" (though located on the same machine) server. 654 String host1 = getServer().getHostname(); 655 String host2 = task.getServer().getHostname(); 656 boolean runningOnSameServer = host1 == null ? host2 == null : host1.equalsIgnoreCase(host2); 657 if (runningOnSameServer) 658 { 659 String f1 = getServer().getInstancePath(); 660 String f2 = task.getServer().getInstancePath(); 661 return Objects.equals(f1, f2); 662 } 663 return runningOnSameServer; 664 } 665 666 /** 667 * Returns the server descriptor on which the task was launched. 668 * @return the server descriptor on which the task was launched. 669 */ 670 public ServerDescriptor getServer() 671 { 672 return server; 673 } 674 675 /** 676 * Returns the full path of the command-line associated with this task or 677 * <CODE>null</CODE> if there is not a command-line (or a single command-line) 678 * associated with the task. 679 * @return the full path of the command-line associated with this task. 680 */ 681 protected abstract String getCommandLinePath(); 682 683 /** 684 * Returns the full path of the command-line for a given script name. 685 * @param scriptBasicName the script basic name (with no extension). 686 * @return the full path of the command-line for a given script name. 687 */ 688 protected String getCommandLinePath(String scriptBasicName) 689 { 690 if (isWindows()) 691 { 692 return getBinaryDir() + scriptBasicName + ".bat"; 693 } 694 return getBinaryDir() + scriptBasicName; 695 } 696 697 /** 698 * Returns the list of command-line arguments. 699 * @return the list of command-line arguments. 700 */ 701 protected abstract List<String> getCommandLineArguments(); 702 703 704 705 /** 706 * Returns the list of obfuscated command-line arguments. This is called 707 * basically to display the equivalent command-line to the user. 708 * @param clearArgs the arguments in clear. 709 * @return the list of obfuscated command-line arguments. 710 */ 711 protected List<String> getObfuscatedCommandLineArguments(List<String> clearArgs) 712 { 713 String[] toObfuscate = { "--bindPassword", "--currentPassword", "--newPassword" }; 714 ArrayList<String> args = new ArrayList<>(clearArgs); 715 for (int i=1; i<args.size(); i++) 716 { 717 for (String argName : toObfuscate) 718 { 719 if (args.get(i-1).equalsIgnoreCase(argName)) 720 { 721 args.set(i, OBFUSCATED_VALUE); 722 break; 723 } 724 } 725 } 726 return args; 727 } 728 729 /** 730 * Returns the command-line arguments that correspond to the configuration. 731 * This method is called to remove them when we display the equivalent 732 * command-line. In some cases we run the methods of the command-line 733 * directly (on this JVM) instead of launching the script in another process. 734 * When we call this methods we must add these arguments, but they are not 735 * to be included as arguments of the command-line (when is launched as a 736 * script). 737 * @return the command-line arguments that correspond to the configuration. 738 */ 739 protected List<String> getConfigCommandLineArguments() 740 { 741 List<String> args = new ArrayList<>(); 742 args.add("--configClass"); 743 args.add(org.opends.server.extensions.ConfigFileHandler.class.getName()); 744 args.add("--configFile"); 745 args.add(ConfigReader.configFile); 746 return args; 747 } 748 749 /** 750 * Returns the list of arguments related to the connection (host, port, bind 751 * DN, etc.). 752 * @return the list of arguments related to the connection. 753 */ 754 protected List<String> getConnectionCommandLineArguments() 755 { 756 return getConnectionCommandLineArguments(true, false); 757 } 758 759 /** 760 * Returns the list of arguments related to the connection (host, port, bind 761 * DN, etc.). 762 * @param useAdminConnector use the administration connector to generate 763 * the command line. 764 * @param addConnectionTypeParameters add the connection type parameters 765 * (--useSSL or --useStartTLS parameters: for ldapadd, ldapdelete, etc.). 766 * @return the list of arguments related to the connection. 767 */ 768 protected List<String> getConnectionCommandLineArguments( 769 boolean useAdminConnector, boolean addConnectionTypeParameters) 770 { 771 ArrayList<String> args = new ArrayList<>(); 772 InitialLdapContext ctx; 773 774 if (useAdminConnector) 775 { 776 ctx = getInfo().getDirContext(); 777 } 778 else 779 { 780 ctx = getInfo().getUserDataDirContext(); 781 } 782 if (isServerRunning() && ctx != null) 783 { 784 String hostName = localHostName; 785 if (hostName == null || !getInfo().getServerDescriptor().isLocal()) 786 { 787 hostName = ConnectionUtils.getHostName(ctx); 788 } 789 int port = ConnectionUtils.getPort(ctx); 790 boolean isSSL = ConnectionUtils.isSSL(ctx); 791 boolean isStartTLS = ConnectionUtils.isStartTLS(ctx); 792 String bindDN = ConnectionUtils.getBindDN(ctx); 793 String bindPwd = ConnectionUtils.getBindPassword(ctx); 794 args.add("--hostName"); 795 args.add(hostName); 796 args.add("--port"); 797 args.add(String.valueOf(port)); 798 args.add("--bindDN"); 799 args.add(bindDN); 800 args.add("--bindPassword"); 801 args.add(bindPwd); 802 if (isSSL || isStartTLS) 803 { 804 args.add("--trustAll"); 805 } 806 if (isSSL && addConnectionTypeParameters) 807 { 808 args.add("--useSSL"); 809 } 810 else if (isStartTLS && addConnectionTypeParameters) 811 { 812 args.add("--useStartTLS"); 813 } 814 } 815 return args; 816 } 817 818 /** 819 * Returns the noPropertiesFile argument. 820 * @return the noPropertiesFile argument. 821 */ 822 protected String getNoPropertiesFileArgument() 823 { 824 return "--noPropertiesFile"; 825 } 826 827 /** 828 * Returns the command-line to be displayed (when we display the equivalent 829 * command-line). 830 * @return the command-line to be displayed. 831 */ 832 public String getCommandLineToDisplay() 833 { 834 String cmdLineName = getCommandLinePath(); 835 if (cmdLineName != null) 836 { 837 List<String> args = 838 getObfuscatedCommandLineArguments(getCommandLineArguments()); 839 args.removeAll(getConfigCommandLineArguments()); 840 return getEquivalentCommandLine(cmdLineName, args); 841 } 842 return null; 843 } 844 845 /** 846 * Commodity method to know if the server is running or not. 847 * @return <CODE>true</CODE> if the server is running and <CODE>false</CODE> 848 * otherwise. 849 */ 850 protected boolean isServerRunning() 851 { 852 return getInfo().getServerDescriptor().getStatus() == 853 ServerDescriptor.ServerStatus.STARTED; 854 } 855 856 /** 857 * 858 * Returns the print stream for the error logs. 859 * @return the print stream for the error logs. 860 */ 861 public ApplicationPrintStream getErrorPrintStream() 862 { 863 return errorPrintStream; 864 } 865 866 /** 867 * 868 * Returns the print stream for the output logs. 869 * @return the print stream for the output logs. 870 */ 871 public ApplicationPrintStream getOutPrintStream() 872 { 873 return outPrintStream; 874 } 875 876 /** 877 * Prints the equivalent modify command line in the progress dialog. 878 * @param dn the dn of the modified entry. 879 * @param mods the modifications. 880 * @param useAdminCtx use the administration connector. 881 */ 882 protected void printEquivalentCommandToModify(DN dn, 883 Collection<ModificationItem> mods, boolean useAdminCtx) 884 { 885 printEquivalentCommandToModify(dn.toString(), mods, useAdminCtx); 886 } 887 888 /** 889 * Prints the equivalent modify command line in the progress dialog. 890 * @param dn the dn of the modified entry. 891 * @param mods the modifications. 892 * @param useAdminCtx use the administration connector. 893 */ 894 protected void printEquivalentCommandToModify(String dn, 895 Collection<ModificationItem> mods, boolean useAdminCtx) 896 { 897 ArrayList<String> args = new ArrayList<>(getObfuscatedCommandLineArguments( 898 getConnectionCommandLineArguments(useAdminCtx, true))); 899 args.add(getNoPropertiesFileArgument()); 900 String equiv = getEquivalentCommandLine(getCommandLinePath("ldapmodify"), args); 901 902 StringBuilder sb = new StringBuilder(); 903 sb.append(INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_MODIFY.get()).append("<br><b>"); 904 sb.append(equiv); 905 sb.append("<br>"); 906 sb.append("dn: ").append(dn); 907 boolean firstChangeType = true; 908 for (ModificationItem mod : mods) 909 { 910 if (firstChangeType) 911 { 912 sb.append("<br>changetype: modify<br>"); 913 } 914 else 915 { 916 sb.append("-<br>"); 917 } 918 firstChangeType = false; 919 Attribute attr = mod.getAttribute(); 920 String attrName = attr.getID(); 921 if (mod.getModificationOp() == DirContext.ADD_ATTRIBUTE) 922 { 923 sb.append("add: ").append(attrName).append("<br>"); 924 } 925 else if (mod.getModificationOp() == DirContext.REPLACE_ATTRIBUTE) 926 { 927 sb.append("replace: ").append(attrName).append("<br>"); 928 } 929 else 930 { 931 sb.append("delete: ").append(attrName).append("<br>"); 932 } 933 for (int i=0; i<attr.size(); i++) 934 { 935 try 936 { 937 Object o = attr.get(i); 938 // We are systematically adding the values in binary mode. 939 // Use the attribute names to figure out the value to be displayed. 940 if (displayBase64(attr.getID())) 941 { 942 sb.append(attrName).append(":: "); 943 } 944 else 945 { 946 sb.append(attrName).append(": "); 947 } 948 sb.append(obfuscateAttributeStringValue(attrName, o)); 949 sb.append("<br>"); 950 } 951 catch (NamingException ne) 952 { 953 // Bug 954 throw new RuntimeException( 955 "Unexpected error parsing modifications: "+ne, ne); 956 } 957 } 958 } 959 sb.append("</b><br><br>"); 960 961 getProgressDialog().appendProgressHtml(Utilities.applyFont( 962 sb.toString(), ColorAndFontConstants.progressFont)); 963 } 964 965 /** 966 * The separator used to link the lines of the resulting command-lines. 967 */ 968 private static final String LINE_SEPARATOR = CommandBuilder.HTML_LINE_SEPARATOR; 969 970 /** 971 * Returns the equivalent command line in HTML without font properties. 972 * @param cmdName the command name. 973 * @param args the arguments for the command line. 974 * @return the equivalent command-line in HTML. 975 */ 976 public static String getEquivalentCommandLine(String cmdName, 977 List<String> args) 978 { 979 StringBuilder sb = new StringBuilder(cmdName); 980 for (String arg : args) 981 { 982 if (arg.charAt(0) == '-') 983 { 984 sb.append(LINE_SEPARATOR); 985 } 986 else 987 { 988 sb.append(" "); 989 } 990 sb.append(CommandBuilder.escapeValue(arg)); 991 } 992 return sb.toString(); 993 } 994 995 /** 996 * Prints the equivalent command line. 997 * @param cmdName the command name. 998 * @param args the arguments for the command line. 999 * @param msg the message associated with the command line. 1000 */ 1001 protected void printEquivalentCommandLine(String cmdName, List<String> args, 1002 LocalizableMessage msg) 1003 { 1004 getProgressDialog().appendProgressHtml(Utilities.applyFont(msg+"<br><b>"+ 1005 getEquivalentCommandLine(cmdName, args)+"</b><br><br>", 1006 ColorAndFontConstants.progressFont)); 1007 } 1008 1009 /** 1010 * Tells whether the provided attribute's values must be displayed using 1011 * base 64 when displaying the equivalent command-line or not. 1012 * @param attrName the attribute name. 1013 * @return <CODE>true</CODE> if the attribute must be displayed using base 64 1014 * and <CODE>false</CODE> otherwise. 1015 */ 1016 protected boolean displayBase64(String attrName) 1017 { 1018 Schema schema = null; 1019 if (getInfo() != null) 1020 { 1021 schema = getInfo().getServerDescriptor().getSchema(); 1022 } 1023 return Utilities.hasBinarySyntax(attrName, schema); 1024 } 1025 1026 /** 1027 * Prints the equivalent rename command line in the progress dialog. 1028 * @param oldDN the old DN of the entry. 1029 * @param newDN the new DN of the entry. 1030 * @param useAdminCtx use the administration connector. 1031 */ 1032 protected void printEquivalentRenameCommand(DN oldDN, DN newDN, 1033 boolean useAdminCtx) 1034 { 1035 ArrayList<String> args = new ArrayList<>(getObfuscatedCommandLineArguments( 1036 getConnectionCommandLineArguments(useAdminCtx, true))); 1037 args.add(getNoPropertiesFileArgument()); 1038 String equiv = getEquivalentCommandLine(getCommandLinePath("ldapmodify"), args); 1039 StringBuilder sb = new StringBuilder(); 1040 sb.append(INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_RENAME.get()).append("<br><b>"); 1041 sb.append(equiv); 1042 sb.append("<br>"); 1043 sb.append("dn: ").append(oldDN); 1044 sb.append("<br>"); 1045 sb.append("changetype: moddn<br>"); 1046 sb.append("newrdn: ").append(newDN.rdn()).append("<br>"); 1047 sb.append("deleteoldrdn: 1"); 1048 sb.append("</b><br><br>"); 1049 getProgressDialog().appendProgressHtml( 1050 Utilities.applyFont(sb.toString(), 1051 ColorAndFontConstants.progressFont)); 1052 } 1053 1054 /** 1055 * Returns the incompatible message between two tasks. 1056 * @param taskRunning the task that is running. 1057 * @param taskToBeLaunched the task that we are trying to launch. 1058 * @return the incompatible message between two tasks. 1059 */ 1060 protected LocalizableMessage getIncompatibilityMessage(Task taskRunning, 1061 Task taskToBeLaunched) 1062 { 1063 return INFO_CTRL_PANEL_INCOMPATIBLE_TASKS.get( 1064 taskRunning.getTaskDescription(), taskToBeLaunched.getTaskDescription()); 1065 } 1066}