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 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.quicksetup.installer; 018 019import static com.forgerock.opendj.cli.Utils.*; 020import static com.forgerock.opendj.util.OperatingSystem.*; 021 022import static org.opends.messages.QuickSetupMessages.*; 023import static org.opends.quicksetup.Installation.*; 024import static org.opends.quicksetup.util.Utils.*; 025import static org.opends.server.types.ExistingFileBehavior.*; 026 027import java.io.BufferedReader; 028import java.io.BufferedWriter; 029import java.io.Closeable; 030import java.io.File; 031import java.io.FileInputStream; 032import java.io.FileReader; 033import java.io.FileWriter; 034import java.io.IOException; 035import java.io.InputStreamReader; 036import java.util.ArrayList; 037import java.util.Arrays; 038import java.util.HashMap; 039import java.util.HashSet; 040import java.util.List; 041import java.util.Map; 042import java.util.Properties; 043import java.util.Random; 044import java.util.Set; 045import java.util.TreeSet; 046 047import org.forgerock.i18n.LocalizableMessage; 048import org.forgerock.i18n.LocalizedIllegalArgumentException; 049import org.forgerock.i18n.slf4j.LocalizedLogger; 050import org.forgerock.opendj.config.ManagedObjectDefinition; 051import org.forgerock.opendj.config.ManagedObjectNotFoundException; 052import org.forgerock.opendj.config.PropertyException; 053import org.forgerock.opendj.config.server.ConfigException; 054import org.forgerock.opendj.ldap.DN; 055import org.forgerock.opendj.server.config.client.BackendCfgClient; 056import org.forgerock.opendj.server.config.client.CryptoManagerCfgClient; 057import org.forgerock.opendj.server.config.client.ReplicationDomainCfgClient; 058import org.forgerock.opendj.server.config.client.ReplicationServerCfgClient; 059import org.forgerock.opendj.server.config.client.ReplicationSynchronizationProviderCfgClient; 060import org.forgerock.opendj.server.config.client.RootCfgClient; 061import org.forgerock.opendj.server.config.meta.BackendCfgDefn; 062import org.forgerock.opendj.server.config.meta.ReplicationDomainCfgDefn; 063import org.forgerock.opendj.server.config.meta.ReplicationServerCfgDefn; 064import org.forgerock.opendj.server.config.meta.ReplicationSynchronizationProviderCfgDefn; 065import org.forgerock.opendj.server.config.server.BackendCfg; 066import org.opends.admin.ads.util.ConnectionWrapper; 067import org.opends.guitools.controlpanel.util.Utilities; 068import org.opends.messages.BackendMessages; 069import org.opends.messages.CoreMessages; 070import org.opends.messages.ReplicationMessages; 071import org.opends.quicksetup.Application; 072import org.opends.quicksetup.ApplicationException; 073import org.opends.quicksetup.JavaArguments; 074import org.opends.quicksetup.ReturnCode; 075import org.opends.quicksetup.UserData; 076import org.opends.quicksetup.util.OutputReader; 077import org.opends.quicksetup.util.Utils; 078import org.opends.server.backends.task.TaskState; 079import org.opends.server.core.DirectoryServer; 080import org.opends.server.tools.ConfigureDS; 081import org.opends.server.tools.ConfigureWindowsService; 082import org.opends.server.tools.JavaPropertiesTool; 083import org.opends.server.types.DirectoryException; 084import org.opends.server.types.LDIFExportConfig; 085import org.opends.server.types.OpenDsException; 086import org.opends.server.util.LDIFException; 087import org.opends.server.util.LDIFWriter; 088import org.opends.server.util.SetupUtils; 089import org.opends.server.util.StaticUtils; 090 091/** 092 * This is the only class that uses classes in org.opends.server (excluding the 093 * case of DynamicConstants, SetupUtils and CertificateManager 094 * which are already included in quicksetup.jar). 095 * 096 * Important note: do not include references to this class until OpenDS.jar has 097 * been loaded. These classes must be loaded during Runtime. 098 * The code is written in a way that when we execute the code that uses these 099 * classes the required jar files are already loaded. However these jar files 100 * are not necessarily loaded when we create this class. 101 */ 102public class InstallerHelper { 103 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 104 105 private static final int MAX_ID_VALUE = Short.MAX_VALUE; 106 private static final long ONE_MEGABYTE = 1024L * 1024; 107 108 /** 109 * Invokes the method ConfigureDS.configMain with the provided parameters. 110 * @param args the arguments to be passed to ConfigureDS.configMain. 111 * @return the return code of the ConfigureDS.configMain method. 112 * @throws ApplicationException if something goes wrong. 113 * @see org.opends.server.tools.ConfigureDS#configMain(String[], 114 * java.io.OutputStream, java.io.OutputStream) 115 */ 116 public int invokeConfigureServer(String[] args) throws ApplicationException { 117 return ConfigureDS.configMain(args, System.out, System.err); 118 } 119 120 /** 121 * Invokes the import-ldif command-line with the provided parameters. 122 * 123 * @param application 124 * the application that is launching this. 125 * @param args 126 * the arguments to be passed to import-ldif. 127 * @return the return code of the import-ldif call. 128 * @throws IOException 129 * if the process could not be launched. 130 * @throws InterruptedException 131 * if the process was interrupted. 132 */ 133 public int invokeImportLDIF(final Application application, String[] args) throws IOException, InterruptedException 134 { 135 final File installPath = new File(application.getInstallationPath()); 136 final File importLDIFPath = getImportPath(installPath); 137 138 final ArrayList<String> argList = new ArrayList<>(); 139 argList.add(Utils.getScriptPath(importLDIFPath.getAbsolutePath())); 140 argList.addAll(Arrays.asList(args)); 141 logger.info(LocalizableMessage.raw("import-ldif arg list: " + argList)); 142 143 final ProcessBuilder processBuilder = new ProcessBuilder(argList.toArray(new String[argList.size()])); 144 final Map<String, String> env = processBuilder.environment(); 145 env.remove(SetupUtils.OPENDJ_JAVA_HOME); 146 env.remove(SetupUtils.OPENDJ_JAVA_ARGS); 147 env.remove("CLASSPATH"); 148 processBuilder.directory(installPath); 149 150 Process process = null; 151 try 152 { 153 process = processBuilder.start(); 154 final BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream())); 155 new OutputReader(err) 156 { 157 @Override 158 public void processLine(final String line) 159 { 160 logger.warn(LocalizableMessage.raw("import-ldif error log: " + line)); 161 application.notifyListeners(LocalizableMessage.raw(line)); 162 application.notifyListeners(application.getLineBreak()); 163 } 164 }; 165 166 final BufferedReader out = new BufferedReader(new InputStreamReader(process.getInputStream())); 167 new OutputReader(out) 168 { 169 @Override 170 public void processLine(final String line) 171 { 172 logger.info(LocalizableMessage.raw("import-ldif out log: " + line)); 173 application.notifyListeners(LocalizableMessage.raw(line)); 174 application.notifyListeners(application.getLineBreak()); 175 } 176 }; 177 178 return process.waitFor(); 179 } 180 finally 181 { 182 if (process != null) 183 { 184 closeProcessStream(process.getErrorStream(), "error"); 185 closeProcessStream(process.getOutputStream(), "output"); 186 } 187 } 188 } 189 190 private File getImportPath(final File installPath) 191 { 192 if (isWindows()) 193 { 194 return buildImportPath(installPath, WINDOWS_BINARIES_PATH_RELATIVE, WINDOWS_IMPORT_LDIF); 195 } 196 return buildImportPath(installPath, UNIX_BINARIES_PATH_RELATIVE, UNIX_IMPORT_LDIF); 197 } 198 199 private File buildImportPath(final File installPath, String binDir, String importLdif) 200 { 201 final File binPath = new File(installPath, binDir); 202 return new File(binPath, importLdif); 203 } 204 205 private void closeProcessStream(final Closeable stream, final String streamName) 206 { 207 try 208 { 209 stream.close(); 210 } 211 catch (Throwable t) 212 { 213 logger.warn(LocalizableMessage.raw("Error closing " + streamName + " stream: " + t, t)); 214 } 215 } 216 217 /** 218 * Returns the LocalizableMessage ID that corresponds to a successfully started server. 219 * @return the LocalizableMessage ID that corresponds to a successfully started server. 220 */ 221 public String getStartedId() 222 { 223 return String.valueOf(CoreMessages.NOTE_DIRECTORY_SERVER_STARTED.ordinal()); 224 } 225 226 /** 227 * This methods enables this server as a Windows service. 228 * @throws ApplicationException if something goes wrong. 229 */ 230 public void enableWindowsService() throws ApplicationException { 231 int code = ConfigureWindowsService.enableService(System.out, System.err); 232 233 LocalizableMessage errorMessage = INFO_ERROR_ENABLING_WINDOWS_SERVICE.get(); 234 235 switch (code) { 236 case 237 ConfigureWindowsService.SERVICE_ENABLE_SUCCESS: 238 break; 239 case 240 ConfigureWindowsService.SERVICE_ALREADY_ENABLED: 241 break; 242 default: 243 throw new ApplicationException( 244 ReturnCode.WINDOWS_SERVICE_ERROR, 245 errorMessage, null); 246 } 247 } 248 249 /** 250 * This method disables this server as a Windows service. 251 * @throws ApplicationException if something goes worong. 252 */ 253 public void disableWindowsService() throws ApplicationException 254 { 255 int code = ConfigureWindowsService.disableService(System.out, System.err); 256 if (code == ConfigureWindowsService.SERVICE_DISABLE_ERROR) { 257 throw new ApplicationException( 258 // TODO: fix this message's format string 259 ReturnCode.WINDOWS_SERVICE_ERROR, 260 INFO_ERROR_DISABLING_WINDOWS_SERVICE.get(""), null); 261 } 262 } 263 264 /** 265 * Creates a template LDIF file with an entry that has as dn the provided 266 * baseDn. 267 * @param baseDn the dn of the entry that will be created in the LDIF file. 268 * @return the File object pointing to the created temporary file. 269 * @throws ApplicationException if something goes wrong. 270 */ 271 public File createBaseEntryTempFile(String baseDn) 272 throws ApplicationException { 273 File ldifFile; 274 try 275 { 276 ldifFile = File.createTempFile("opendj-base-entry", ".ldif"); 277 ldifFile.deleteOnExit(); 278 } catch (IOException ioe) 279 { 280 LocalizableMessage failedMsg = 281 getThrowableMsg(INFO_ERROR_CREATING_TEMP_FILE.get(), ioe); 282 throw new ApplicationException( 283 ReturnCode.FILE_SYSTEM_ACCESS_ERROR, 284 failedMsg, ioe); 285 } 286 287 LDIFExportConfig exportConfig = new LDIFExportConfig(ldifFile.getAbsolutePath(), OVERWRITE); 288 try (LDIFWriter writer = new LDIFWriter(exportConfig)) { 289 DN dn = DN.valueOf(baseDn); 290 writer.writeEntry(StaticUtils.createEntry(dn)); 291 } catch (LocalizedIllegalArgumentException | LDIFException | IOException de) { 292 throw new ApplicationException( 293 ReturnCode.CONFIGURATION_ERROR, 294 getThrowableMsg(INFO_ERROR_IMPORTING_LDIF.get(), de), de); 295 } catch (Throwable t) { 296 throw new ApplicationException( 297 ReturnCode.BUG, getThrowableMsg( 298 INFO_BUG_MSG.get(), t), t); 299 } 300 return ldifFile; 301 } 302 303 /** 304 * Deletes a backend on the server. It assumes the server is stopped. 305 * @param backendName the name of the backend to be deleted. 306 * @throws ApplicationException if something goes wrong. 307 */ 308 public void deleteBackend(String backendName) 309 throws ApplicationException 310 { 311 try 312 { 313 // Read the configuration file. 314 DN dn = DN.valueOf("ds-cfg-backend-id" + "=" + backendName + ",cn=Backends,cn=config"); 315 Utilities.deleteConfigSubtree(DirectoryServer.getConfigurationHandler(), dn); 316 } 317 catch (OpenDsException | ConfigException ode) 318 { 319 throw new ApplicationException( 320 ReturnCode.CONFIGURATION_ERROR, ode.getMessageObject(), ode); 321 } 322 } 323 324 /** 325 * Creates a database backend on the server. 326 * 327 * @param conn 328 * the connection to the server. 329 * @param backendName 330 * the name of the backend to be created. 331 * @param baseDNs 332 * the list of base DNs to be defined on the server. 333 * @param backendType 334 * the backend type. 335 * @throws ApplicationException 336 * if something goes wrong. 337 */ 338 public void createBackend(ConnectionWrapper conn, String backendName, Set<String> baseDNs, 339 ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backendType) 340 throws ApplicationException 341 { 342 try 343 { 344 RootCfgClient root = conn.getRootConfiguration(); 345 BackendCfgClient backend = root.createBackend(backendType, backendName, null); 346 backend.setEnabled(true); 347 backend.setBaseDN(toDNs(baseDNs)); 348 backend.setBackendId(backendName); 349 backend.setWritabilityMode(BackendCfgDefn.WritabilityMode.ENABLED); 350 backend.commit(); 351 } 352 catch (Throwable t) 353 { 354 throw new ApplicationException( 355 ReturnCode.CONFIGURATION_ERROR, INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get(conn.getHostPort(), t), t); 356 } 357 } 358 359 private Set<DN> toDNs(Set<String> strings) throws DirectoryException 360 { 361 Set<DN> results = new HashSet<>(); 362 for (String s : strings) 363 { 364 results.add(DN.valueOf(s)); 365 } 366 return results; 367 } 368 369 /** 370 * Configures the replication on a given server. 371 * @param conn the connection to the server where we want to configure 372 * the replication. 373 * @param replicationServers a Map where the key value is the base dn and 374 * the value is the list of replication servers for that base dn (or domain). 375 * @param replicationPort the replicationPort of the server that is being 376 * configured (it might not exist and the user specified it in the setup). 377 * @param useSecureReplication whether to encrypt connections with the 378 * replication port or not. 379 * @param usedReplicationServerIds the list of replication server ids that 380 * are already used. 381 * @param usedServerIds the list of server ids (domain ids) that 382 * are already used. 383 * @throws ApplicationException if something goes wrong. 384 * @return a ConfiguredReplication object describing what has been configured. 385 */ 386 public ConfiguredReplication configureReplication( 387 ConnectionWrapper conn, Map<String,Set<String>> replicationServers, 388 int replicationPort, boolean useSecureReplication, Set<Integer> usedReplicationServerIds, 389 Set<Integer> usedServerIds) 390 throws ApplicationException 391 { 392 boolean synchProviderCreated; 393 boolean synchProviderEnabled; 394 boolean replicationServerCreated; 395 boolean secureReplicationEnabled; 396 try 397 { 398 RootCfgClient root = conn.getRootConfiguration(); 399 400 /* 401 * Configure Synchronization plugin. 402 */ 403 ReplicationSynchronizationProviderCfgClient sync = null; 404 try 405 { 406 sync = (ReplicationSynchronizationProviderCfgClient) 407 root.getSynchronizationProvider("Multimaster Synchronization"); 408 } 409 catch (ManagedObjectNotFoundException monfe) 410 { 411 // It does not exist. 412 } 413 if (sync == null) 414 { 415 ReplicationSynchronizationProviderCfgDefn provider = 416 ReplicationSynchronizationProviderCfgDefn.getInstance(); 417 sync = root.createSynchronizationProvider(provider, 418 "Multimaster Synchronization", 419 new ArrayList<PropertyException>()); 420 sync.setJavaClass( 421 org.opends.server.replication.plugin.MultimasterReplication.class. 422 getName()); 423 sync.setEnabled(Boolean.TRUE); 424 synchProviderCreated = true; 425 synchProviderEnabled = false; 426 } 427 else 428 { 429 synchProviderCreated = false; 430 if (!sync.isEnabled()) 431 { 432 sync.setEnabled(Boolean.TRUE); 433 synchProviderEnabled = true; 434 } 435 else 436 { 437 synchProviderEnabled = false; 438 } 439 } 440 sync.commit(); 441 442 /* 443 * Configure the replication server. 444 */ 445 ReplicationServerCfgClient replicationServer; 446 447 if (!sync.hasReplicationServer()) 448 { 449 if (useSecureReplication) 450 { 451 CryptoManagerCfgClient crypto = root.getCryptoManager(); 452 if (!crypto.isSSLEncryption()) 453 { 454 crypto.setSSLEncryption(true); 455 crypto.commit(); 456 secureReplicationEnabled = true; 457 } 458 else 459 { 460 // Only mark as true if we actually change the configuration 461 secureReplicationEnabled = false; 462 } 463 } 464 else 465 { 466 secureReplicationEnabled = false; 467 } 468 int id = getReplicationId(usedReplicationServerIds); 469 usedReplicationServerIds.add(id); 470 replicationServer = sync.createReplicationServer( 471 ReplicationServerCfgDefn.getInstance(), 472 new ArrayList<PropertyException>()); 473 replicationServer.setReplicationServerId(id); 474 replicationServer.setReplicationPort(replicationPort); 475 replicationServerCreated = true; 476 } 477 else 478 { 479 secureReplicationEnabled = false; 480 replicationServer = sync.getReplicationServer(); 481 usedReplicationServerIds.add( 482 replicationServer.getReplicationServerId()); 483 replicationServerCreated = false; 484 } 485 486 Set<String> servers = replicationServer.getReplicationServer(); 487 if (servers == null) 488 { 489 servers = new HashSet<>(); 490 } 491 Set<String> oldServers = new HashSet<>(servers); 492 for (Set<String> rs : replicationServers.values()) 493 { 494 servers.addAll(rs); 495 } 496 497 replicationServer.setReplicationServer(servers); 498 replicationServer.commit(); 499 500 Set<String> newReplicationServers = intersect(servers, oldServers); 501 502 /* 503 * Create the domains 504 */ 505 String[] domainNames = sync.listReplicationDomains(); 506 if (domainNames == null) 507 { 508 domainNames = new String[]{}; 509 } 510 Set<ConfiguredDomain> domainsConf = new HashSet<>(); 511 ReplicationDomainCfgClient[] domains = 512 new ReplicationDomainCfgClient[domainNames.length]; 513 for (int i=0; i<domains.length; i++) 514 { 515 domains[i] = sync.getReplicationDomain(domainNames[i]); 516 } 517 for (String dn : replicationServers.keySet()) 518 { 519 ReplicationDomainCfgClient domain = null; 520 boolean isCreated; 521 String domainName = null; 522 for (int i = 0; i < domains.length && domain == null; i++) 523 { 524 if (areDnsEqual(dn, 525 domains[i].getBaseDN().toString())) 526 { 527 domain = domains[i]; 528 domainName = domainNames[i]; 529 } 530 } 531 if (domain == null) 532 { 533 int domainId = getReplicationId(usedServerIds); 534 usedServerIds.add(domainId); 535 domainName = getDomainName(domainNames, dn); 536 domain = sync.createReplicationDomain( 537 ReplicationDomainCfgDefn.getInstance(), domainName, 538 new ArrayList<PropertyException>()); 539 domain.setServerId(domainId); 540 domain.setBaseDN(DN.valueOf(dn)); 541 isCreated = true; 542 } 543 else 544 { 545 isCreated = false; 546 } 547 oldServers = domain.getReplicationServer(); 548 if (oldServers == null) 549 { 550 oldServers = new TreeSet<>(); 551 } 552 servers = replicationServers.get(dn); 553 domain.setReplicationServer(servers); 554 usedServerIds.add(domain.getServerId()); 555 556 domain.commit(); 557 Set<String> addedServers = intersect(servers, oldServers); 558 ConfiguredDomain domainConf = new ConfiguredDomain(domainName, 559 isCreated, addedServers); 560 domainsConf.add(domainConf); 561 } 562 return new ConfiguredReplication(synchProviderCreated, 563 synchProviderEnabled, replicationServerCreated, 564 secureReplicationEnabled, newReplicationServers, 565 domainsConf); 566 } 567 catch (Throwable t) 568 { 569 throw new ApplicationException( 570 ReturnCode.CONFIGURATION_ERROR, 571 INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get(conn.getHostPort(), t), 572 t); 573 } 574 } 575 576 private Set<String> intersect(Set<String> set1, Set<String> set2) 577 { 578 Set<String> result = new TreeSet<>(set1); 579 result.removeAll(set2); 580 return result; 581 } 582 583 /** 584 * Configures the replication on a given server. 585 * 586 * @param conn 587 * the connection to the server where we want to configure the 588 * replication. 589 * @param replConf 590 * the object describing what was configured. 591 * @throws ApplicationException 592 * if something goes wrong. 593 */ 594 public void unconfigureReplication(ConnectionWrapper conn, ConfiguredReplication replConf) throws ApplicationException 595 { 596 try 597 { 598 RootCfgClient root = conn.getRootConfiguration(); 599 final String syncProvider = "Multimaster Synchronization"; 600 // Unconfigure Synchronization plugin. 601 if (replConf.isSynchProviderCreated()) 602 { 603 try 604 { 605 root.removeSynchronizationProvider(syncProvider); 606 } 607 catch (ManagedObjectNotFoundException monfe) 608 { 609 // It does not exist. 610 } 611 } 612 else 613 { 614 try 615 { 616 ReplicationSynchronizationProviderCfgClient sync = 617 (ReplicationSynchronizationProviderCfgClient) root.getSynchronizationProvider(syncProvider); 618 if (replConf.isSynchProviderEnabled()) 619 { 620 sync.setEnabled(Boolean.FALSE); 621 } 622 623 if (replConf.isReplicationServerCreated()) 624 { 625 sync.removeReplicationServer(); 626 } 627 else if (sync.hasReplicationServer()) 628 { 629 ReplicationServerCfgClient replicationServer = sync.getReplicationServer(); 630 Set<String> replServers = replicationServer.getReplicationServer(); 631 if (replServers != null) 632 { 633 replServers.removeAll(replConf.getNewReplicationServers()); 634 replicationServer.setReplicationServer(replServers); 635 replicationServer.commit(); 636 } 637 } 638 639 for (ConfiguredDomain domain : replConf.getDomainsConf()) 640 { 641 if (domain.isCreated()) 642 { 643 sync.removeReplicationDomain(domain.getDomainName()); 644 } 645 else 646 { 647 try 648 { 649 ReplicationDomainCfgClient d = sync.getReplicationDomain(domain.getDomainName()); 650 Set<String> replServers = d.getReplicationServer(); 651 if (replServers != null) 652 { 653 replServers.removeAll(domain.getAddedReplicationServers()); 654 d.setReplicationServer(replServers); 655 d.commit(); 656 } 657 } 658 catch (ManagedObjectNotFoundException monfe) 659 { 660 // It does not exist. 661 } 662 } 663 } 664 sync.commit(); 665 } 666 catch (ManagedObjectNotFoundException monfe) 667 { 668 // It does not exist. 669 } 670 } 671 672 if (replConf.isSecureReplicationEnabled()) 673 { 674 CryptoManagerCfgClient crypto = root.getCryptoManager(); 675 if (crypto.isSSLEncryption()) 676 { 677 crypto.setSSLEncryption(false); 678 crypto.commit(); 679 } 680 } 681 } 682 catch (Throwable t) 683 { 684 throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get( 685 conn.getHostPort(), t), t); 686 } 687 } 688 689 /** 690 * For the given state provided by a Task tells if the task is done or not. 691 * 692 * @param sState 693 * the String representing the task state. 694 * @return <CODE>true</CODE> if the task is done and <CODE>false</CODE> 695 * otherwise. 696 */ 697 public boolean isDone(String sState) 698 { 699 return TaskState.isDone(TaskState.fromString(sState)); 700 } 701 702 /** 703 * For the given state provided by a Task tells if the task is successful or 704 * not. 705 * 706 * @param sState 707 * the String representing the task state. 708 * @return <CODE>true</CODE> if the task is successful and <CODE>false</CODE> 709 * otherwise. 710 */ 711 public boolean isSuccessful(String sState) 712 { 713 return TaskState.isSuccessful(TaskState.fromString(sState)); 714 } 715 716 /** 717 * For the given state provided by a Task tells if the task is complete with 718 * errors or not. 719 * 720 * @param sState 721 * the String representing the task state. 722 * @return <CODE>true</CODE> if the task is complete with errors and 723 * <CODE>false</CODE> otherwise. 724 */ 725 public boolean isCompletedWithErrors(String sState) 726 { 727 return TaskState.COMPLETED_WITH_ERRORS == TaskState.fromString(sState); 728 } 729 730 /** 731 * For the given state provided by a Task tells if the task is stopped by 732 * error or not. 733 * 734 * @param sState 735 * the String representing the task state. 736 * @return <CODE>true</CODE> if the task is stopped by error and 737 * <CODE>false</CODE> otherwise. 738 */ 739 public boolean isStoppedByError(String sState) 740 { 741 return TaskState.STOPPED_BY_ERROR == TaskState.fromString(sState); 742 } 743 744 /** 745 * Tells whether the provided log message corresponds to a peers not found 746 * error during the initialization of a replica or not. 747 * 748 * @param logMsg 749 * the log message. 750 * @return <CODE>true</CODE> if the log message corresponds to a peers not 751 * found error during initialization and <CODE>false</CODE> otherwise. 752 */ 753 public boolean isPeersNotFoundError(String logMsg) 754 { 755 return logMsg.contains("=" + ReplicationMessages.ERR_NO_REACHABLE_PEER_IN_THE_DOMAIN.ordinal()); 756 } 757 758 /** 759 * Returns the ID to be used for a new replication server or domain. 760 * @param usedIds the list of already used ids. 761 * @return the ID to be used for a new replication server or domain. 762 */ 763 public static int getReplicationId(Set<Integer> usedIds) 764 { 765 Random r = new Random(); 766 int id = 0; 767 while (id == 0 || usedIds.contains(id)) 768 { 769 id = r.nextInt(MAX_ID_VALUE); 770 } 771 return id; 772 } 773 774 /** 775 * Returns the name to be used for a new replication domain. 776 * @param existingDomains the existing domains names. 777 * @param baseDN the base DN of the domain. 778 * @return the name to be used for a new replication domain. 779 */ 780 public static String getDomainName(String[] existingDomains, String baseDN) 781 { 782 String domainName = baseDN; 783 boolean nameExists = true; 784 int j = 0; 785 while (nameExists) 786 { 787 boolean found = false; 788 for (int i=0; i<existingDomains.length && !found; i++) 789 { 790 found = existingDomains[i].equalsIgnoreCase(domainName); 791 } 792 if (found) 793 { 794 domainName = baseDN+"-"+j; 795 } 796 else 797 { 798 nameExists = false; 799 } 800 j++; 801 } 802 return domainName; 803 } 804 805 /** 806 * Writes the set-java-home file that is used by the scripts to set the java 807 * home and the java arguments. 808 * 809 * @param uData 810 * the data provided by the user. 811 * @param installPath 812 * where the server is installed. 813 * @throws IOException 814 * if an error occurred writing the file. 815 */ 816 public void writeSetOpenDSJavaHome(UserData uData, String installPath) throws IOException 817 { 818 String javaHome = System.getProperty("java.home"); 819 if (javaHome == null || javaHome.length() == 0) 820 { 821 javaHome = System.getenv(SetupUtils.OPENDJ_JAVA_HOME); 822 } 823 824 // Try to transform things if necessary. The following map has as key 825 // the original JavaArgument object and as value the 'transformed' JavaArgument. 826 Map<JavaArguments, JavaArguments> hmJavaArguments = new HashMap<>(); 827 for (String script : uData.getScriptNamesForJavaArguments()) 828 { 829 JavaArguments origJavaArguments = uData.getJavaArguments(script); 830 if (hmJavaArguments.get(origJavaArguments) == null) 831 { 832 if (Utils.supportsOption(origJavaArguments.getStringArguments(), javaHome, installPath)) 833 { 834 // The argument works, so just use it. 835 hmJavaArguments.put(origJavaArguments, origJavaArguments); 836 } 837 else 838 { 839 // We have to fix it somehow: test separately memory and other 840 // arguments to see if something works. 841 JavaArguments transformedArguments = getBestEffortArguments(origJavaArguments, javaHome, installPath); 842 hmJavaArguments.put(origJavaArguments, transformedArguments); 843 } 844 } 845 // else, support is already checked. 846 } 847 848 Properties fileProperties = getJavaPropertiesFileContents(getPropertiesFileName(installPath)); 849 Map<String, JavaArguments> args = new HashMap<>(); 850 Map<String, String> otherProperties = new HashMap<>(); 851 852 for (String script : uData.getScriptNamesForJavaArguments()) 853 { 854 JavaArguments origJavaArgument = uData.getJavaArguments(script); 855 JavaArguments transformedJavaArg = hmJavaArguments.get(origJavaArgument); 856 JavaArguments defaultJavaArg = uData.getDefaultJavaArguments(script); 857 858 // Apply the following policy: overwrite the values in the file only 859 // if the values provided by the user are not the default ones. 860 String propertiesKey = getJavaArgPropertyForScript(script); 861 if (origJavaArgument.equals(defaultJavaArg) && fileProperties.containsKey(propertiesKey)) 862 { 863 otherProperties.put(propertiesKey, fileProperties.getProperty(propertiesKey)); 864 } 865 else 866 { 867 args.put(script, transformedJavaArg); 868 } 869 } 870 871 putBooleanPropertyFrom("overwrite-env-java-home", fileProperties, otherProperties); 872 putBooleanPropertyFrom("overwrite-env-java-args", fileProperties, otherProperties); 873 874 if (!fileProperties.containsKey("default.java-home")) 875 { 876 otherProperties.put("default.java-home", javaHome); 877 } 878 879 writeSetOpenDSJavaHome(installPath, args, otherProperties); 880 } 881 882 private void putBooleanPropertyFrom( 883 final String propertyName, final Properties propertiesSource, final Map<String, String> destMap) 884 { 885 final String propertyValue = propertiesSource.getProperty(propertyName); 886 if (propertyValue == null || !("true".equalsIgnoreCase(propertyValue) || "false".equalsIgnoreCase(propertyValue))) 887 { 888 destMap.put(propertyName, "false"); 889 } 890 else 891 { 892 destMap.put("overwrite-env-java-home", propertyValue.toLowerCase()); 893 } 894 } 895 896 /** 897 * Tries to figure out a new JavaArguments object that works, based on the 898 * provided JavaArguments. It is more efficient to call this method if we are 899 * sure that the provided JavaArguments object does not work. 900 * 901 * @param origJavaArguments 902 * the java arguments that does not work. 903 * @param javaHome 904 * the java home to be used to test the java arguments. 905 * @param installPath 906 * the install path. 907 * @return a working JavaArguments object. 908 */ 909 private JavaArguments getBestEffortArguments(JavaArguments origJavaArguments, String javaHome, String installPath) 910 { 911 JavaArguments memArgs = new JavaArguments(); 912 memArgs.setInitialMemory(origJavaArguments.getInitialMemory()); 913 memArgs.setMaxMemory(origJavaArguments.getMaxMemory()); 914 String m = memArgs.getStringArguments(); 915 boolean supportsMemory = false; 916 if (m.length() > 0) 917 { 918 supportsMemory = Utils.supportsOption(m, javaHome, installPath); 919 } 920 921 JavaArguments additionalArgs = new JavaArguments(); 922 additionalArgs.setAdditionalArguments(origJavaArguments.getAdditionalArguments()); 923 String a = additionalArgs.getStringArguments(); 924 boolean supportsAdditional = false; 925 if (a.length() > 0) 926 { 927 supportsAdditional = Utils.supportsOption(a, javaHome, installPath); 928 } 929 930 JavaArguments javaArgs = new JavaArguments(); 931 if (supportsMemory) 932 { 933 javaArgs.setInitialMemory(origJavaArguments.getInitialMemory()); 934 javaArgs.setMaxMemory(origJavaArguments.getMaxMemory()); 935 } 936 else 937 { 938 // Try to figure out a smaller amount of memory. 939 long currentMaxMemory = Runtime.getRuntime().maxMemory(); 940 int maxMemory = origJavaArguments.getMaxMemory(); 941 if (maxMemory != -1) 942 { 943 maxMemory = maxMemory / 2; 944 while (ONE_MEGABYTE * maxMemory < currentMaxMemory 945 && !Utils.supportsOption(JavaArguments.getMaxMemoryArgument(maxMemory), javaHome, installPath)) 946 { 947 maxMemory = maxMemory / 2; 948 } 949 950 if (ONE_MEGABYTE * maxMemory > currentMaxMemory) 951 { 952 // Supports this option. 953 javaArgs.setMaxMemory(maxMemory); 954 } 955 } 956 } 957 if (supportsAdditional) 958 { 959 javaArgs.setAdditionalArguments(origJavaArguments.getAdditionalArguments()); 960 } 961 return javaArgs; 962 } 963 964 private List<String> getJavaPropertiesFileComments(String propertiesFile) throws IOException 965 { 966 ArrayList<String> commentLines = new ArrayList<>(); 967 BufferedReader reader = new BufferedReader(new FileReader(propertiesFile)); 968 String line; 969 while ((line = reader.readLine()) != null) 970 { 971 String trimmedLine = line.trim(); 972 if (trimmedLine.startsWith("#") || trimmedLine.length() == 0) 973 { 974 commentLines.add(line); 975 } 976 else 977 { 978 break; 979 } 980 } 981 return commentLines; 982 } 983 984 private Properties getJavaPropertiesFileContents(String propertiesFile) throws IOException 985 { 986 Properties fileProperties = new Properties(); 987 try (FileInputStream fs = new FileInputStream(propertiesFile)) 988 { 989 fileProperties.load(fs); 990 } 991 catch (Throwable t) 992 { /* do nothing */ 993 } 994 return fileProperties; 995 } 996 997 private String getPropertiesFileName(String installPath) 998 { 999 String configDir = Utils.getPath( 1000 Utils.getInstancePathFromInstallPath(installPath), CONFIG_PATH_RELATIVE); 1001 return Utils.getPath(configDir, DEFAULT_JAVA_PROPERTIES_FILE); 1002 } 1003 1004 /** 1005 * Writes the set-java-home file that is used by the scripts to set the java 1006 * home and the java arguments. Since the set-java-home file is created and 1007 * may be changed, it's created under the instancePath. 1008 * 1009 * @param installPath 1010 * the install path of the server. 1011 * @param arguments 1012 * a Map containing as key the name of the script and as value, the 1013 * java arguments to be set for the script. 1014 * @param otherProperties 1015 * other properties that must be set in the file. 1016 * @throws IOException 1017 * if an error occurred writing the file. 1018 */ 1019 private void writeSetOpenDSJavaHome(String installPath, Map<String, JavaArguments> arguments, 1020 Map<String, String> otherProperties) throws IOException 1021 { 1022 String propertiesFile = getPropertiesFileName(installPath); 1023 List<String> commentLines = getJavaPropertiesFileComments(propertiesFile); 1024 try (BufferedWriter writer = new BufferedWriter(new FileWriter(propertiesFile, false))) 1025 { 1026 for (String line: commentLines) 1027 { 1028 writer.write(line); 1029 writer.newLine(); 1030 } 1031 1032 for (String key : otherProperties.keySet()) 1033 { 1034 writer.write(key + "=" + otherProperties.get(key)); 1035 writer.newLine(); 1036 } 1037 1038 for (String scriptName : arguments.keySet()) 1039 { 1040 String argument = arguments.get(scriptName).getStringArguments(); 1041 writer.newLine(); 1042 writer.write(getJavaArgPropertyForScript(scriptName) + "=" + argument); 1043 } 1044 } 1045 1046 String libDir = Utils.getPath( 1047 Utils.getInstancePathFromInstallPath(installPath), LIBRARIES_PATH_RELATIVE); 1048 // Create directory if it doesn't exist yet 1049 File fLib = new File(libDir); 1050 if (!fLib.exists()) 1051 { 1052 fLib.mkdir(); 1053 } 1054 final String destinationFile = Utils.getPath(libDir, isWindows() ? SET_JAVA_PROPERTIES_FILE_WINDOWS 1055 : SET_JAVA_PROPERTIES_FILE_UNIX); 1056 // Launch the script 1057 int returnValue = JavaPropertiesTool.mainCLI( 1058 "--propertiesFile", propertiesFile, "--destinationFile", destinationFile, "--quiet"); 1059 if (JavaPropertiesTool.ErrorReturnCode.SUCCESSFUL.getReturnCode() != returnValue && 1060 JavaPropertiesTool.ErrorReturnCode.SUCCESSFUL_NOP.getReturnCode() != returnValue) 1061 { 1062 logger.warn(LocalizableMessage.raw("Error creating java home scripts, error code: " + returnValue)); 1063 throw new IOException(ERR_ERROR_CREATING_JAVA_HOME_SCRIPTS.get(returnValue).toString()); 1064 } 1065 } 1066 1067 /** 1068 * Returns the java argument property for a given script. 1069 * 1070 * @param scriptName 1071 * the script name. 1072 * @return the java argument property for a given script. 1073 */ 1074 private static String getJavaArgPropertyForScript(String scriptName) 1075 { 1076 return scriptName + ".java-args"; 1077 } 1078 1079 /** 1080 * If the log message is of type "[03/Apr/2008:21:25:43 +0200] category=JEB 1081 * severity=NOTICE msgID=8847454 Processed 1 entries, imported 0, skipped 1, 1082 * rejected 0 and migrated 0 in 1 seconds (average rate 0.0/sec)" returns the 1083 * message part. Returns <CODE>null</CODE> otherwise. 1084 * 1085 * @param msg 1086 * the message to be parsed. 1087 * @return the parsed import message. 1088 */ 1089 public String getImportProgressMessage(String msg) 1090 { 1091 if (msg != null && (msg.contains("msgID=" + BackendMessages.NOTE_IMPORT_FINAL_STATUS.ordinal()) 1092 || msg.contains("msgID=" + BackendMessages.NOTE_IMPORT_PROGRESS_REPORT.ordinal()))) 1093 { 1094 int index = msg.indexOf("msg="); 1095 if (index != -1) 1096 { 1097 return msg.substring(index + 4); 1098 } 1099 } 1100 return null; 1101 } 1102}