/*
* 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-2014 ForgeRock AS
*/
package org.opends.quicksetup.util;
import static org.forgerock.util.Utils.*;
import static org.opends.messages.QuickSetupMessages.*;
import static org.opends.server.util.DynamicConstants.*;
import java.io.*;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapName;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.TrustManager;
import org.opends.admin.ads.ADSContext;
import org.opends.admin.ads.ReplicaDescriptor;
import org.opends.admin.ads.ServerDescriptor;
import org.opends.admin.ads.SuffixDescriptor;
import org.opends.admin.ads.TopologyCacheException;
import org.opends.admin.ads.util.ConnectionUtils;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.LocalizableMessageDescriptor;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.quicksetup.*;
import org.opends.quicksetup.installer.AuthenticationData;
import org.opends.quicksetup.installer.DataReplicationOptions;
import org.opends.quicksetup.installer.NewSuffixOptions;
import org.opends.quicksetup.installer.SuffixesToReplicateOptions;
import org.opends.quicksetup.ui.UIFactory;
import org.opends.server.util.SetupUtils;
import org.opends.server.util.StaticUtils;
/**
* This class provides some static convenience methods of different nature.
*
*/
public class Utils
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
private static final int BUFFER_SIZE = 1024;
private static final int MAX_LINE_WIDTH = 80;
private Utils()
{
}
/**
* The class name that contains the control panel customizations for
* products.
*/
private final static String CUSTOMIZATION_CLASS_NAME =
"org.opends.server.util.ReleaseDefinition";
/**
* The service name required by the JNLP downloader.
*/
public static String JNLP_SERVICE_NAME = "javax.jnlp.DownloadService";
/**
* Returns true if the provided port is free and we can use it,
* false otherwise.
* @param port the port we are analyzing.
* @return true if the provided port is free and we can use it,
* false otherwise.
*/
public static boolean canUseAsPort(int port)
{
return SetupUtils.canUseAsPort(port);
}
/**
* Returns true if the provided port is a priviledged port,
* false otherwise.
* @param port the port we are analyzing.
* @return true if the provided port is a priviledged port,
* false otherwise.
*/
public static boolean isPriviledgedPort(int port)
{
return SetupUtils.isPriviledgedPort(port);
}
/**
* Tells whether the provided java installation supports a given option or
* not.
* @param javaHome the java installation path.
* @param option the java option that we want to check.
* @param installPath the install path of the server.
* @return true if the provided java installation supports a
* given option and false otherwise.
*/
public static boolean supportsOption(String option, String javaHome,
String installPath)
{
boolean supported = false;
logger.info(LocalizableMessage.raw("Checking if options "+option+
" are supported with java home: "+javaHome));
try
{
List args = new ArrayList();
String script;
String libPath = Utils.getPath(installPath,
Installation.LIBRARIES_PATH_RELATIVE);
if (Utils.isWindows())
{
script = Utils.getScriptPath(Utils.getPath(libPath,
Installation.SCRIPT_UTIL_FILE_WINDOWS));
}
else
{
script = Utils.getScriptPath(Utils.getPath(libPath,
Installation.SCRIPT_UTIL_FILE_UNIX));
}
args.add(script);
ProcessBuilder pb = new ProcessBuilder(args);
Map env = pb.environment();
env.put(SetupUtils.OPENDJ_JAVA_HOME, javaHome);
env.put("OPENDJ_JAVA_ARGS", option);
env.put("SCRIPT_UTIL_CMD", "set-full-environment-and-test-java");
env.remove("OPENDJ_JAVA_BIN");
// In windows by default the scripts ask the user to click on enter when
// they fail. Set this environment variable to avoid it.
if (Utils.isWindows())
{
env.put("DO_NOT_PAUSE", "true");
}
final Process process = pb.start();
logger.info(LocalizableMessage.raw("launching "+args+ " with env: "+env));
InputStream is = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
boolean errorDetected = false;
while (null != (line = reader.readLine())) {
logger.info(LocalizableMessage.raw("The output: "+line));
if (line.contains("ERROR: The detected Java version"))
{
if (Utils.isWindows())
{
// If we are running windows, the process get blocked waiting for
// user input. Just wait for a certain time to print the output
// in the logger and then kill the process.
Thread t = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
Thread.sleep(3000);
// To see if the process is over, call the exitValue method.
// If it is not over, a IllegalThreadStateException.
process.exitValue();
}
catch (Throwable t)
{
process.destroy();
}
}
});
t.start();
}
errorDetected = true;
}
}
process.waitFor();
int returnCode = process.exitValue();
logger.info(LocalizableMessage.raw("returnCode: "+returnCode));
supported = returnCode == 0 && !errorDetected;
logger.info(LocalizableMessage.raw("supported: "+supported));
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw("Error testing option "+option+" on "+javaHome, t));
}
return supported;
}
/**
* Creates a new file attempting to create the parent directories
* if necessary.
* @param f File to create
* @return boolean indicating whether the file was created; false otherwise
* @throws IOException if something goes wrong
*/
public static boolean createFile(File f) throws IOException {
boolean success = false;
if (f != null) {
File parent = f.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
success = f.createNewFile();
}
return success;
}
/**
* Returns the absolute path for the given parentPath and relativePath.
* @param parentPath the parent path.
* @param relativePath the relative path.
* @return the absolute path for the given parentPath and relativePath.
*/
public static String getPath(String parentPath, String relativePath)
{
return getPath(new File(new File(parentPath), relativePath));
}
/**
* Returns the String that can be used to launch an script using Runtime.exec.
* This method is required because in Windows the script that contain a "="
* in their path must be quoted.
* @param script the script name
* @return the absolute path for the given parentPath and relativePath.
*/
public static String getScriptPath(String script)
{
return SetupUtils.getScriptPath(script);
}
/**
* Returns the absolute path for the given file. It tries to get the
* canonical file path. If it fails it returns the string representation.
* @param f File to get the path
* @return the absolute path for the given file.
*/
public static String getPath(File f)
{
String path = null;
if (f != null) {
try
{
/*
* Do a best effort to avoid having a relative representation (for
* instance to avoid having ../../../).
*/
f = f.getCanonicalFile();
}
catch (IOException ioe)
{
/* This is a best effort to get the best possible representation of the
* file: reporting the error is not necessary.
*/
}
path = f.toString();
}
return path;
}
/**
* Returns true if the first provided path is under the second
* path in the file system.
* @param descendant the descendant candidate path.
* @param path the path.
* @return true if the first provided path is under the second
* path in the file system; false otherwise or if
* either of the files are null
*/
public static boolean isDescendant(File descendant, File path) {
boolean isDescendant = false;
if (descendant != null && path != null) {
File parent = descendant.getParentFile();
while ((parent != null) && !isDescendant) {
isDescendant = path.equals(parent);
if (!isDescendant) {
parent = parent.getParentFile();
}
}
}
return isDescendant;
}
/**
* Returns true if we are running under windows and
* false otherwise.
* @return true if we are running under windows and
* false otherwise.
*/
public static boolean isWindows()
{
return SetupUtils.isWindows();
}
/**
* Returns true if we are running under Mac OS and
* false otherwise.
* @return true if we are running under Mac OS and
* false otherwise.
*/
public static boolean isMacOS()
{
return SetupUtils.isMacOS();
}
/**
* Returns true if we are running under Unix and
* false otherwise.
* @return true if we are running under Unix and
* false otherwise.
*/
public static boolean isUnix()
{
return SetupUtils.isUnix();
}
/**
* Returns a String representation of the OS we are running.
* @return a String representation of the OS we are running.
*/
public static String getOSString()
{
return SetupUtils.getOSString();
}
/**
* Returns true if the parent directory for the provided path
* exists and false otherwise.
* @param path the path that we are analyzing.
* @return true if the parent directory for the provided path
* exists and false otherwise.
*/
public static boolean parentDirectoryExists(String path)
{
boolean parentExists = false;
File f = new File(path);
File parentFile = f.getParentFile();
if (parentFile != null)
{
parentExists = parentFile.isDirectory();
}
return parentExists;
}
/**
* Returns true if the the provided path is a file and exists and
* false otherwise.
* @param path the path that we are analyzing.
* @return true if the the provided path is a file and exists and
* false otherwise.
*/
public static boolean fileExists(String path)
{
File f = new File(path);
return f.isFile();
}
/**
* Returns true if the the provided path is a directory, exists
* and is not empty false otherwise.
* @param path the path that we are analyzing.
* @return true if the the provided path is a directory, exists
* and is not empty false otherwise.
*/
public static boolean directoryExistsAndIsNotEmpty(String path)
{
boolean directoryExistsAndIsNotEmpty = false;
File f = new File(path);
if (f.isDirectory())
{
String[] ch = f.list();
directoryExistsAndIsNotEmpty = (ch != null) && (ch.length > 0);
}
return directoryExistsAndIsNotEmpty;
}
/**
* Returns true if the the provided string is a DN and
* false otherwise.
* @param dn the String we are analyzing.
* @return true if the the provided string is a DN and
* false otherwise.
*/
public static boolean isDn(String dn)
{
boolean isDn = true;
try
{
new LdapName(dn);
} catch (Exception ex)
{
isDn = false;
}
return isDn;
}
/**
* Returns true if the the provided string is a configuration DN
* and false otherwise.
* @param dn the String we are analyzing.
* @return true if the the provided string is a configuration DN
* and false otherwise.
*/
public static boolean isConfigurationDn(String dn)
{
boolean isConfigurationDn = false;
String[] configDns =
{ "cn=config", Constants.SCHEMA_DN };
for (int i = 0; i < configDns.length && !isConfigurationDn; i++)
{
isConfigurationDn = areDnsEqual(dn, configDns[i]);
}
return isConfigurationDn;
}
/**
* Returns true if the the provided strings represent the same
* DN and false otherwise.
* @param dn1 the first dn to compare.
* @param dn2 the second dn to compare.
* @return true if the the provided strings represent the same
* DN and false otherwise.
*/
public static boolean areDnsEqual(String dn1, String dn2)
{
boolean areDnsEqual = false;
try
{
LdapName name1 = new LdapName(dn1);
LdapName name2 = new LdapName(dn2);
areDnsEqual = name1.equals(name2);
} catch (Exception ex) {
// do nothing
}
return areDnsEqual;
}
/**
* Creates the parent directory if it does not already exist.
* @param f File for which parentage will be insured
* @return boolean indicating whether or not the input f
* has a parent after this method is invoked.
*/
static public boolean insureParentsExist(File f) {
File parent = f.getParentFile();
boolean b = parent.exists();
if (!b) {
b = parent.mkdirs();
}
return b;
}
/**
* Creates the a directory in the provided path.
* @param path the path.
* @return true if the path was created or already existed (and
* was a directory) and false otherwise.
* @throws IOException if something goes wrong.
*/
public static boolean createDirectory(String path) throws IOException {
return createDirectory(new File(path));
}
/**
* Creates the a directory in the provided path.
* @param f the path.
* @return true if the path was created or already existed (and
* was a directory) and false otherwise.
* @throws IOException if something goes wrong.
*/
public static boolean createDirectory(File f) throws IOException
{
boolean directoryCreated;
if (!f.exists())
{
directoryCreated = f.mkdirs();
} else
{
directoryCreated = f.isDirectory();
}
return directoryCreated;
}
/**
* Creates a file on the specified path with the contents of the provided
* stream.
* @param path the path where the file will be created.
* @param is the InputStream with the contents of the file.
* @throws IOException if something goes wrong.
*/
public static void createFile(File path, InputStream is) throws IOException
{
FileOutputStream out;
BufferedOutputStream dest;
byte[] data = new byte[BUFFER_SIZE];
int count;
out = new FileOutputStream(path);
dest = new BufferedOutputStream(out);
while ((count = is.read(data, 0, BUFFER_SIZE)) != -1)
{
dest.write(data, 0, count);
}
dest.flush();
dest.close();
}
/**
* Creates a file on the specified path with the contents of the provided
* String. The file is protected, so that 'others' have no access to it.
* @param path the path where the file will be created.
* @param content the String with the contents of the file.
* @throws IOException if something goes wrong.
* @throws InterruptedException if there is a problem changing the permissions
* of the file.
*/
public static void createProtectedFile(String path, String content)
throws IOException, InterruptedException
{
FileWriter file = new FileWriter(path);
PrintWriter out = new PrintWriter(file);
out.println(content);
out.flush();
out.close();
if (!isWindows())
{
setPermissionsUnix(path, "600");
}
}
/**
* This is a helper method that gets a LocalizableMessage representation of the elements
* in the Collection of Messages. The LocalizableMessage will display the different
* elements separated by the separator String.
*
* @param col
* the collection containing the messages.
* @param separator
* the separator String to be used.
* @return the message representation for the collection;
* null if col is null
*/
public static LocalizableMessage getMessageFromCollection(Collection col,
String separator) {
LocalizableMessage message = null;
if (col != null) {
LocalizableMessageBuilder mb = null;
for (LocalizableMessage m : col) {
if (mb == null) {
mb = new LocalizableMessageBuilder(m);
} else {
mb.append(separator).append(m);
}
}
if (mb == null) mb = new LocalizableMessageBuilder();
message = mb.toMessage();
}
return message;
}
/**
* Returns the default server location that will be proposed to the user
* in the installation.
* @return the default server location that will be proposed to the user
* in the installation.
*/
public static String getDefaultServerLocation()
{
String userDir = System.getProperty("user.home");
String firstLocation = userDir + File.separator
+ SHORT_NAME.toLowerCase(Locale.ENGLISH);
String serverLocation = firstLocation;
int i = 1;
while (fileExists(serverLocation)
|| directoryExistsAndIsNotEmpty(serverLocation))
{
serverLocation = firstLocation + "-" + i;
i++;
}
return serverLocation;
}
/**
* Returns true if there is more disk space in the provided path
* than what is specified with the bytes parameter.
* @param directoryPath the path.
* @param bytes the disk space.
* @return true if there is more disk space in the provided path
* than what is specified with the bytes parameter.
*/
public static synchronized boolean hasEnoughSpace(String directoryPath,
long bytes)
{
// TODO This does not work with quotas etc. but at least it seems that
// we do not write all data on disk if it fails.
boolean hasEnoughSpace = false;
File file = null;
RandomAccessFile raf = null;
File directory = new File(directoryPath);
boolean deleteDirectory = false;
if (!directory.exists())
{
deleteDirectory = directory.mkdir();
}
try
{
file = File.createTempFile("temp" + System.nanoTime(), ".tmp", directory);
raf = new RandomAccessFile(file, "rw");
raf.setLength(bytes);
hasEnoughSpace = true;
} catch (IOException ex)
{ /* do nothing */
} finally
{
StaticUtils.close(raf);
if (file != null)
{
file.delete();
}
}
if (deleteDirectory)
{
directory.delete();
}
return hasEnoughSpace;
}
/**
* Returns a localized message for a given properties key an throwable.
* @param message prefix
* @param t the throwable for which we want to get a message.
*
* @return a localized message for a given properties key and throwable.
*/
public static LocalizableMessage getThrowableMsg(LocalizableMessage message, Throwable t)
{
LocalizableMessageBuilder mb = new LocalizableMessageBuilder(message);
LocalizableMessageDescriptor.Arg1