/* * 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 entriesFromLdifString(String ldif) throws Exception { LDIFImportConfig ldifImportConfig = new LDIFImportConfig(new StringReader(ldif)); LDIFReader reader = new LDIFReader(ldifImportConfig); List entries = new ArrayList(); Entry entry = null; while ((entry = reader.readEntry()) != null) { entries.add(entry); } return entries; } /** * This is used as a convenience when and LDIF string only includes a single * entry. 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. * * @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 little easier to work with than
       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 smiths = TestCaseUtils.makeEntries(
      "dn: cn=John Smith,dc=example,dc=com",
      "objectclass: inetorgperson",
      "cn: John Smith",
      "sn: Smith",
      "givenname: John",
      "",
      "dn: cn=Jane Smith,dc=example,dc=com",
      "objectclass: inetorgperson",
      "cn: Jane Smith",
      "sn: Smith",
      "givenname: Jane");
   
* @see #makeLdif */ public static List makeEntries(String... lines) throws Exception { return entriesFromLdifString(makeLdif(lines)); } /** * Creates a temporary text file with the specified contents. It will be * marked for automatic deletion when the JVM exits. * * @return The absolute path to the file that was created. * * @throws Exception If an unexpected problem occurs. */ public static String createTempFile(String... lines) throws Exception { File f = File.createTempFile("LDAPModifyTestCase", ".txt"); f.deleteOnExit(); FileWriter w = new FileWriter(f); for (String s : lines) { w.write(s + System.getProperty("line.separator")); } w.close(); return f.getAbsolutePath(); } /** Convenience method so we don't have to catch InterruptedException everywhere. */ public static void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { // Ignore it. } } // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // The set of loggers for which the console logger has been disabled. private final static Map disabledLogHandlers = new HashMap(); /** The original System.err print stream. Use this if you absolutely * must write something to System.err. */ public final static PrintStream originalSystemErr = System.err; /** The original System.out print stream. Use this if you absolutely * must write something to System.out. */ public final static PrintStream originalSystemOut = System.out; public synchronized static void suppressOutput() { String suppressStr = System.getProperty("org.opends.test.suppressOutput"); if ((suppressStr != null) && suppressStr.equalsIgnoreCase("true")) { System.setOut(NullOutputStream.printStream()); System.setErr(NullOutputStream.printStream()); LogManager logManager = LogManager.getLogManager(); Enumeration loggerNames = logManager.getLoggerNames(); while (loggerNames.hasMoreElements()) { String loggerName = loggerNames.nextElement(); Logger logger = logManager.getLogger(loggerName); for (Handler h : logger.getHandlers()) { if (h instanceof ConsoleHandler) { disabledLogHandlers.put(logger, h); logger.removeHandler(h); break; } } } } } public synchronized static void unsupressOutput() { System.setOut(originalSystemOut); System.setErr(originalSystemErr); for (Logger l : disabledLogHandlers.keySet()) { Handler h = disabledLogHandlers.get(l); l.addHandler(h); } disabledLogHandlers.clear(); } }