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