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