/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2015 ForgeRock AS */ package org.opends.quicksetup.installer; import static org.opends.messages.QuickSetupMessages.*; import static org.opends.quicksetup.util.Utils.*; import static com.forgerock.opendj.cli.Utils.*; import static com.forgerock.opendj.util.OperatingSystem.*; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.TreeSet; import javax.naming.directory.DirContext; import javax.naming.ldap.InitialLdapContext; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.config.server.ConfigException; import org.opends.guitools.controlpanel.util.Utilities; import org.opends.messages.BackendMessages; import org.opends.messages.CoreMessages; import org.opends.messages.ReplicationMessages; import org.opends.quicksetup.Application; import org.opends.quicksetup.ApplicationException; import org.opends.quicksetup.Installation; import org.opends.quicksetup.JavaArguments; import org.opends.quicksetup.ReturnCode; import org.opends.quicksetup.UserData; import org.opends.quicksetup.util.OutputReader; import org.opends.quicksetup.util.Utils; import org.opends.server.admin.ManagedObjectDefinition; import org.opends.server.admin.ManagedObjectNotFoundException; import org.opends.server.admin.PropertyException; import org.opends.server.admin.client.ManagementContext; import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor; import org.opends.server.admin.client.ldap.LDAPManagementContext; import org.opends.server.admin.std.client.BackendCfgClient; import org.opends.server.admin.std.client.CryptoManagerCfgClient; import org.opends.server.admin.std.client.ReplicationDomainCfgClient; import org.opends.server.admin.std.client.ReplicationServerCfgClient; import org.opends.server.admin.std.client.ReplicationSynchronizationProviderCfgClient; import org.opends.server.admin.std.client.RootCfgClient; import org.opends.server.admin.std.meta.BackendCfgDefn; import org.opends.server.admin.std.meta.ReplicationDomainCfgDefn; import org.opends.server.admin.std.meta.ReplicationServerCfgDefn; import org.opends.server.admin.std.meta.ReplicationSynchronizationProviderCfgDefn; import org.opends.server.admin.std.server.BackendCfg; import org.opends.server.backends.task.TaskState; import org.opends.server.core.DirectoryServer; import org.opends.server.tools.ConfigureDS; import org.opends.server.tools.ConfigureWindowsService; import org.opends.server.tools.JavaPropertiesTool; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.ExistingFileBehavior; import org.opends.server.types.LDIFExportConfig; import org.opends.server.types.OpenDsException; import org.opends.server.util.LDIFException; import org.opends.server.util.LDIFWriter; import org.opends.server.util.SetupUtils; import org.opends.server.util.StaticUtils; /** * This is the only class that uses classes in org.opends.server (excluding the * case of DynamicConstants, SetupUtils and CertificateManager * which are already included in quicksetup.jar). * * Important note: do not include references to this class until OpenDS.jar has * been loaded. These classes must be loaded during Runtime. * The code is written in a way that when we execute the code that uses these * classes the required jar files are already loaded. However these jar files * are not necessarily loaded when we create this class. */ public class InstallerHelper { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); private static final int MAX_ID_VALUE = Short.MAX_VALUE; private static final long ONE_MEGABYTE = 1024L * 1024; /** * Invokes the method ConfigureDS.configMain with the provided parameters. * @param args the arguments to be passed to ConfigureDS.configMain. * @return the return code of the ConfigureDS.configMain method. * @throws ApplicationException if something goes wrong. * @see org.opends.server.tools.ConfigureDS#configMain(String[], * java.io.OutputStream, java.io.OutputStream) */ public int invokeConfigureServer(String[] args) throws ApplicationException { return ConfigureDS.configMain(args, System.out, System.err); } /** * Invokes the import-ldif command-line with the provided parameters. * * @param application * the application that is launching this. * @param args * the arguments to be passed to import-ldif. * @return the return code of the import-ldif call. * @throws IOException * if the process could not be launched. * @throws InterruptedException * if the process was interrupted. */ public int invokeImportLDIF(final Application application, String[] args) throws IOException, InterruptedException { final File installPath = new File(application.getInstallationPath()); final File binPath = new File(installPath, isWindows() ? Installation.WINDOWS_BINARIES_PATH_RELATIVE : Installation.UNIX_BINARIES_PATH_RELATIVE); final File importLDIFPath = new File(binPath, isWindows() ? Installation.WINDOWS_IMPORT_LDIF : Installation.UNIX_IMPORT_LDIF); final ArrayList<String> argList = new ArrayList<>(); argList.add(Utils.getScriptPath(importLDIFPath.getAbsolutePath())); argList.addAll(Arrays.asList(args)); logger.info(LocalizableMessage.raw("import-ldif arg list: " + argList)); final ProcessBuilder processBuilder = new ProcessBuilder(argList.toArray(new String[argList.size()])); final Map<String, String> env = processBuilder.environment(); env.remove(SetupUtils.OPENDJ_JAVA_HOME); env.remove(SetupUtils.OPENDJ_JAVA_ARGS); env.remove("CLASSPATH"); processBuilder.directory(installPath); Process process = null; try { process = processBuilder.start(); final BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream())); new OutputReader(err) { @Override public void processLine(final String line) { logger.warn(LocalizableMessage.raw("import-ldif error log: " + line)); application.notifyListeners(LocalizableMessage.raw(line)); application.notifyListeners(application.getLineBreak()); } }; final BufferedReader out = new BufferedReader(new InputStreamReader(process.getInputStream())); new OutputReader(out) { @Override public void processLine(final String line) { logger.info(LocalizableMessage.raw("import-ldif out log: " + line)); application.notifyListeners(LocalizableMessage.raw(line)); application.notifyListeners(application.getLineBreak()); } }; return process.waitFor(); } finally { if (process != null) { closeProcessStream(process.getErrorStream(), "error"); closeProcessStream(process.getOutputStream(), "output"); } } } private void closeProcessStream(final Closeable stream, final String streamName) { try { stream.close(); } catch (Throwable t) { logger.warn(LocalizableMessage.raw("Error closing " + streamName + " stream: " + t, t)); } } /** * Returns the LocalizableMessage ID that corresponds to a successfully started server. * @return the LocalizableMessage ID that corresponds to a successfully started server. */ public String getStartedId() { return String.valueOf(CoreMessages.NOTE_DIRECTORY_SERVER_STARTED.ordinal()); } /** * This methods enables this server as a Windows service. * @throws ApplicationException if something goes wrong. */ public void enableWindowsService() throws ApplicationException { int code = ConfigureWindowsService.enableService(System.out, System.err); LocalizableMessage errorMessage = INFO_ERROR_ENABLING_WINDOWS_SERVICE.get(); switch (code) { case ConfigureWindowsService.SERVICE_ENABLE_SUCCESS: break; case ConfigureWindowsService.SERVICE_ALREADY_ENABLED: break; default: throw new ApplicationException( ReturnCode.WINDOWS_SERVICE_ERROR, errorMessage, null); } } /** * This method disables this server as a Windows service. * @throws ApplicationException if something goes worong. */ public void disableWindowsService() throws ApplicationException { int code = ConfigureWindowsService.disableService(System.out, System.err); if (code == ConfigureWindowsService.SERVICE_DISABLE_ERROR) { throw new ApplicationException( // TODO: fix this message's format string ReturnCode.WINDOWS_SERVICE_ERROR, INFO_ERROR_DISABLING_WINDOWS_SERVICE.get(""), null); } } /** * Creates a template LDIF file with an entry that has as dn the provided * baseDn. * @param baseDn the dn of the entry that will be created in the LDIF file. * @return the File object pointing to the created temporary file. * @throws ApplicationException if something goes wrong. */ public File createBaseEntryTempFile(String baseDn) throws ApplicationException { File ldifFile; try { ldifFile = File.createTempFile("opendj-base-entry", ".ldif"); ldifFile.deleteOnExit(); } catch (IOException ioe) { LocalizableMessage failedMsg = getThrowableMsg(INFO_ERROR_CREATING_TEMP_FILE.get(), ioe); throw new ApplicationException( ReturnCode.FILE_SYSTEM_ACCESS_ERROR, failedMsg, ioe); } try { LDIFExportConfig exportConfig = new LDIFExportConfig( ldifFile.getAbsolutePath(), ExistingFileBehavior.OVERWRITE); LDIFWriter writer = new LDIFWriter(exportConfig); DN dn = DN.valueOf(baseDn); Entry entry = StaticUtils.createEntry(dn); writer.writeEntry(entry); writer.close(); } catch (DirectoryException | LDIFException | IOException de) { throw new ApplicationException( ReturnCode.CONFIGURATION_ERROR, getThrowableMsg(INFO_ERROR_IMPORTING_LDIF.get(), de), de); } catch (Throwable t) { throw new ApplicationException( ReturnCode.BUG, getThrowableMsg( INFO_BUG_MSG.get(), t), t); } return ldifFile; } /** * Deletes a backend on the server. * @param ctx the connection to the server. * @param backendName the name of the backend to be deleted. * @param serverDisplay the server display. * @throws ApplicationException if something goes wrong. */ public void deleteBackend(InitialLdapContext ctx, String backendName, String serverDisplay) throws ApplicationException { try { ManagementContext mCtx = LDAPManagementContext.createFromContext( JNDIDirContextAdaptor.adapt(ctx)); RootCfgClient root = mCtx.getRootConfiguration(); root.removeBackend(backendName); } catch (Throwable t) { throw new ApplicationException( ReturnCode.CONFIGURATION_ERROR, INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get(serverDisplay, t), t); } } /** * Deletes a backend on the server. It assumes the server is stopped. * @param backendName the name of the backend to be deleted. * @throws ApplicationException if something goes wrong. */ public void deleteBackend(String backendName) throws ApplicationException { try { // Read the configuration file. String dn = Utilities.getRDNString("ds-cfg-backend-id", backendName)+",cn=Backends,cn=config"; Utilities.deleteConfigSubtree( DirectoryServer.getConfigHandler(), DN.valueOf(dn)); } catch (OpenDsException | ConfigException ode) { throw new ApplicationException( ReturnCode.CONFIGURATION_ERROR, ode.getMessageObject(), ode); } } /** * Creates a database backend on the server. * * @param ctx * the connection to the server. * @param backendName * the name of the backend to be created. * @param baseDNs * the list of base DNs to be defined on the server. * @param serverDisplay * the server display. * @param backendType * the backend type. * @throws ApplicationException * if something goes wrong. */ public void createBackend(DirContext ctx, String backendName, Set<String> baseDNs, String serverDisplay, ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backendType) throws ApplicationException { try { ManagementContext mCtx = LDAPManagementContext.createFromContext(JNDIDirContextAdaptor.adapt(ctx)); RootCfgClient root = mCtx.getRootConfiguration(); BackendCfgClient backend = root.createBackend(backendType, backendName, null); backend.setEnabled(true); backend.setBaseDN(toByteStrings(baseDNs)); backend.setBackendId(backendName); backend.setWritabilityMode(BackendCfgDefn.WritabilityMode.ENABLED); backend.commit(); } catch (Throwable t) { throw new ApplicationException( ReturnCode.CONFIGURATION_ERROR, INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get(serverDisplay, t), t); } } private Set<DN> toByteStrings(Set<String> strings) throws DirectoryException { Set<DN> results = new HashSet<>(); for (String s : strings) { results.add(DN.valueOf(s)); } return results; } /** * Sets the base DNs on a given backend. * @param ctx the connection to the server. * @param backendName the name of the backend where the base Dns must be * defined. * @param baseDNs the list of base DNs to be defined on the server. * @param serverDisplay the server display. * @throws ApplicationException if something goes wrong. */ public void setBaseDns(InitialLdapContext ctx, String backendName, Set<String> baseDNs, String serverDisplay) throws ApplicationException { try { ManagementContext mCtx = LDAPManagementContext.createFromContext( JNDIDirContextAdaptor.adapt(ctx)); RootCfgClient root = mCtx.getRootConfiguration(); BackendCfgClient backend = root.getBackend(backendName); backend.setBaseDN(toByteStrings(baseDNs)); backend.commit(); } catch (Throwable t) { throw new ApplicationException( ReturnCode.CONFIGURATION_ERROR, INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get(serverDisplay, t), t); } } /** * Configures the replication on a given server. * @param remoteCtx the connection to the server where we want to configure * the replication. * @param replicationServers a Map where the key value is the base dn and * the value is the list of replication servers for that base dn (or domain). * @param replicationPort the replicationPort of the server that is being * configured (it might not exist and the user specified it in the setup). * @param useSecureReplication whether to encrypt connections with the * replication port or not. * @param serverDisplay the server display. * @param usedReplicationServerIds the list of replication server ids that * are already used. * @param usedServerIds the list of server ids (domain ids) that * are already used. * @throws ApplicationException if something goes wrong. * @return a ConfiguredReplication object describing what has been configured. */ public ConfiguredReplication configureReplication( InitialLdapContext remoteCtx, Map<String,Set<String>> replicationServers, int replicationPort, boolean useSecureReplication, String serverDisplay, Set<Integer> usedReplicationServerIds, Set<Integer> usedServerIds) throws ApplicationException { boolean synchProviderCreated; boolean synchProviderEnabled; boolean replicationServerCreated; boolean secureReplicationEnabled; try { ManagementContext mCtx = LDAPManagementContext.createFromContext( JNDIDirContextAdaptor.adapt(remoteCtx)); RootCfgClient root = mCtx.getRootConfiguration(); /* * Configure Synchronization plugin. */ ReplicationSynchronizationProviderCfgClient sync = null; try { sync = (ReplicationSynchronizationProviderCfgClient) root.getSynchronizationProvider("Multimaster Synchronization"); } catch (ManagedObjectNotFoundException monfe) { // It does not exist. } if (sync == null) { ReplicationSynchronizationProviderCfgDefn provider = ReplicationSynchronizationProviderCfgDefn.getInstance(); sync = root.createSynchronizationProvider(provider, "Multimaster Synchronization", new ArrayList<PropertyException>()); sync.setJavaClass( org.opends.server.replication.plugin.MultimasterReplication.class. getName()); sync.setEnabled(Boolean.TRUE); synchProviderCreated = true; synchProviderEnabled = false; } else { synchProviderCreated = false; if (!sync.isEnabled()) { sync.setEnabled(Boolean.TRUE); synchProviderEnabled = true; } else { synchProviderEnabled = false; } } sync.commit(); /* * Configure the replication server. */ ReplicationServerCfgClient replicationServer; if (!sync.hasReplicationServer()) { if (useSecureReplication) { CryptoManagerCfgClient crypto = root.getCryptoManager(); if (!crypto.isSSLEncryption()) { crypto.setSSLEncryption(true); crypto.commit(); secureReplicationEnabled = true; } else { // Only mark as true if we actually change the configuration secureReplicationEnabled = false; } } else { secureReplicationEnabled = false; } int id = getReplicationId(usedReplicationServerIds); usedReplicationServerIds.add(id); replicationServer = sync.createReplicationServer( ReplicationServerCfgDefn.getInstance(), new ArrayList<PropertyException>()); replicationServer.setReplicationServerId(id); replicationServer.setReplicationPort(replicationPort); replicationServerCreated = true; } else { secureReplicationEnabled = false; replicationServer = sync.getReplicationServer(); usedReplicationServerIds.add( replicationServer.getReplicationServerId()); replicationServerCreated = false; } Set<String> servers = replicationServer.getReplicationServer(); if (servers == null) { servers = new HashSet<>(); } Set<String> oldServers = new HashSet<>(servers); for (Set<String> rs : replicationServers.values()) { servers.addAll(rs); } replicationServer.setReplicationServer(servers); replicationServer.commit(); Set<String> newReplicationServers = intersect(servers, oldServers); /* * Create the domains */ String[] domainNames = sync.listReplicationDomains(); if (domainNames == null) { domainNames = new String[]{}; } Set<ConfiguredDomain> domainsConf = new HashSet<>(); ReplicationDomainCfgClient[] domains = new ReplicationDomainCfgClient[domainNames.length]; for (int i=0; i<domains.length; i++) { domains[i] = sync.getReplicationDomain(domainNames[i]); } for (String dn : replicationServers.keySet()) { ReplicationDomainCfgClient domain = null; boolean isCreated; String domainName = null; for (int i = 0; i < domains.length && domain == null; i++) { if (areDnsEqual(dn, domains[i].getBaseDN().toString())) { domain = domains[i]; domainName = domainNames[i]; } } if (domain == null) { int domainId = getReplicationId(usedServerIds); usedServerIds.add(domainId); domainName = getDomainName(domainNames, domainId, dn); domain = sync.createReplicationDomain( ReplicationDomainCfgDefn.getInstance(), domainName, new ArrayList<PropertyException>()); domain.setServerId(domainId); domain.setBaseDN(DN.valueOf(dn)); isCreated = true; } else { isCreated = false; } oldServers = domain.getReplicationServer(); if (oldServers == null) { oldServers = new TreeSet<>(); } servers = replicationServers.get(dn); domain.setReplicationServer(servers); usedServerIds.add(domain.getServerId()); domain.commit(); Set<String> addedServers = intersect(servers, oldServers); ConfiguredDomain domainConf = new ConfiguredDomain(domainName, isCreated, addedServers); domainsConf.add(domainConf); } return new ConfiguredReplication(synchProviderCreated, synchProviderEnabled, replicationServerCreated, secureReplicationEnabled, newReplicationServers, domainsConf); } catch (Throwable t) { throw new ApplicationException( ReturnCode.CONFIGURATION_ERROR, INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get(serverDisplay, t), t); } } private Set<String> intersect(Set<String> set1, Set<String> set2) { Set<String> result = new TreeSet<>(set1); result.removeAll(set2); return result; } /** * Configures the replication on a given server. * * @param remoteCtx * the connection to the server where we want to configure the * replication. * @param replConf * the object describing what was configured. * @param serverDisplay * the server display. * @throws ApplicationException * if something goes wrong. */ public void unconfigureReplication(InitialLdapContext remoteCtx, ConfiguredReplication replConf, String serverDisplay) throws ApplicationException { try { ManagementContext mCtx = LDAPManagementContext.createFromContext(JNDIDirContextAdaptor.adapt(remoteCtx)); RootCfgClient root = mCtx.getRootConfiguration(); final String syncProvider = "Multimaster Synchronization"; // Unconfigure Synchronization plugin. if (replConf.isSynchProviderCreated()) { try { root.removeSynchronizationProvider(syncProvider); } catch (ManagedObjectNotFoundException monfe) { // It does not exist. } } else { try { ReplicationSynchronizationProviderCfgClient sync = (ReplicationSynchronizationProviderCfgClient) root.getSynchronizationProvider(syncProvider); if (replConf.isSynchProviderEnabled()) { sync.setEnabled(Boolean.FALSE); } if (replConf.isReplicationServerCreated()) { sync.removeReplicationServer(); } else if (sync.hasReplicationServer()) { ReplicationServerCfgClient replicationServer = sync.getReplicationServer(); Set<String> replServers = replicationServer.getReplicationServer(); if (replServers != null) { replServers.removeAll(replConf.getNewReplicationServers()); replicationServer.setReplicationServer(replServers); replicationServer.commit(); } } for (ConfiguredDomain domain : replConf.getDomainsConf()) { if (domain.isCreated()) { sync.removeReplicationDomain(domain.getDomainName()); } else { try { ReplicationDomainCfgClient d = sync.getReplicationDomain(domain.getDomainName()); Set<String> replServers = d.getReplicationServer(); if (replServers != null) { replServers.removeAll(domain.getAddedReplicationServers()); d.setReplicationServer(replServers); d.commit(); } } catch (ManagedObjectNotFoundException monfe) { // It does not exist. } } } sync.commit(); } catch (ManagedObjectNotFoundException monfe) { // It does not exist. } } if (replConf.isSecureReplicationEnabled()) { CryptoManagerCfgClient crypto = root.getCryptoManager(); if (crypto.isSSLEncryption()) { crypto.setSSLEncryption(false); crypto.commit(); } } } catch (Throwable t) { throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get( serverDisplay, t), t); } } /** * For the given state provided by a Task tells if the task is done or not. * * @param sState * the String representing the task state. * @return <CODE>true</CODE> if the task is done and <CODE>false</CODE> * otherwise. */ public boolean isDone(String sState) { return TaskState.isDone(TaskState.fromString(sState)); } /** * For the given state provided by a Task tells if the task is successful or * not. * * @param sState * the String representing the task state. * @return <CODE>true</CODE> if the task is successful and <CODE>false</CODE> * otherwise. */ public boolean isSuccessful(String sState) { return TaskState.isSuccessful(TaskState.fromString(sState)); } /** * For the given state provided by a Task tells if the task is complete with * errors or not. * * @param sState * the String representing the task state. * @return <CODE>true</CODE> if the task is complete with errors and * <CODE>false</CODE> otherwise. */ public boolean isCompletedWithErrors(String sState) { return TaskState.COMPLETED_WITH_ERRORS == TaskState.fromString(sState); } /** * For the given state provided by a Task tells if the task is stopped by * error or not. * * @param sState * the String representing the task state. * @return <CODE>true</CODE> if the task is stopped by error and * <CODE>false</CODE> otherwise. */ public boolean isStoppedByError(String sState) { return TaskState.STOPPED_BY_ERROR == TaskState.fromString(sState); } /** * Tells whether the provided log message corresponds to a peers not found * error during the initialization of a replica or not. * * @param logMsg * the log message. * @return <CODE>true</CODE> if the log message corresponds to a peers not * found error during initialization and <CODE>false</CODE> otherwise. */ public boolean isPeersNotFoundError(String logMsg) { return logMsg.contains("=" + ReplicationMessages.ERR_NO_REACHABLE_PEER_IN_THE_DOMAIN.ordinal()); } /** * Returns the ID to be used for a new replication server or domain. * @param usedIds the list of already used ids. * @return the ID to be used for a new replication server or domain. */ public static int getReplicationId(Set<Integer> usedIds) { Random r = new Random(); int id = 0; while (id == 0 || usedIds.contains(id)) { id = r.nextInt(MAX_ID_VALUE); } return id; } /** * Returns the name to be used for a new replication domain. * @param existingDomains the existing domains names. * @param newDomainId the new domain replication id. * @param baseDN the base DN of the domain. * @return the name to be used for a new replication domain. */ public static String getDomainName(String[] existingDomains, int newDomainId, String baseDN) { String domainName = baseDN; boolean nameExists = true; int j = 0; while (nameExists) { boolean found = false; for (int i=0; i<existingDomains.length && !found; i++) { found = existingDomains[i].equalsIgnoreCase(domainName); } if (found) { domainName = baseDN+"-"+j; } else { nameExists = false; } j++; } return domainName; } /** * Writes the set-java-home file that is used by the scripts to set the java * home and the java arguments. * * @param uData * the data provided by the user. * @param installPath * where the server is installed. * @throws IOException * if an error occurred writing the file. */ public void writeSetOpenDSJavaHome(UserData uData, String installPath) throws IOException { String javaHome = System.getProperty("java.home"); if (javaHome == null || javaHome.length() == 0) { javaHome = System.getenv(SetupUtils.OPENDJ_JAVA_HOME); } // Try to transform things if necessary. The following map has as key // the original JavaArgument object and as value the 'transformed' JavaArgument. Map<JavaArguments, JavaArguments> hmJavaArguments = new HashMap<>(); for (String script : uData.getScriptNamesForJavaArguments()) { JavaArguments origJavaArguments = uData.getJavaArguments(script); if (hmJavaArguments.get(origJavaArguments) == null) { if (Utils.supportsOption(origJavaArguments.getStringArguments(), javaHome, installPath)) { // The argument works, so just use it. hmJavaArguments.put(origJavaArguments, origJavaArguments); } else { // We have to fix it somehow: test separately memory and other // arguments to see if something works. JavaArguments transformedArguments = getBestEffortArguments(origJavaArguments, javaHome, installPath); hmJavaArguments.put(origJavaArguments, transformedArguments); } } // else, support is already checked. } Properties fileProperties = getJavaPropertiesFileContents(getPropertiesFileName(installPath)); Map<String, JavaArguments> args = new HashMap<>(); Map<String, String> otherProperties = new HashMap<>(); for (String script : uData.getScriptNamesForJavaArguments()) { JavaArguments origJavaArgument = uData.getJavaArguments(script); JavaArguments transformedJavaArg = hmJavaArguments.get(origJavaArgument); JavaArguments defaultJavaArg = uData.getDefaultJavaArguments(script); // Apply the following policy: overwrite the values in the file only // if the values provided by the user are not the default ones. String propertiesKey = getJavaArgPropertyForScript(script); if (origJavaArgument.equals(defaultJavaArg) && fileProperties.containsKey(propertiesKey)) { otherProperties.put(propertiesKey, fileProperties.getProperty(propertiesKey)); } else { args.put(script, transformedJavaArg); } } putBooleanPropertyFrom("overwrite-env-java-home", fileProperties, otherProperties); putBooleanPropertyFrom("overwrite-env-java-args", fileProperties, otherProperties); if (!fileProperties.containsKey("default.java-home")) { otherProperties.put("default.java-home", javaHome); } writeSetOpenDSJavaHome(installPath, javaHome, args, otherProperties); } private void putBooleanPropertyFrom( final String propertyName, final Properties propertiesSource, final Map<String, String> destMap) { final String propertyValue = propertiesSource.getProperty(propertyName); if (propertyValue == null || !("true".equalsIgnoreCase(propertyValue) || "false".equalsIgnoreCase(propertyValue))) { destMap.put(propertyName, "false"); } else { destMap.put("overwrite-env-java-home", propertyValue.toLowerCase()); } } /** * Tries to figure out a new JavaArguments object that works, based on the * provided JavaArguments. It is more efficient to call this method if we are * sure that the provided JavaArguments object does not work. * * @param origJavaArguments * the java arguments that does not work. * @param javaHome * the java home to be used to test the java arguments. * @param installPath * the install path. * @return a working JavaArguments object. */ private JavaArguments getBestEffortArguments(JavaArguments origJavaArguments, String javaHome, String installPath) { JavaArguments memArgs = new JavaArguments(); memArgs.setInitialMemory(origJavaArguments.getInitialMemory()); memArgs.setMaxMemory(origJavaArguments.getMaxMemory()); String m = memArgs.getStringArguments(); boolean supportsMemory = false; if (m.length() > 0) { supportsMemory = Utils.supportsOption(m, javaHome, installPath); } JavaArguments additionalArgs = new JavaArguments(); additionalArgs.setAdditionalArguments(origJavaArguments.getAdditionalArguments()); String a = additionalArgs.getStringArguments(); boolean supportsAdditional = false; if (a.length() > 0) { supportsAdditional = Utils.supportsOption(a, javaHome, installPath); } JavaArguments javaArgs = new JavaArguments(); if (supportsMemory) { javaArgs.setInitialMemory(origJavaArguments.getInitialMemory()); javaArgs.setMaxMemory(origJavaArguments.getMaxMemory()); } else { // Try to figure out a smaller amount of memory. long currentMaxMemory = Runtime.getRuntime().maxMemory(); int maxMemory = origJavaArguments.getMaxMemory(); if (maxMemory != -1) { maxMemory = maxMemory / 2; while (ONE_MEGABYTE * maxMemory < currentMaxMemory && !Utils.supportsOption(JavaArguments.getMaxMemoryArgument(maxMemory), javaHome, installPath)) { maxMemory = maxMemory / 2; } if (ONE_MEGABYTE * maxMemory > currentMaxMemory) { // Supports this option. javaArgs.setMaxMemory(maxMemory); } } } if (supportsAdditional) { javaArgs.setAdditionalArguments(origJavaArguments.getAdditionalArguments()); } return javaArgs; } private List<String> getJavaPropertiesFileComments(String propertiesFile) throws IOException { ArrayList<String> commentLines = new ArrayList<>(); BufferedReader reader = new BufferedReader(new FileReader(propertiesFile)); String line; while ((line = reader.readLine()) != null) { String trimmedLine = line.trim(); if (trimmedLine.startsWith("#") || trimmedLine.length() == 0) { commentLines.add(line); } else { break; } } return commentLines; } private Properties getJavaPropertiesFileContents(String propertiesFile) throws IOException { FileInputStream fs = null; Properties fileProperties = new Properties(); try { fs = new FileInputStream(propertiesFile); fileProperties.load(fs); } catch (Throwable t) { /* do nothing */ } finally { StaticUtils.close(fs); } return fileProperties; } private String getPropertiesFileName(String installPath) { String configDir = Utils.getPath( Utils.getInstancePathFromInstallPath(installPath), Installation.CONFIG_PATH_RELATIVE); return Utils.getPath(configDir, Installation.DEFAULT_JAVA_PROPERTIES_FILE); } /** * Writes the set-java-home file that is used by the scripts to set the java * home and the java arguments. Since the set-java-home file is created and * may be changed, it's created under the instancePath. * * @param installPath * the install path of the server. * @param javaHome * the java home to be used. * @param arguments * a Map containing as key the name of the script and as value, the * java arguments to be set for the script. * @param otherProperties * other properties that must be set in the file. * @throws IOException * if an error occurred writing the file. */ private void writeSetOpenDSJavaHome(String installPath, String javaHome, Map<String, JavaArguments> arguments, Map<String, String> otherProperties) throws IOException { String propertiesFile = getPropertiesFileName(installPath); List<String> commentLines = getJavaPropertiesFileComments(propertiesFile); BufferedWriter writer = new BufferedWriter(new FileWriter(propertiesFile, false)); for (String line: commentLines) { writer.write(line); writer.newLine(); } for (String key : otherProperties.keySet()) { writer.write(key + "=" + otherProperties.get(key)); writer.newLine(); } for (String scriptName : arguments.keySet()) { String argument = arguments.get(scriptName).getStringArguments(); writer.newLine(); writer.write(getJavaArgPropertyForScript(scriptName) + "=" + argument); } writer.close(); String libDir = Utils.getPath( Utils.getInstancePathFromInstallPath(installPath), Installation.LIBRARIES_PATH_RELATIVE); // Create directory if it doesn't exist yet File fLib = new File(libDir); if (!fLib.exists()) { fLib.mkdir(); } final String destinationFile = Utils.getPath(libDir, isWindows() ? Installation.SET_JAVA_PROPERTIES_FILE_WINDOWS : Installation.SET_JAVA_PROPERTIES_FILE_UNIX); // Launch the script int returnValue = JavaPropertiesTool.mainCLI( "--propertiesFile", propertiesFile, "--destinationFile", destinationFile, "--quiet"); if (JavaPropertiesTool.ErrorReturnCode.SUCCESSFUL.getReturnCode() != returnValue && JavaPropertiesTool.ErrorReturnCode.SUCCESSFUL_NOP.getReturnCode() != returnValue) { logger.warn(LocalizableMessage.raw("Error creating java home scripts, error code: " + returnValue)); throw new IOException(ERR_ERROR_CREATING_JAVA_HOME_SCRIPTS.get(returnValue).toString()); } } /** * Returns the java argument property for a given script. * * @param scriptName * the script name. * @return the java argument property for a given script. */ private static String getJavaArgPropertyForScript(String scriptName) { return scriptName + ".java-args"; } /** * If the log message is of type "[03/Apr/2008:21:25:43 +0200] category=JEB * severity=NOTICE msgID=8847454 Processed 1 entries, imported 0, skipped 1, * rejected 0 and migrated 0 in 1 seconds (average rate 0.0/sec)" returns the * message part. Returns <CODE>null</CODE> otherwise. * * @param msg * the message to be parsed. * @return the parsed import message. */ public String getImportProgressMessage(String msg) { if (msg != null && (msg.contains("msgID=" + BackendMessages.NOTE_IMPORT_FINAL_STATUS.ordinal()) || msg.contains("msgID=" + BackendMessages.NOTE_IMPORT_PROGRESS_REPORT.ordinal()))) { int index = msg.indexOf("msg="); if (index != -1) { return msg.substring(index + 4); } } return null; } }