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