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