/* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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-2013 ForgeRock AS */ package org.opends.quicksetup; import static org.opends.messages.QuickSetupMessages.*; import java.io.*; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.logging.Level; import java.util.logging.Logger; import org.opends.messages.Message; import org.opends.quicksetup.util.Utils; import org.opends.server.util.SetupUtils; /** * This class represents the physical state of an OpenDJ installation. All the * operations are dependent upon the root directory that is specified in the * constructor. */ public final class Installation { /** Relative path to bootstrap OpenDJ jar file. */ public static final String OPENDJ_BOOTSTRAP_JAR_RELATIVE_PATH = "lib/bootstrap.jar"; /** * The relative path where all the Windows binaries (batch files) are. */ public static final String WINDOWS_BINARIES_PATH_RELATIVE = "bat"; /** * The relative path where all the UNIX binaries (scripts) are. */ public static final String UNIX_BINARIES_PATH_RELATIVE = "bin"; /** * The relative path where all the MacOS X Applications are. */ public static final String MAC_APPLICATIONS_PATH_RELATIVE = "bin"; /** * The relative path where all the libraries (jar files) are. */ public static final String LIBRARIES_PATH_RELATIVE = SetupUtils.LIBRARIES_PATH_RELATIVE; /** * The relative path where the resources directory (to customize the product) * is. */ public static final String RESOURCES_PATH_RELATIVE = "resources"; /** * The relative path where customer classes are. */ public static final String CLASSES_PATH_RELATIVE = "classes"; /** * The relative path where the database files are. */ public static final String DATABASES_PATH_RELATIVE = "db"; /** * The relative path where the log files are. */ public static final String LOGS_PATH_RELATIVE = "logs"; /** * The relative path where the LDIF files are. */ public static final String LDIFS_PATH_RELATIVE = "ldif"; /** * The relative path where the backup files are. */ public static final String BACKUPS_PATH_RELATIVE = "bak"; /** * The relative path where the config files are. */ public static final String CONFIG_PATH_RELATIVE = "config"; /** * The relative path where the config files are. */ public static final String HISTORY_PATH_RELATIVE = "history"; /** * Path to the config/upgrade directory where upgrade base files are stored. */ public static final String UPGRADE_PATH = "upgrade"; /** * Relative path to the locks directory. */ public static final String LOCKS_PATH_RELATIVE = "locks"; /** * Relative path to the locks directory. */ public static final String TMP_PATH_RELATIVE = "tmp"; /** * The relative path to the current Configuration LDIF file. */ public static final String CURRENT_CONFIG_FILE_NAME = "config.ldif"; /** * The relative path to the current Configuration LDIF file. */ public static final String BASE_CONFIG_FILE_PREFIX = "config.ldif."; /** * The path to the default instance. public static final String DEFAULT_INSTANCE_PATH = "/var/opendj"; */ /** * The relative path to the instance.loc file. */ public static final String INSTANCE_LOCATION_PATH_RELATIVE = "instance.loc"; /** * The path to the instance.loc file. */ public static final String INSTANCE_LOCATION_PATH = "/etc/opendj/" + INSTANCE_LOCATION_PATH_RELATIVE; /** * The relative path to tmpl_instance. */ public static final String TEMPLATE_RELATIVE_PATH = "template"; /** * The relative path to buildinfo file. */ public static final String BUILDINFO_RELATIVE_PATH = "buildinfo"; /** * The UNIX setup script file name. */ public static final String UNIX_SETUP_FILE_NAME = "setup"; /** * The Windows setup batch file name. */ public static final String WINDOWS_SETUP_FILE_NAME = "setup.bat"; /** * The UNIX uninstall script file name. */ public static final String UNIX_UNINSTALL_FILE_NAME = "uninstall"; /** * The Windows uninstall batch file name. */ public static final String WINDOWS_UNINSTALL_FILE_NAME = "uninstall.bat"; /** * The UNIX upgrade script file name. */ public static final String UNIX_UPGRADE_FILE_NAME = "upgrade"; /** * The UNIX configure script file name. */ public static final String UNIX_CONFIGURE_FILE_NAME = "configure"; /** * The UNIX start script file name. */ public static final String UNIX_START_FILE_NAME = "start-ds"; /** * The Windows start batch file name. */ public static final String WINDOWS_START_FILE_NAME = "start-ds.bat"; /** * The UNIX stop script file name. */ public static final String UNIX_STOP_FILE_NAME = "stop-ds"; /** * The Windows stop batch file name. */ public static final String WINDOWS_STOP_FILE_NAME = "stop-ds.bat"; /** * The UNIX control panel script file name. */ public static final String UNIX_CONTROLPANEL_FILE_NAME = "control-panel"; /** * The Windows control panel batch file name. */ public static final String WINDOWS_CONTROLPANEL_FILE_NAME = "control-panel.bat"; /** * The MacOS X Java application stub name. */ public static final String MAC_JAVA_APP_STUB_NAME = "JavaApplicationStub"; /** * The MacOS X control panel application bundle name. */ public static final String MAC_CONTROLPANEL_FILE_NAME = "ControlPanel.app"; /** * The UNIX status command line script file name. */ public static final String UNIX_STATUSCLI_FILE_NAME = "status"; /** * The Windows status command line batch file name. */ public static final String WINDOWS_STATUSCLI_FILE_NAME = "status.bat"; /** * The UNIX import LDIF script file name. */ public static final String UNIX_IMPORT_LDIF = "import-ldif"; /** * The Windows import LDIF batch file name. */ public static final String WINDOWS_IMPORT_LDIF = "import-ldif.bat"; /** * Name of the file kept in the history directory containing logs of upgrade * and reversions. */ public static final String HISTORY_LOG_FILE_NAME = "log"; /** * The default java properties file. */ public static final String DEFAULT_JAVA_PROPERTIES_FILE = "java.properties"; /** * The default java properties file relative path. */ public static final String RELATIVE_JAVA_PROPERTIES_FILE = CONFIG_PATH_RELATIVE + File.separator + "java.properties"; /** * The set java home and arguments properties file for Windows. */ public static final String SET_JAVA_PROPERTIES_FILE_WINDOWS = "set-java-home.bat"; /** * script utils file for UNIX systems. */ public static final String SCRIPT_UTIL_FILE_UNIX = "_script-util.sh"; /** * script utils file for Windows. */ public static final String SCRIPT_UTIL_FILE_WINDOWS = "_script-util.bat"; /** * The set java home and arguments properties file for UNIX systems. */ public static final String SET_JAVA_PROPERTIES_FILE_UNIX = "set-java-home"; /** * Directories required to be present for this installation to be considered * valid. */ public static final String[] REQUIRED_DIRECTORIES = new String[] { CONFIG_PATH_RELATIVE, DATABASES_PATH_RELATIVE, LIBRARIES_PATH_RELATIVE }; /** * Performs validation on the specified file to make sure that it is an actual * OpenDJ installation. * * @param rootDirectory * File directory candidate * @throws IllegalArgumentException * if root directory does not appear to be an OpenDJ installation * root. The thrown exception contains a localized message * indicating the reason why <code>rootDirectory</code> is not a * valid OpenDJ install root. */ static public void validateRootDirectory(File rootDirectory) throws IllegalArgumentException { Message failureReason = null; if (rootDirectory == null) { failureReason = INFO_ERROR_INSTALL_ROOT_DIR_NULL.get(); } else if (!rootDirectory.exists()) { failureReason = INFO_ERROR_INSTALL_ROOT_DIR_NO_EXIST.get(Utils .getPath(rootDirectory)); } else if (!rootDirectory.isDirectory()) { failureReason = INFO_ERROR_INSTALL_ROOT_DIR_NOT_DIR.get(Utils .getPath(rootDirectory)); } else { String[] children = rootDirectory.list(); if (children != null) { Set<String> childrenSet = new HashSet<String>(Arrays.asList(children)); for (String dir : REQUIRED_DIRECTORIES) { if (!childrenSet.contains(dir)) { failureReason = INFO_ERROR_INSTALL_ROOT_DIR_NO_DIR.get( Utils.getPath(rootDirectory), dir); } } } else { failureReason = INFO_ERROR_INSTALL_ROOT_DIR_EMPTY.get(Utils .getPath(rootDirectory)); } } if (failureReason != null) { throw new IllegalArgumentException(failureReason.toString()); } } static private Installation local; /** * Obtains the installation by reading the classpath of the running JVM to * determine the location of the jars and determine the installation root. * * @return Installation obtained by reading the classpath */ static public Installation getLocal() { if (local == null) { // This allows testing of configuration components when the OpenDJ.jar // in the classpath does not necessarily point to the server's String installRoot = System.getProperty("org.opends.quicksetup.Root"); String instanceRoot = System .getProperty("org.opends.quicksetup.instance"); if (installRoot == null) { installRoot = Utils.getInstallPathFromClasspath(); } if (instanceRoot == null) { instanceRoot = Utils.getInstancePathFromInstallPath(installRoot); } local = new Installation(installRoot, instanceRoot); } return local; } static private final Logger LOG = Logger.getLogger(Installation.class .getName()); private File rootDirectory; private File instanceDirectory; private Status status; private Configuration configuration; private Configuration baseConfiguration; private BuildInformation buildInformation; private BuildInformation instanceInformation; /** * Creates a new instance from a root directory specified as a string. * * @param rootDirectory * of this installation * @param instanceRootDirectory * The instance root directory */ public Installation(String rootDirectory, String instanceRootDirectory) { this(new File(rootDirectory), new File(instanceRootDirectory)); } /** * Creates a new instance from a root directory specified as a File. * * @param rootDirectory * of this installation * @param instanceDirectory * of the instance */ public Installation(File rootDirectory, File instanceDirectory) { setRootDirectory(rootDirectory); setInstanceDirectory(instanceDirectory); } /** * Gets the top level directory of an OpenDJ installation. * * @return File object representing the top level directory of and OpenDJ * installation */ public File getRootDirectory() { return this.rootDirectory; } /** * Gets the top level directory of an OpenDJ instance. * * @return File object representing the top level directory of and OpenDK * installation */ public File getInstanceDirectory() { return this.instanceDirectory; } /** * Sets the root directory of this installation. * * @param rootDirectory * File of this installation */ public void setRootDirectory(File rootDirectory) { // Hold off on doing validation of rootDirectory since // some applications (like the Installer) create an Installation // before the actual bits have been laid down on the file system. this.rootDirectory = rootDirectory; // Obtaining build information is a fairly time consuming operation. // Try to get a head start if possible. if (isValid(rootDirectory)) { try { BuildInformation bi = getBuildInformation(); LOG.log(Level.INFO, "build info for " + rootDirectory.getName() + ": " + bi); } catch (ApplicationException e) { LOG.log(Level.INFO, "error determining build information", e); } } } /** * Sets the root directory of this instance. * * @param instanceDirectory * File of this instance */ public void setInstanceDirectory(File instanceDirectory) { // Hold off on doing validation of rootDirectory since // some applications (like the Installer) create an Installation // before the actual bits have been laid down on the filesyste. this.instanceDirectory = instanceDirectory; // Obtaining build information is a fairly time consuming operation. // Try to get a head start if possible. if (isValid(instanceDirectory)) { try { BuildInformation bi = getBuildInformation(); LOG.log(Level.INFO, "build info for " + instanceDirectory.getName() + ": " + bi); } catch (ApplicationException e) { LOG.log(Level.INFO, "error determining build information", e); } } } /** * Indicates whether or not this installation appears to be an actual OpenDJ * installation. * * @param file * The root directory * @return boolean where true indicates that this does indeed appear to be a * valid OpenDJ installation; false otherwise */ public boolean isValid(File file) { boolean valid = true; try { validateRootDirectory(file); } catch (IllegalArgumentException e) { valid = false; } return valid; } /** * Creates a string explaining why this is not a legitimate OpenDJ * installation. Null if this is in fact a vaild installation. * * @return localized message indicating the reason this is not an OpenDJ * installation */ public String getInvalidityReason() { String reason = null; try { validateRootDirectory(rootDirectory); } catch (IllegalArgumentException e) { reason = e.getLocalizedMessage(); } return reason; } /** * Gets the Configuration object representing this file. The current * configuration is stored in config/config.ldif. * * @return Configuration representing the current configuration. */ public Configuration getCurrentConfiguration() { if (configuration == null) { configuration = new Configuration(this, getCurrentConfigurationFile()); } return configuration; } /** * Gets the Configuration object representing this file. The base * configuration is stored in config/upgrade/config.ldif.[svn rev]. * * @return Configuration object representing the base configuration. * @throws ApplicationException * if there was a problem determining the svn rev number. */ public Configuration getBaseConfiguration() throws ApplicationException { if (baseConfiguration == null) { baseConfiguration = new Configuration(this, getBaseConfigurationFile()); } return baseConfiguration; } /** * Gets the current status of this installation. * * @return Status object representing the state of this installation. */ public Status getStatus() { if (status == null) { status = new Status(this); } return status; } /** * Returns the path to the libraries. * * @return the path to the libraries. */ public File getLibrariesDirectory() { return new File(getRootDirectory(), LIBRARIES_PATH_RELATIVE); } /** * Returns the path to the resources directory. * * @return the path to the resources directory. */ public File getResourcesDirectory() { return new File(getRootDirectory(), RESOURCES_PATH_RELATIVE); } /** * Returns the path to the classes directory. * * @return the path to the classes directory. */ public File getClassesDirectory() { return new File(getRootDirectory(), CLASSES_PATH_RELATIVE); } /** * Creates a File object representing config/upgrade/schema.ldif.current which * the server creates the first time it starts if there are schema * customizations. * * @return File object with no */ public File getSchemaConcatFile() { return new File(getConfigurationUpgradeDirectory(), "schema.ldif.current"); } /** * Creates a File object representing config/upgrade/schema.ldif.current which * the server creates the first time it starts if there are schema * customizations. * * @return File object with no * @throws ApplicationException * if there was a problem determining the svn revision number */ public File getBaseSchemaFile() throws ApplicationException { return new File(getConfigurationUpgradeDirectory(), "schema.ldif." + getInstanceSvnRev().toString()); } /** * Creates a File object representing config/upgrade/schema.ldif.current which * the server creates the first time it starts if there are schema * customizations. * * @return File object with no * @throws ApplicationException * if there was a problem determining the svn revision number */ public File getBaseConfigurationFile() throws ApplicationException { return new File(getConfigurationUpgradeDirectory(), BASE_CONFIG_FILE_PREFIX + getInstanceSvnRev().toString()); } /** * Gets the SVN revision number of the build. * * @return Integer representing the svn number * @throws ApplicationException * if for some reason the number could not be determined */ public Integer getSvnRev() throws ApplicationException { BuildInformation bi = getBuildInformation(); return bi.getRevisionNumber(); } /** * Gets the SVN revision number of the instance. * * @return Integer representing the svn number * @throws ApplicationException * if for some reason the number could not be determined */ public Integer getInstanceSvnRev() throws ApplicationException { BuildInformation bi = getInstanceBuildInformation(); return bi.getRevisionNumber(); } /** * Returns the path to the configuration file of the directory server. Note * that this method assumes that this code is being run locally. * * @return the path of the configuration file of the directory server. */ public File getCurrentConfigurationFile() { return new File(getConfigurationDirectory(), CURRENT_CONFIG_FILE_NAME); } /** * Returns the relative path of the directory containing the binaries/scripts * of the Open DS installation. The path is relative to the installation path. * * @return the relative path of the directory containing the binaries/scripts * of the Open DS installation. */ public File getBinariesDirectory() { File binPath; if (Utils.isWindows()) { binPath = new File(getRootDirectory(), WINDOWS_BINARIES_PATH_RELATIVE); } else { binPath = new File(getRootDirectory(), UNIX_BINARIES_PATH_RELATIVE); } return binPath; } /** * Returns the path to the database files under the install path. * * @return the path to the database files under the install path. */ public File getDatabasesDirectory() { return new File(getInstanceDirectory(), DATABASES_PATH_RELATIVE); } /** * Returns the path to the backup files under the install path. * * @return the path to the backup files under the install path. */ public File getBackupDirectory() { return new File(getInstanceDirectory(), BACKUPS_PATH_RELATIVE); } /** * Returns the path to the config files under the install path. * * @return the path to the config files under the install path. */ public File getConfigurationDirectory() { return new File(getInstanceDirectory(), CONFIG_PATH_RELATIVE); } /** * Returns the path to the log files under the install path. * * @return the path to the log files under the install path. */ public File getLogsDirectory() { return new File(getInstanceDirectory(), LOGS_PATH_RELATIVE); } /** * Returns the directory where the lock files are stored. * * @return the path to the lock files. */ public File getLocksDirectory() { return new File(getInstanceDirectory(), LOCKS_PATH_RELATIVE); } /** * Gets the directory used to store the template configuration. * * @return The directory used to store the template configuration. */ public File getTemplateDirectory() { return new File(getRootDirectory(), TEMPLATE_RELATIVE_PATH); } /** * Gets the directory used to store files temporarily. * * @return File temporary directory */ public File getTemporaryDirectory() { return new File(getInstanceDirectory(), TMP_PATH_RELATIVE); } /** * Returns the directory where the lock files are stored. * * @return the path to the lock files. */ public File getHistoryDirectory() { return new File(getInstanceDirectory(), HISTORY_PATH_RELATIVE); } /** * Creates a new directory in the history directory appropriate for backing up * an installation during an upgrade. * * @return File representing a new backup directory. The directory can be * assumed to exist if this method returns cleanly. * @throws IOException * if an error occurred creating the directory. */ public File createHistoryBackupDirectory() throws IOException { File backupDirectory = new File(getHistoryDirectory(), Long.toString(System .currentTimeMillis())); if (backupDirectory.exists()) { backupDirectory.delete(); } if (!backupDirectory.mkdirs()) { throw new IOException("failed to create history backup directory"); } return backupDirectory; } /** * Gets the log file where the history of upgrades and reversions is kept. * * @return File containing upgrade/reversion history. */ public File getHistoryLogFile() { return new File(getHistoryDirectory(), HISTORY_LOG_FILE_NAME); } /** * Gets the directory config/upgrade. * * @return File representing the config/upgrade directory */ public File getConfigurationUpgradeDirectory() { return new File(getConfigurationDirectory(), UPGRADE_PATH); } /** * Gets the directory where the upgrader stores files temporarily. * * @return File representing the upgrader's temporary directory */ public File getTemporaryUpgradeDirectory() { return new File(getTemporaryDirectory(), UPGRADE_PATH); } /** * Gets the file for invoking a particular command appropriate for the current * operating system. * * @param command * namd of the command * @return File representing the command */ public File getCommandFile(String command) { File commandFile; if (Utils.isWindows()) { commandFile = new File(getBinariesDirectory(), command + ".bat"); } else { commandFile = new File(getBinariesDirectory(), command); } return commandFile; } /** * Gets the file responsible for stopping the server appropriate for the * current operating system. * * @return File representing the stop command */ public File getServerStartCommandFile() { File startCommandFile; if (Utils.isWindows()) { startCommandFile = new File(getBinariesDirectory(), WINDOWS_START_FILE_NAME); } else { startCommandFile = new File(getBinariesDirectory(), UNIX_START_FILE_NAME); } return startCommandFile; } /** * Gets the file responsible for stopping the server appropriate for the * current operating system. * * @return File representing the stop command */ public File getServerStopCommandFile() { File stopCommandFile; if (Utils.isWindows()) { stopCommandFile = new File(getBinariesDirectory(), WINDOWS_STOP_FILE_NAME); } else { stopCommandFile = new File(getBinariesDirectory(), UNIX_STOP_FILE_NAME); } return stopCommandFile; } /** * Returns the 'ldif' directory. * * @return the 'ldif' directory. */ public File getLdifDirectory() { return new File(getRootDirectory(), LDIFS_PATH_RELATIVE); } /** * Returns the path to the quicksetup jar file. * * @return the path to the quicksetup jar file. */ public File getQuicksetupJarFile() { return new File(getLibrariesDirectory(), "quicksetup.jar"); } /** * Returns the path to the opends jar file. * * @return the path to the opends jar file. */ public File getOpenDSJarFile() { return new File(getLibrariesDirectory(), "OpenDJ.jar"); } /** * Returns the path to the uninstall.bat file. * * @return the path to the uninstall.bat file. */ public File getUninstallBatFile() { return new File(getRootDirectory(), "uninstall.bat"); } /** * Gets the control panel command file appropriate for the current operating * system. * * @return File object representing the control panel command */ public File getControlPanelCommandFile() { File controlPanelCommandFile; if (Utils.isWindows()) { controlPanelCommandFile = new File(getBinariesDirectory(), WINDOWS_CONTROLPANEL_FILE_NAME); } else if (Utils.isMacOS()) { controlPanelCommandFile = new File(getRootDirectory() + File.separator + MAC_APPLICATIONS_PATH_RELATIVE, MAC_CONTROLPANEL_FILE_NAME); } else { controlPanelCommandFile = new File(getBinariesDirectory(), UNIX_CONTROLPANEL_FILE_NAME); } return controlPanelCommandFile; } /** * Gets information about the build that was used to produce the bits for this * installation. * * @return BuildInformation object describing this installation * @throws ApplicationException * if there is a problem obtaining the build information */ public BuildInformation getBuildInformation() throws ApplicationException { return getBuildInformation(true); } /** * Gets information about the build that was used to produce the bits for this * installation. * * @param useCachedVersion * where true indicates that a potentially cached version of the * build information is acceptable for use; false indicates the the * build information will be created from scratch which is * potentially time consuming * @return BuildInformation object describing this installation * @throws ApplicationException * if there is a problem obtaining the build information */ public BuildInformation getBuildInformation(boolean useCachedVersion) throws ApplicationException { if (buildInformation == null || !useCachedVersion) { FutureTask<BuildInformation> ft = new FutureTask<BuildInformation>( new Callable<BuildInformation>() { @Override public BuildInformation call() throws ApplicationException { return BuildInformation.create(Installation.this); } }); new Thread(ft).start(); try { buildInformation = ft.get(); } catch (InterruptedException e) { LOG.log(Level.INFO, "interrupted trying to get build information", e); } catch (ExecutionException e) { throw (ApplicationException) e.getCause(); } } return buildInformation; } /** * Gets information about the build that was used to produce the instance. * * @return BuildInformation object describing this instance */ public BuildInformation getInstanceBuildInformation() { return getInstanceBuildInformation(true); } /** * Gets information about the build that was used to produce the instance. * * @param useCachedVersion * where true indicates that a potentially cached version of the * build information is acceptable for use; false indicates the build * information will be created from scratch which is potentially time * consuming * @return BuildInformation object describing this instance */ public BuildInformation getInstanceBuildInformation(boolean useCachedVersion) { if (instanceInformation == null || !useCachedVersion) { try { File bif = new File(getConfigurationDirectory(), BUILDINFO_RELATIVE_PATH); if (bif.exists()) { BufferedReader reader = new BufferedReader(new FileReader(bif)); // Read the first line and close the file. String line; try { line = reader.readLine(); instanceInformation = BuildInformation.fromBuildString(line); } finally { try { reader.close(); } catch (Exception e) { // do nothing } } } else { return getBuildInformation(); } } catch (Exception e) { LOG.log(Level.SEVERE, "error getting build information for " + "current instance", e); } } return instanceInformation; } }