/*
* 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
*
*
* Portions Copyright 2006 Sun Microsystems, Inc.
*/
package org.opends.server;
import org.opends.server.types.Entry;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.util.LDIFReader;
import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Enumeration;
import java.util.Map;
import java.util.logging.Logger;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.ConsoleHandler;
import java.net.ServerSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import org.opends.server.backends.MemoryBackend;
import org.opends.server.backends.jeb.BackendImpl;
import org.opends.server.config.ConfigException;
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.LockFileManager;
import org.opends.server.extensions.ConfigFileHandler;
import org.opends.server.loggers.Error;
import org.opends.server.loggers.Debug;
import org.opends.server.plugins.InvocationCounterPlugin;
import org.opends.server.types.DN;
import org.opends.server.types.FilePermission;
import org.opends.server.types.InitializationException;
import org.opends.server.types.OperatingSystem;
import org.opends.server.types.NullOutputStream;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.tasks.TaskUtils;
/**
* This class defines some utility functions which can be used by test
* cases.
*/
public final class TestCaseUtils {
/**
* The name of the system property that specifies the server build root.
*/
public static final String PROPERTY_BUILD_ROOT =
"org.opends.server.BuildRoot";
/**
* The name of the system property that specifies the ldap port.
* Set this prtoperty when running the server if you want to use a given
* port number, otherwise a port is choosed randomly at test startup time.
*/
public static final String PROPERTY_LDAP_PORT =
"org.opends.server.LdapPort";
/**
* The string representation of the DN that will be used as the base entry for
* the test backend. This must not be changed, as there are a number of test
* cases that depend on this specific value of "o=test".
*/
public static final String TEST_ROOT_DN_STRING = "o=test";
/**
* Indicates whether the server has already been started. The value of this
* constant must not be altered by anything outside the
* startServer method.
*/
public static boolean SERVER_STARTED = false;
/**
* The memory-based backend configured for use in the server.
*/
private static MemoryBackend memoryBackend = null;
/**
* The LDAP port the server is bound to on start.
*/
private static int serverLdapPort;
/**
* The JMX port the server is bound to on start.
*/
private static int serverJmxPort;
/**
* The LDAPS port the server is bound to on start.
*/
private static int serverLdapsPort;
/**
* Starts the Directory Server so that it will be available for use while
* running the unit tests. This will only actually start the server once, so
* subsequent attempts to start it will be ignored because it will already be
* available.
*
* @throws IOException If a problem occurs while interacting with the
* filesystem to prepare the test package root.
*
* @throws InitializationException If a problem occurs while starting the
* server.
*
* @throws ConfigException If there is a problem with the server
* configuration.
*/
public static void startServer()
throws IOException, InitializationException, ConfigException
{
if (SERVER_STARTED)
{
return;
}
InvocationCounterPlugin.resetStartupCalled();
// Get the build root and use it to create a test package directory.
String buildRoot = System.getProperty(PROPERTY_BUILD_ROOT);
File testRoot = new File(buildRoot + File.separator + "build" +
File.separator + "unit-tests" + File.separator +
"package");
File testSrcRoot = new File(buildRoot + File.separator + "tests" +
File.separator + "unit-tests-testng");
if (testRoot.exists())
{
deleteDirectory(testRoot);
}
testRoot.mkdirs();
//db_verify is second jeb backend used by the jeb verify test cases
String[] subDirectories = { "bak", "bin", "changelogDb", "classes",
"config", "db", "db_verify", "ldif", "lib",
"locks", "logs" };
for (String s : subDirectories)
{
new File(testRoot, s).mkdir();
}
// Copy the configuration, schema, and MakeLDIF resources into the
// appropriate place under the test package.
File resourceDir = new File(buildRoot, "resource");
File testResourceDir = new File(testSrcRoot, "resource");
File testConfigDir = new File(testRoot, "config");
File testBinDir = new File(testRoot, "bin");
copyDirectory(new File(resourceDir, "bin"), testBinDir);
copyDirectory(new File(resourceDir, "config"), testConfigDir);
copyDirectory(new File(resourceDir, "schema"),
new File(testConfigDir, "schema"));
copyDirectory(new File(resourceDir, "MakeLDIF"),
new File(testConfigDir, "MakeLDIF"));
copyFile(new File(testResourceDir, "server.keystore"),
new File(testConfigDir, "server.keystore"));
copyFile(new File(testResourceDir, "server.truststore"),
new File(testConfigDir, "server.truststore"));
copyFile(new File(testResourceDir, "client.keystore"),
new File(testConfigDir, "client.keystore"));
copyFile(new File(testResourceDir, "client.truststore"),
new File(testConfigDir, "client.truststore"));
copyFile(new File(testResourceDir, "server-cert.p12"),
new File(testConfigDir, "server-cert.p12"));
copyFile(new File(testResourceDir, "client-cert.p12"),
new File(testConfigDir, "client-cert.p12"));
// Make the shell scripts in the bin directory executable, if possible.
OperatingSystem os = DirectoryServer.getOperatingSystem();
if ((os != null) && OperatingSystem.isUNIXBased(os) &&
FilePermission.canSetPermissions())
{
try
{
FilePermission perm = FilePermission.decodeUNIXMode("755");
for (File f : testBinDir.listFiles())
{
if (f.getName().endsWith(".sh"))
{
FilePermission.setPermissions(f, perm);
}
}
} catch (Exception e) {}
}
// Find some free ports for the listeners and write them to the
// config-chamges.ldif file.
ServerSocket serverLdapSocket = null;
ServerSocket serverJmxSocket = null;
ServerSocket serverLdapsSocket = null;
String ldapPort = System.getProperty(PROPERTY_LDAP_PORT);
if (ldapPort == null)
{
serverLdapSocket = bindFreePort();
serverLdapPort = serverLdapSocket.getLocalPort();
}
else
{
serverLdapPort = Integer.valueOf(ldapPort);
serverLdapSocket = bindPort(serverLdapPort);
}
serverJmxSocket = bindFreePort();
serverJmxPort = serverJmxSocket.getLocalPort();
serverLdapsSocket = bindFreePort();
serverLdapsPort = serverLdapsSocket.getLocalPort();
BufferedReader reader = new BufferedReader(new FileReader(
new File(testResourceDir,
"config-changes.ldif")
));
FileOutputStream outFile = new FileOutputStream(
new File(testConfigDir, "config-changes.ldif"));
PrintStream writer = new PrintStream(outFile);
String line = reader.readLine();
while(line != null)
{
line = line.replaceAll("#ldapport#", String.valueOf(serverLdapPort));
line = line.replaceAll("#jmxport#", String.valueOf(serverJmxPort));
line = line.replaceAll("#ldapsport#", String.valueOf(serverLdapsPort));
writer.println(line);
line = reader.readLine();
}
writer.close();
outFile.close();
serverLdapSocket.close();
serverJmxSocket.close();
serverLdapsSocket.close();
// Actually start the server and set a variable that will prevent us from
// needing to do it again.
System.setProperty(PROPERTY_SERVER_ROOT, testRoot.getAbsolutePath());
System.setProperty(PROPERTY_FORCE_DAEMON_THREADS, "true");
String configClass = ConfigFileHandler.class.getName();
String configFile = testConfigDir.getAbsolutePath() + File.separator +
"config.ldif";
DirectoryServer directoryServer = DirectoryServer.getInstance();
directoryServer.bootstrapServer();
directoryServer.initializeConfiguration(configClass, configFile);
Error.removeAllErrorLoggers(false);
Debug.removeAllDebugLoggers(false);
directoryServer.startServer();
assertTrue(InvocationCounterPlugin.startupCalled());
SERVER_STARTED = true;
}
/**
* Binds to the given socket port on the local host.
* @return the bounded Server socket.
*
* @throws IOException in case of underlying exception.
* @throws SocketException in case of underlying exception.
*/
private static ServerSocket bindPort(int port)
throws IOException, SocketException
{
ServerSocket serverLdapSocket;
serverLdapSocket = new ServerSocket();
serverLdapSocket.setReuseAddress(true);
serverLdapSocket.bind(new InetSocketAddress("127.0.0.1", port));
return serverLdapSocket;
}
/**
* Find and binds to a free server socket port on the local host.
* @return the bounded Server socket.
*
* @throws IOException in case of underlying exception.
* @throws SocketException in case of underlying exception.
*/
public static ServerSocket bindFreePort() throws IOException, SocketException
{
ServerSocket serverLdapSocket;
serverLdapSocket = new ServerSocket();
serverLdapSocket.setReuseAddress(true);
serverLdapSocket.bind(new InetSocketAddress("127.0.0.1", 0));
return serverLdapSocket;
}
/**
* Shut down the server, if it has been started.
* @param reason The reason for the shutdown.
*/
public static void shutdownServer(String reason)
{
if (SERVER_STARTED)
{
InvocationCounterPlugin.resetShutdownCalled();
DirectoryServer.shutDown("org.opends.server.TestCaseUtils", reason);
assertTrue(InvocationCounterPlugin.shutdownCalled());
SERVER_STARTED = false;
}
}
/**
* Initializes a memory-based backend that may be used to perform operations
* while testing the server. This will ensure that the memory backend is
* created in the server if it does not yet exist, and that it is empty. Note
* that the base DN for the test backend will always be "o=test", and it must
* not be changed. It is acceptable for test cases using this backend to
* hard-code their sample data to use this base DN, although they may still
* reference the TEST_ROOT_DN_STRING constant if they wish.
*
* @param createBaseEntry Indicate whether to automatically create the base
* entry and add it to the backend.
*
* @throws Exception If an unexpected problem occurs.
*/
public static void initializeTestBackend(boolean createBaseEntry)
throws Exception
{
startServer();
DN baseDN = DN.decode(TEST_ROOT_DN_STRING);
if (memoryBackend == null)
{
memoryBackend = new MemoryBackend();
memoryBackend.setBackendID("test");
memoryBackend.initializeBackend(null, new DN[] { baseDN });
DirectoryServer.registerBackend(memoryBackend);
}
memoryBackend.clearMemoryBackend();
if (createBaseEntry)
{
Entry e = createEntry(baseDN);
memoryBackend.addEntry(e, null);
}
}
/**
* Clears all the entries from the JE backend determined by the
* be id passed into the method.
* @param createBaseEntry Indicate whether to automatically create the base
* entry and add it to the backend.
*
* @param beID The be id to clear.
*
* @param dn The suffix of the backend to create if the the createBaseEntry
* boolean is true.
*
* @throws Exception If an unexpected problem occurs.
*/
public static void clearJEBackend(boolean createBaseEntry, String beID, String dn)
throws Exception
{
BackendImpl backend = (BackendImpl)DirectoryServer.getBackend(beID);
DN[] baseDNs = backend.getBaseDNs();
ConfigEntry configEntry = TaskUtils.getConfigEntry(backend);
TaskUtils.setBackendEnabled(configEntry, false);
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (!LockFileManager.acquireExclusiveLock(lockFile, failureReason))
{
throw new RuntimeException(failureReason.toString());
}
try
{
backend.clearBackend(configEntry, baseDNs);
}
finally
{
LockFileManager.releaseLock(lockFile, failureReason);
}
}
finally
{
TaskUtils.setBackendEnabled(configEntry, true);
}
if (createBaseEntry)
{
DN baseDN = DN.decode(dn);
Entry e = createEntry(baseDN);
backend = (BackendImpl)DirectoryServer.getBackend(beID);
backend.addEntry(e, null);
}
}
/**
* Create a temporary directory with the specified prefix.
*
* @param prefix
* The directory prefix.
* @return The temporary directory.
* @throws IOException
* If the temporary directory could not be created.
*/
public static File createTemporaryDirectory(String prefix)
throws IOException {
File tempDirectory = File.createTempFile(prefix, null);
if (!tempDirectory.delete()) {
throw new IOException("Unable to delete temporary file: "
+ tempDirectory);
}
if (!tempDirectory.mkdir()) {
throw new IOException("Unable to create temporary directory: "
+ tempDirectory);
}
return tempDirectory;
}
/**
* Copy a directory and its contents.
*
* @param src
* The name of the directory to copy.
* @param dst
* The name of the destination directory.
* @throws IOException
* If the directory could not be copied.
*/
public static void copyDirectory(File src, File dst) throws IOException {
if (src.isDirectory()) {
// Create the destination directory if it does not exist.
if (!dst.exists()) {
dst.mkdirs();
}
// Recursively copy sub-directories and files.
for (String child : src.list()) {
copyDirectory(new File(src, child), new File(dst, child));
}
} else {
copyFile(src, dst);
}
}
/**
* Delete a directory and its contents.
*
* @param dir
* The name of the directory to delete.
* @throws IOException
* If the directory could not be deleted.
*/
public static void deleteDirectory(File dir) throws IOException {
if (dir.isDirectory()) {
// Recursively delete sub-directories and files.
for (String child : dir.list()) {
deleteDirectory(new File(dir, child));
}
}
dir.delete();
}
/**
* Copy a file.
*
* @param src
* The name of the source file.
* @param dst
* The name of the destination file.
* @throws IOException
* If the file could not be copied.
*/
public static void copyFile(File src, File dst) throws IOException {
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst);
// Transfer bytes from in to out
byte[] buf = new byte[8192];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
/**
* Get the LDAP port the test environment Directory Server instance is
* running on.
*
* @return The port number.
*/
public static long getServerLdapPort()
{
return serverLdapPort;
}
/**
* Get the JMX port the test environment Directory Server instance is
* running on.
*
* @return The port number.
*/
public static long getServerJmxPort()
{
return serverJmxPort;
}
/**
* Get teh LDAPS port the test environment Directory Server instance is
* running on
*
* @return The port number.
*/
public static long getServerLdapsPort()
{
return serverLdapsPort;
}
/**
* Method for getting a file from the test resources directory.
*
* @return The directory as a File
*/
public static File getTestResource(String filename)
{
String buildRoot = System.getProperty(PROPERTY_BUILD_ROOT);
File testResourceDir = new File(buildRoot + File.separator + "tests" +
File.separator + "unit-tests-testng" +
File.separator + "resource");
return new File(testResourceDir, filename);
}
/**
* Prevent instantiation.
*/
private TestCaseUtils() {
// No implementation.
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//
// Various methods for converting LDIF Strings to Entries
//
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/**
* Returns a modifiable List of entries parsed from the provided LDIF.
* It's best to call this after the server has been initialized so
* that schema checking happens.
*
* Also take a look at the makeLdif method below since this makes
* expressing LDIF a little bit cleaner.
*
* @param ldif of the entries to parse.
* @return a List of EntryS parsed from the ldif string.
* @see #makeLdif
*/
public static List
* Also take a look at the makeLdif method below since this makes
* expressing LDIF a little bit cleaner.
*
* @return the first Entry parsed from the ldif String
* @see #makeLdif
*/
public static Entry entryFromLdifString(String ldif) throws Exception {
return entriesFromLdifString(ldif).get(0);
}
/**
* This method provides the minor convenience of not having to specify the
* newline character at the end of every line of LDIF in test code.
* This is an admittedly small advantage, but it does make things a little
* easier and less error prone. For example, this
*
private static final String JOHN_SMITH_LDIF = TestCaseUtils.makeLdif(
"dn: cn=John Smith,dc=example,dc=com",
"objectclass: inetorgperson",
"cn: John Smith",
"sn: Smith",
"givenname: John");
is a
private static final String JOHN_SMITH_LDIF =
"dn: cn=John Smith,dc=example,dc=com\n" +
"objectclass: inetorgperson\n" +
"cn: John Smith\n" +
"sn: Smith\n" +
"givenname: John\n";
*
* @return the concatenation of each line followed by a newline character
*/
public static String makeLdif(String... lines) {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
buffer.append(lines[i]).append(EOL);
}
return buffer.toString();
}
/**
* This is a convience method that constructs an Entry from the specified
* lines of LDIF. Here's a sample usage
*
Entry john = TestCaseUtils.makeEntry(
"dn: cn=John Smith,dc=example,dc=com",
"objectclass: inetorgperson",
"cn: John Smith",
"sn: Smith",
"givenname: John");
* @see #makeLdif
*/
public static Entry makeEntry(String... lines) throws Exception {
return entryFromLdifString(makeLdif(lines));
}
/**
* This is a convience method that constructs an List of EntryS from the
* specified lines of LDIF. Here's a sample usage
*
List
* @see #makeLdif
*/
public static List