/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. * Portions Copyright 2013 Manuel Gaupp * Portions Copyright 2018-2025 3A Systems, LLC */ package org.opends.server; import static org.forgerock.opendj.server.embedded.ConfigParameters.configParams; import static org.forgerock.opendj.server.embedded.ConnectionParameters.connectionParams; import static org.forgerock.opendj.server.embedded.EmbeddedDirectoryServer.manageEmbeddedDirectoryServer; import static org.opends.server.loggers.TextAccessLogPublisher.getStartupTextAccessPublisher; import static org.opends.server.loggers.TextErrorLogPublisher.*; import static org.opends.server.loggers.TextHTTPAccessLogPublisher.getStartupTextHTTPAccessPublisher; import static org.opends.server.types.NullOutputStream.nullPrintStream; import static org.opends.server.util.ServerConstants.PROPERTY_RUNNING_UNIT_TESTS; import static org.testng.Assert.assertTrue; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.StringReader; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.net.BindException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.LogManager; import java.util.logging.Logger; import com.forgerock.opendj.ldap.tools.LDAPSearch; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.opendj.config.dsconfig.DSConfig; import org.forgerock.opendj.config.server.ConfigException; import org.forgerock.opendj.io.ASN1; import org.forgerock.opendj.io.ASN1Reader; import org.forgerock.opendj.io.ASN1Writer; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.DN; import org.forgerock.opendj.ldap.ResultCode; import org.opends.server.api.LocalBackend; import org.opends.server.api.WorkQueue; import org.opends.server.api.plugin.PluginType; import org.opends.server.backends.MemoryBackend; import org.opends.server.backends.pluggable.BackendImpl; import org.opends.server.backends.pluggable.EntryContainer; import org.opends.server.backends.pluggable.RootContainer; import org.opends.server.core.AddOperation; import org.opends.server.core.BackendConfigManager; import org.opends.server.core.DeleteOperation; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ServerContext; import org.opends.server.loggers.AccessLogPublisher; import org.opends.server.loggers.AccessLogger; import org.opends.server.loggers.DebugLogger; import org.opends.server.loggers.ErrorLogPublisher; import org.opends.server.loggers.ErrorLogger; import org.opends.server.loggers.HTTPAccessLogPublisher; import org.opends.server.loggers.HTTPAccessLogger; import org.opends.server.plugins.InvocationCounterPlugin; import org.opends.server.protocols.ldap.BindRequestProtocolOp; import org.opends.server.protocols.ldap.BindResponseProtocolOp; import org.opends.server.protocols.ldap.LDAPMessage; import org.opends.server.protocols.ldap.LDAPReader; import com.forgerock.opendj.ldap.tools.LDAPModify; import org.opends.server.types.Attribute; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.FilePermission; import org.opends.server.types.InitializationException; import org.opends.server.types.LDIFImportConfig; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.server.embedded.EmbeddedDirectoryServer; import org.opends.server.util.BuildVersion; import org.opends.server.util.DynamicConstants; import org.opends.server.util.LDIFReader; import com.forgerock.opendj.util.OperatingSystem; import static org.mockito.Mockito.*; import static org.opends.server.protocols.internal.InternalClientConnection.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; import static org.testng.Assert.*; /** This class defines some utility functions which can be used by test cases. */ @SuppressWarnings("javadoc") 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"; public static final String PROPERTY_BUILD_DIR = "org.opends.server.BuildDir"; /** * The name of the system property that specifies an existing OpenDS * installation root (inside or outside of the source tree). */ public static final String PROPERTY_INSTALLED_ROOT = "org.opends.server.InstalledRoot"; /** * The name of the system property that specifies an LDIF file * with changes compare to the default config.ldif. */ public static final String PROPERTY_CONFIG_CHANGE_FILE = "org.opends.server.ConfigChangeFile"; /** * The name of the system property that specifies if the test instance * directory needs to be wiped out before starting the setup or not. This will * let the caller the possibility to copy some files (ie extensions) inside * the test instance directory before the server starts up. */ public static final String PROPERTY_CLEANUP_REQUIRED = "org.opends.server.CleanupDirectories"; /** * The name of the system property that specifies the ldap port. * Set this property when running the server if you want to use a given * port number, otherwise a port is chosen randomly at test startup time. */ public static final String PROPERTY_LDAP_PORT = "org.opends.server.LdapPort"; /** * The name of the system property that specifies the admin port. Set this * property when running the server if you want to use a given port number, * otherwise a port is chosen randomly at test startup time. */ public static final String PROPERTY_ADMIN_PORT = "org.opends.server.AdminPort"; /** * If this System property is set to true, then the classes/ directory * will be copied into the server package setup for the tests. This allows * the server tools (e.g. ldapsearch) to be used on a live server, but it * takes a while to copy all of the files, so we don't do it by default. */ public static final String PROPERTY_COPY_CLASSES_TO_TEST_PKG = "org.opends.test.copyClassesToTestPackage"; /** * 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"; /** The backend if for the test backend. */ public static final String TEST_BACKEND_ID = "test"; /** * The string representation of the OpenDMK jar file location * that will be used as base to determine if snmp is included or not. */ public static final String PROPERTY_OPENDMK_LOCATION = "org.opends.server.snmp.opendmk"; /** The test text writer for the Debug Logger. */ public static TestTextWriter DEBUG_TEXT_WRITER = new TestTextWriter(); /** The test text writer for the Error Logger. */ public static TestTextWriter ERROR_TEXT_WRITER = new TestTextWriter(); /** The test text writer for the Access Logger. */ public static TestTextWriter ACCESS_TEXT_WRITER = new TestTextWriter(); /** The test text writer for the HTTP Access Logger. */ public static TestTextWriter HTTP_ACCESS_TEXT_WRITER = new TestTextWriter(); /** * 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; /** * This is used to store the schema as it was before starting the fake server * (for example, it could have been the real schema) so test tearDown can set it back. */ private static Schema schemaBeforeStartingFakeServer; /** Incremented by one each time the server has restarted. */ private static int serverRestarts; /** The paths to directories and files used in the tests. */ public static TestPaths paths = new TestPaths(); /** The ports used in the tests. */ private static TestPorts ports; /** The embedded server used in the tests. */ private static EmbeddedDirectoryServer server; /** The host name of the server used in the tests. */ private static String hostname; /** * Setup in-memory versions of everything needed to run unit tests with the * {@link DirectoryServer} class. *

* This method is trying hard to provide sensible defaults and core data you * would expect from a normal install, including AttributeTypes, etc. * * @see #shutdownFakeServer() Matching method that must be called in the test * tear down. */ public static void startFakeServer() throws Exception { DirectoryServer.bootstrapClient(); schemaBeforeStartingFakeServer = DirectoryServer.getInstance().getServerContext().getSchema(); DirectoryServer.getInstance().getServerContext().getSchemaHandler().updateSchema(Schema.getDefaultSchema()); } public static class TestPaths { final String buildRoot; final File buildDir; final File unitRoot; final String installedRoot; final File testInstallRoot; public final File testInstanceRoot; final File testConfigDir; final File configFile; final File testSrcRoot; TestPaths() { installedRoot = System.getProperty(PROPERTY_INSTALLED_ROOT); buildRoot = System.getProperty(PROPERTY_BUILD_ROOT,System.getProperty("user.dir")); String buildDirStr = System.getProperty(PROPERTY_BUILD_DIR, buildRoot + File.separator + "target"); buildDir = new File(buildDirStr); unitRoot = new File(buildDir, "unit-tests"+((testName!=null)?"/"+testName:"")); if (installedRoot == null) { testInstallRoot = new File(unitRoot, "package-install"); testInstanceRoot = new File(unitRoot, "package-instance"); } else { testInstallRoot = new File(unitRoot, "package"); testInstanceRoot = testInstallRoot; } testConfigDir = new File(testInstanceRoot, "config"); configFile = new File(testConfigDir, "config.ldif"); testSrcRoot = new File(buildRoot + File.separator + "tests" + File.separator + "unit-tests-testng"); } } static class TestPorts { /** The LDAP port the server is bound to on start. */ final int serverLdapPort; /** The Administration port the server is bound to on start. */ final int serverAdminPort; /** The JMX port the server is bound to on start. */ final int serverJmxPort; /** The LDAPS port the server is bound to on start. */ final int serverLdapsPort; TestPorts() throws IOException { final int[] ports = findFreePorts(4); serverLdapPort = getFreePort(PROPERTY_LDAP_PORT, ports[0]); serverAdminPort = getFreePort(PROPERTY_ADMIN_PORT, ports[1]); serverJmxPort = ports[2]; serverLdapsPort = ports[3]; } } public static void startServer() throws Exception { System.setProperty(PROPERTY_RUNNING_UNIT_TESTS, "true"); try { if (SERVER_STARTED) { return; } InvocationCounterPlugin.resetStartupCalled(); initializePortsAndServer(); deployDirectoryDirsAndFiles(); setupLoggers(); writeBuildInfoFile(); server.start(); assertTrue(InvocationCounterPlugin.startupCalled()); // Save config.ldif for when we restart the server backupServerConfigLdif(); SERVER_STARTED = true; initializeTestBackend(true); } catch (Exception e) { e.printStackTrace(originalSystemErr); throw e; } } private static void initializePortsAndServer() throws Exception { ports = new TestPorts(); hostname = "127.0.0.1"; server = manageEmbeddedDirectoryServer( configParams() .serverRootDirectory(paths.testInstallRoot.getPath()) .serverInstanceDirectory(paths.testInstanceRoot.getPath()) .configurationFile(paths.configFile.getPath()), connectionParams() .bindDn("cn=Directory Manager") .bindPassword("password") .hostName(hostname) .ldapPort(ports.serverLdapPort) .adminPort(ports.serverAdminPort), System.out, System.err); } public static void cleanTestPath() throws IOException { deleteDirectory(paths.unitRoot); } /** * Setup the directory server in separate install root directory and instance root directory. * After this method the directory server should be ready to be started. */ private static void deployDirectoryDirsAndFiles() throws IOException { // cleanup directories if necessary String cleanupRequiredString = System.getProperty(PROPERTY_CLEANUP_REQUIRED, "true"); boolean cleanupRequired = !"false".equalsIgnoreCase(cleanupRequiredString); //originalSystemErr.println("start "+paths.unitRoot); if (cleanupRequired) { deleteDirectory(paths.testInstallRoot); deleteDirectory(paths.testInstanceRoot); paths.testInstallRoot.mkdirs(); paths.testInstanceRoot.mkdirs(); } // deploy the server to separate install directory and instance directory File testInstanceSchema = new File(paths.testInstanceRoot, "config" + File.separator + "schema"); testInstanceSchema.mkdirs(); // db_verify is second jeb backend used by the jeb verify test cases // db_rebuild is the third jeb backend used by the jeb rebuild test cases // db_unindexed is the forth backend used by the unindexed search privilege // test cases String[] installSubDirectories = { "bin", "lib", "bat", "config" }; String[] instanceSubDirectories = { "bak", "changelogDb", "classes", "config", "db", "import-tmp", "db_verify", "ldif", "locks", "logs", "db_rebuild", "db_unindexed", "db_index_test", "db_import_test" }; for (String s : installSubDirectories) { new File(paths.testInstallRoot, s).mkdir(); } for (String s : instanceSubDirectories) { new File(paths.testInstanceRoot, s).mkdir(); } // Copy the configuration, schema, and MakeLDIF resources into the // appropriate place under the test package. File makeLdifResourcesDir = Paths.get(paths.buildRoot, "..", "opendj-core", "src", "main", "resources", "org", "forgerock", "opendj", "ldif").toAbsolutePath().toFile(); File serverClassesDir = new File(paths.buildDir, "classes"); File unitClassesDir = new File(paths.unitRoot, "classes"); File libDir = new File(paths.buildDir.getPath() + "/package/opendj/lib"); File upgradeDir = new File(paths.buildDir.getPath() + "/package/opendj/template/config/upgrade"); File resourceDir = new File(paths.buildRoot, "resource"); File testResourceDir = new File(paths.testSrcRoot, "resource"); // Set the class variable File testSchemaDir = new File(paths.testInstanceRoot, "config"); File testClassesDir = new File(paths.testInstanceRoot, "classes"); File testLibDir = new File(paths.testInstallRoot, "lib"); File testBinDir = new File(paths.testInstallRoot, "bin"); // Snmp resource String opendmkJarFileLocation = System.getProperty(PROPERTY_OPENDMK_LOCATION); File opendmkJar = new File(opendmkJarFileLocation, "jdmkrt.jar"); File snmpResourceDir = new File(paths.buildRoot + File.separator + "src" + File.separator + "snmp" + File.separator + "resource"); File snmpConfigDir = new File(snmpResourceDir, "config"); File testSnmpResourceDir = new File(paths.testConfigDir + File.separator + "snmp"); if (Boolean.getBoolean(PROPERTY_COPY_CLASSES_TO_TEST_PKG)) { copyDirectory(serverClassesDir, testClassesDir); copyDirectory(unitClassesDir, testClassesDir); } if (paths.installedRoot != null) { copyDirectory(new File(paths.installedRoot), paths.testInstallRoot); // Get the instance location } else { copyDirectory(libDir, testLibDir); copyDirectory(new File(resourceDir, "bin"), testBinDir); copyDirectory(new File(resourceDir, "config"), paths.testConfigDir); // copy upgrade directory copyDirectory(upgradeDir, new File(paths.testConfigDir, "upgrade")); copyDirectory(new File(resourceDir, "schema"), new File(testSchemaDir, "schema")); copyDirectory(makeLdifResourcesDir, new File(paths.testConfigDir, "MakeLDIF")); copyDirectory(new File(snmpResourceDir, "security"), new File(testSnmpResourceDir, "security")); copyFileFromTo("server.keystore", testResourceDir, paths.testConfigDir); copyFileFromTo("server.truststore", testResourceDir, paths.testConfigDir); copyFileFromTo("client.keystore", testResourceDir, paths.testConfigDir); copyFileFromTo("client-emailAddress.keystore", testResourceDir, paths.testConfigDir); copyFileFromTo("client.truststore", testResourceDir, paths.testConfigDir); copyFileFromTo("server-cert.p12", testResourceDir, paths.testConfigDir); copyFileFromTo("client-cert.p12", testResourceDir, paths.testConfigDir); // Update the install.loc file File installLoc = new File(paths.testInstallRoot + File.separator + "instance.loc"); installLoc.deleteOnExit(); try (FileWriter w = new FileWriter(installLoc)) { w.write(paths.testInstanceRoot.getAbsolutePath()); } if (opendmkJar.exists()) { appendFile(new File(snmpConfigDir, "config.snmp.ldif"), new File(paths.testConfigDir, "config.ldif")); } for (File f : testBinDir.listFiles()) { try { FilePermission.setPermissions(f, FilePermission.decodeUNIXMode("755")); } catch (Exception e) { } } // Make the shell scripts in the bin directory executable, if possible. if (OperatingSystem.isUnixBased()) { try { FilePermission perm = FilePermission.decodeUNIXMode("755"); for (File f : testBinDir.listFiles()) { if (f.getName().endsWith(".sh")) { FilePermission.setPermissions(f, perm); } } } catch (Exception e) { } } } copyTestConfigChangesFile(); } private static void copyTestConfigChangesFile() throws FileNotFoundException, IOException { File testResourceDir = new File(paths.testSrcRoot, "resource"); String defaultConfigChangeFile = testResourceDir + File.separator + "config-changes.ldif"; String configChangeFile = System.getProperty(PROPERTY_CONFIG_CHANGE_FILE, defaultConfigChangeFile); try (BufferedReader reader = new BufferedReader(new FileReader(new File(configChangeFile))); FileOutputStream outFile = new FileOutputStream(new File(paths.testConfigDir, "config-changes.ldif")); PrintStream writer = new PrintStream(outFile)) { String line; while ((line = reader.readLine()) != null) { line = line .replaceAll("#ldapport#", String.valueOf(ports.serverLdapPort)) .replaceAll("#adminport#", String.valueOf(ports.serverAdminPort)) .replaceAll("#jmxport#", String.valueOf(ports.serverJmxPort)) .replaceAll("#ldapsport#", String.valueOf(ports.serverLdapsPort)); writer.println(line); } } } private static void setupLoggers() { AccessLogger.getInstance().addLogPublisher( (AccessLogPublisher) getStartupTextAccessPublisher(ACCESS_TEXT_WRITER, false)); HTTPAccessLogger.getInstance().addLogPublisher( (HTTPAccessLogPublisher) getStartupTextHTTPAccessPublisher(HTTP_ACCESS_TEXT_WRITER)); // Enable more verbose error logger. ErrorLogger.getInstance().addLogPublisher( (ErrorLogPublisher) getToolStartupTextErrorPublisher(ERROR_TEXT_WRITER)); ErrorLogger.getInstance().addLogPublisher( (ErrorLogPublisher) getServerStartupTextErrorPublisher(ERROR_TEXT_WRITER)); DebugLogger.getInstance().addPublisherIfRequired(DEBUG_TEXT_WRITER); } private static void writeBuildInfoFile() throws IOException { try (final FileWriter buildInfoWriter = new FileWriter(new File(paths.testConfigDir, "buildinfo"))) { buildInfoWriter.write(BuildVersion.binaryVersion().toString()); } } private static int getFreePort(String portPropertyName, int defaultPort) throws IOException { String port = System.getProperty(portPropertyName); if (port == null) { return defaultPort; } int portNb = Integer.parseInt(port); // Check this port is free bindPort(portNb).close(); return portNb; } /** * Similar to startServer, but it will restart the server each time it is * called. Since this is somewhat expensive, it should be called under * two circumstances. Either in an @AfterClass method for a test that * makes lots of configuration changes to the server, or in a @BeforeClass * method for a test that is very sensitive to running in a clean server. * * @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 synchronized void restartServer() throws IOException, InitializationException, ConfigException, DirectoryException, Exception { if (!SERVER_STARTED) { startServer(); return; } try { new File(DirectoryServer.getEnvironmentConfig().getSchemaDirectory(), "99-user.ldif").delete(); long startMs = System.currentTimeMillis(); clearLoggersContents(); server.stop(TestCaseUtils.class.getSimpleName(), LocalizableMessage.raw("restart server for tests")); restoreServerConfigLdif(); server.start(); clearJEBackends(); initializeTestBackend(true); // This generates too much noise, so it's disabled by default. // outputLogContentsIfError("Potential problem during in-core restart. You be the judge."); // Keep track of these so we can report how long they took in the test summary long durationMs = System.currentTimeMillis() - startMs; restartTimesMs.add(durationMs); serverRestarts++; } catch (Exception e) { e.printStackTrace(originalSystemErr); throw e; } } /** * Returns the embedded server used for tests. * * @return the embedded server. */ public static EmbeddedDirectoryServer getServer() { return server; } private static List restartTimesMs = new ArrayList<>(); public static List getRestartTimesMs() { return Collections.unmodifiableList(restartTimesMs); } private static void clearJEBackends() throws Exception { for (LocalBackend backend : getServerContext().getBackendConfigManager().getLocalBackends()) { if (backend instanceof BackendImpl) { clearBackend(backend.getBackendID()); } } } public static void clearDataBackends() throws Exception { clearJEBackends(); clearMemoryBackend(TEST_BACKEND_ID); } private static File getTestConfigDir() { if (paths.testConfigDir == null) { throw new RuntimeException("The testConfigDir variable is not set yet!"); } return paths.testConfigDir; } public static File getBuildRoot() { String buildRoot = System.getProperty(PROPERTY_BUILD_ROOT,System.getProperty("user.dir")); return new File(buildRoot); } private static void backupServerConfigLdif() throws IOException { File testConfigDir = getTestConfigDir(); copyFile(new File(testConfigDir, "config.ldif"), new File(testConfigDir, "config.ldif.for-restart")); } private static void restoreServerConfigLdif() throws IOException { File testConfigDir = getTestConfigDir(); File from = new File(testConfigDir, "config.ldif.for-restart"); File to = new File(testConfigDir, "config.ldif"); // Sometimes this fails because config.ldif is in use, so we wait // and try it again. try { copyFile(from, to); } catch (IOException e) { sleep(1000); copyFile(from, to); } } /** * Bring the server to a quiescent state. This includes waiting for all * operations to complete. This can be used in a @BeforeMethod setup method * to make sure that the server has finished processing all operations * from previous tests. */ public static void quiesceServer() { waitForOpsToComplete(); } /** This can be made public if quiesceServer becomes too heavy-weight in some circumstance. */ private static void waitForOpsToComplete() { try { WorkQueue workQueue = DirectoryServer.getWorkQueue(); final long NO_TIMEOUT = -1; workQueue.waitUntilIdle(NO_TIMEOUT); } catch (Exception e) { // Ignore it, maybe the server hasn't been started. } } /** * Binds to the given socket port on the local host. * @return the bounded Server socket. * * @throws IOException in case of underlying exception. */ private static ServerSocket bindPort(int port) throws IOException { ServerSocket serverLdapSocket; serverLdapSocket = new ServerSocket(); serverLdapSocket.setReuseAddress(true); serverLdapSocket.bind(new InetSocketAddress(port)); serverLdapSocket.close(); serverLdapSocket = new ServerSocket(); serverLdapSocket.setReuseAddress(true); serverLdapSocket.bind(new InetSocketAddress("localhost",port)); serverLdapSocket.close(); serverLdapSocket = new ServerSocket(); serverLdapSocket.setReuseAddress(true); serverLdapSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(),port)); serverLdapSocket.close(); serverLdapSocket = new ServerSocket(); serverLdapSocket.setReuseAddress(true); serverLdapSocket.bind(new InetSocketAddress("127.0.0.1",port)); return serverLdapSocket; } static int port = 30000; /** * Find and binds to a free server socket port on the local host. Avoid allocating ephemeral ports since these may * be used by client applications such as dsconfig. Instead scan through ports starting from a reasonably high number * which avoids most reserved services (see /etc/services) and continues up to the beginning of the ephemeral port * range. On most Linux OSes this is 32768, but may be higher. * * @return the bounded Server socket. * * @throws IOException in case of underlying exception. */ public synchronized static ServerSocket bindFreePort() throws IOException { for (; port > 15000;) { try { return bindPort(port--); } catch (BindException e){} } throw new BindException("Unable to bind to a free port"); } /** * Find a free port on the local host. * * @throws IOException * in case of underlying exception. * @return the free port number found */ public static int findFreePort() throws IOException { return findFreePorts(1)[0]; } /** * Find nb free ports on the local host. * * @param nb * the number of free ports to find * @throws IOException * in case of underlying exception. * @return an array with the free port numbers found */ public static int[] findFreePorts(int nb) throws IOException { final ServerSocket[] sockets = new ServerSocket[nb]; try { final int[] ports = new int[nb]; for (int i = 0; i < nb; i++) { sockets[i] = bindFreePort(); ports[i] = sockets[i].getLocalPort(); } close(sockets); return ports; } finally { } } /** * Finds a free server socket port on the local host. * * @return The free port. */ public static SocketAddress findFreeSocketAddress() { try (ServerSocket serverLdapSocket = bindFreePort()) { return serverLdapSocket.getLocalSocketAddress(); } catch (IOException e) { throw new RuntimeException(e); } } /** * Undo all the setup done by #startFakeServer(). * * @throws DirectoryException * If the initial schema contains warning * @see #startFakeServer() Matching method that starts the fake server */ public static void shutdownFakeServer() throws DirectoryException { DirectoryServer.getInstance().getServerContext().getSchemaHandler().updateSchema(schemaBeforeStartingFakeServer); } /** Returns the server context. */ public static ServerContext getServerContext() { ServerContext serverContext = DirectoryServer.getInstance().getServerContext(); if (serverContext == null) { throw new RuntimeException("Server context is null"); } return serverContext; } /** * Shut down the server. This should only be called at the end of the test * suite and not by any unit tests. * * @param reason * The reason for the shutdown. */ static void shutdownServer(LocalizableMessage 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 { initializeMemoryBackend(TEST_BACKEND_ID, TEST_ROOT_DN_STRING, createBaseEntry); } /** * 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. * * @param backendID the ID of the backend to create * @param namingContext the naming context to create in the backend * @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 initializeMemoryBackend( String backendID, String namingContext, boolean createBaseEntry ) throws Exception { startServer(); DN baseDN = DN.valueOf(namingContext); // Retrieve backend. Warning: it is important to perform this each time, // because a test may have disabled then enabled the backend (i.e a test // performing an import task). As it is a memory backend, when the backend // is re-enabled, a new backend object is in fact created and old reference // to memory backend must be invalidated. So to prevent this problem, we // retrieve the memory backend reference each time before cleaning it. BackendConfigManager backendConfigManager = getServerContext().getBackendConfigManager(); MemoryBackend memoryBackend = (MemoryBackend) backendConfigManager.getLocalBackendById(backendID); if (memoryBackend == null) { memoryBackend = new MemoryBackend(); memoryBackend.setBackendID(backendID); memoryBackend.setBaseDNs(baseDN); memoryBackend.configureBackend(null, getServerContext()); memoryBackend.openBackend(); backendConfigManager.registerLocalBackend(memoryBackend); } memoryBackend.clearMemoryBackend(); if (createBaseEntry) { Entry e = createEntry(baseDN); memoryBackend.addEntry(e, null); } } /** Clears a memory-based backend. */ public static void clearMemoryBackend(String backendID) throws Exception { MemoryBackend memoryBackend = (MemoryBackend) getServerContext().getBackendConfigManager().getLocalBackendById(backendID); // FIXME JNR I suspect we could call finalizeBackend() here (but also in other // places in this class), because finalizeBackend() calls clearMemoryBackend(). if (memoryBackend != null) { memoryBackend.clearMemoryBackend(); } } /** * Clears all the entries from the backend determined by the backend id passed into the method. * * @throws Exception If an unexpected problem occurs. */ public static void clearBackend(String backendId) throws Exception { clearBackend(backendId, null); } /** * Clears all the entries from the backend determined by the backend id passed into the method. * * @param backendId The backend id to clear * @param baseDN If not null, the suffix of the backend to create * @throws Exception If an unexpected problem occurs. */ public static void clearBackend(String backendId, String baseDN) throws Exception { LocalBackend b = getServerContext().getBackendConfigManager().getLocalBackendById(backendId); if (clearBackend(b) && baseDN != null) { Entry e = createEntry(DN.valueOf(baseDN)); b.addEntry(e, mock(AddOperation.class)); } } private static boolean clearBackend(LocalBackend b) { if (b instanceof BackendImpl) { final BackendImpl backend = (BackendImpl) b; final RootContainer rootContainer = backend.getRootContainer(); if (rootContainer != null) { for (EntryContainer ec : rootContainer.getEntryContainers()) { ec.clear(); // assertEquals(ec.getHighestEntryID().longValue(), 0L); } rootContainer.resetNextEntryID(); return true; } } return false; } /** * 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 tmpDir = File.createTempFile(prefix, null); if (!tmpDir.delete()) { throw new IOException("Unable to delete temporary file: " + tmpDir); } if (!tmpDir.mkdir()) { throw new IOException("Unable to create temporary directory: " + tmpDir); } return tmpDir; } /** * 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 == null || !dir.exists()) { return; } if (dir.isDirectory()) { // Recursively delete sub-directories and files. for (String child : dir.list()) { deleteDirectory(new File(dir, child)); } } dir.delete(); } private static void copyFileFromTo(String filename, File fromDir, File toDir) throws IOException { copyFile(new File(fromDir, filename), new File(toDir, filename)); } /** * 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 { copyOrAppend(src, dst, false); } public static void appendFile(File src, File dst) throws IOException { copyOrAppend(src, dst, true); } private static void copyOrAppend(File src, File dst, boolean append) throws IOException { try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst, append)) { // Transfer bytes from in to out byte[] buf = new byte[8192]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } } } /** * Get the LDAP port the test environment Directory Server instance is * running on. * * @return The port number. */ public static int getServerLdapPort() { return ports.serverLdapPort; } /** * Get the Admin port the test environment Directory Server instance is * running on. * * @return The port number. */ public static int getServerAdminPort() { return ports.serverAdminPort; } /** * Get the JMX port the test environment Directory Server instance is * running on. * * @return The port number. */ public static int getServerJmxPort() { return ports.serverJmxPort; } /** * Get the LDAPS port the test environment Directory Server instance is * running on. * * @return The port number. */ public static int getServerLdapsPort() { return ports.serverLdapsPort; } /** * Get the number of times the server has done a restart during the unit tests. * * @return the number of server restarts. */ public static int getNumServerRestarts() { return serverRestarts; } /** * Method for getting a file from the test resources directory. * * @return The directory as a File */ public static File getTestResource(String filename) { File testResourceDir = new File(paths.testSrcRoot, "resource"); return new File(testResourceDir, filename); } public static File getUnitTestRootPath() { return paths.unitRoot; } /** Get the complete path to the OpenDJ archive. */ public static File getOpenDJArchivePath() { String qualifier = DynamicConstants.VERSION_QUALIFIER; String openDJArchiveName = DynamicConstants.SHORT_NAME.toLowerCase() + "-" + DynamicConstants.VERSION_NUMBER_STRING + (qualifier != null && !qualifier.isEmpty() ? "-" + qualifier : ""); return getBuildRoot().toPath().resolve("target/package").resolve(openDJArchiveName + ".zip").toFile(); } /** 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)); ldifImportConfig.setValidateSchema(false); try (LDIFReader reader = new LDIFReader(ldifImportConfig)) { List entries = new ArrayList<>(); Entry entry; 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 (String line : lines) { buffer.append(line).append(EOL); } // Append an extra line so we can append LDIF Strings. buffer.append(EOL); return buffer.toString(); } /** * This is a convenience 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 convenience 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)); } /** * Adds the provided entry to the Directory Server using an internal * operation. * * @param entry The entry to be added. * * @throws Exception If an unexpected problem occurs. */ public static void addEntry(Entry entry) throws Exception { AddOperation addOperation = getRootConnection().processAdd(entry); assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS,entry.toString()); } /** * Deletes the provided entry from the Directory Server using an * internal operation. * * @param entry The entry to be deleted. * * @throws Exception If an unexpected problem occurs. */ public static void deleteEntry(Entry entry) throws Exception { deleteEntry(entry.getName()); } /** * Deletes the provided entry from the Directory Server using an * internal operation. * * @param dn The dn of entry to be deleted * * @throws Exception If an unexpected problem occurs. */ public static void deleteEntry(DN dn) throws Exception { DeleteOperation deleteOperation = getRootConnection().processDelete(dn); assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); } public static boolean canBind(String dn, String pw) throws Exception { // Check that the user can bind. try (Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort())) { TestCaseUtils.configureSocket(s); ASN1Reader r = ASN1.getReader(s.getInputStream()); ASN1Writer w = ASN1.getWriter(s.getOutputStream()); BindRequestProtocolOp bindRequest = new BindRequestProtocolOp( ByteString.valueOfUtf8(dn), 3, ByteString.valueOfUtf8(pw)); LDAPMessage message = new LDAPMessage(1, bindRequest); message.write(w); message = LDAPReader.readMessage(r); BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp(); return bindResponse.getResultCode() == 0; } catch (Exception t) { t.printStackTrace(); return false; } } /** * Configures a socket for use in unit tests. This should only be used if the * socket is not expected to timeout. * * @param s * The socket. * @throws Exception * If an unexpected exception occurred while configuring the socket. */ public static void configureSocket(Socket s) throws Exception { s.setReuseAddress(true); s.setSoTimeout(60 * 1000); } /** * Adds the provided entry to the Directory Server using an internal * operation. * * @param lines The lines that make up the entry to be added. * @return the added entry * @throws Exception If an unexpected problem occurs. */ public static Entry addEntry(String... lines) throws Exception { final Entry entry = makeEntry(lines); AddOperation addOperation = getRootConnection().processAdd(entry); assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS, addOperation.getErrorMessage().toString()); final Entry e = DirectoryServer.getEntry(entry.getName()); assertNotNull(e); return e; } /** * Adds the provided entry to the Directory Server using an internal * operation. * * @param lines The lines that make up the entry to be added. * * @return result code for this operation. * * @throws Exception If an unexpected problem occurs. */ public static ResultCode addEntryOperation(String... lines) throws Exception { Entry entry = makeEntry(lines); AddOperation addOperation = getRootConnection().processAdd(entry); return addOperation.getResultCode(); } /** * Adds the provided set of entries to the Directory Server using internal * operations. * * @param entries The entries to be added. * * @throws Exception If an unexpected problem occurs. */ public static void addEntries(List entries) throws Exception { for (Entry entry : entries) { addEntry(entry); } } /** * Adds the provided set of entries to the Directory Server using internal * operations. * * @param lines The lines defining the entries to add. If there are * multiple entries, then they should be separated by blank * lines. * * @throws Exception If an unexpected problem occurs. */ public static void addEntries(String... lines) throws Exception { for (Entry entry : makeEntries(lines)) { addEntry(entry); } } /** * Applies a set of modifications to the server as described in the provided * set of lines (using LDIF change form). The changes will be applied over * LDAP using the ldapmodify tool using the "cn=Directory Manager" account. * * @param lines The set of lines including the changes to make to the * server. * * @return The result code from applying the set of modifications. if it is * nonzero, then there was a failure of some kind. * * @throws Exception If an unexpected problem occurs. */ public static int applyModifications(boolean useAdminPort, String... lines) throws Exception { if (! SERVER_STARTED) { startServer(); } String path = createTempFile(lines); String[] args = { "--noPropertiesFile", "-h", "127.0.0.1", "-p", String.valueOf(ports.serverLdapPort), "-D", "cn=Directory Manager", "-w", "password", "-f", path }; String[] adminArgs = { "--noPropertiesFile", "-h", "127.0.0.1", "-p", String.valueOf(ports.serverAdminPort), "-Z", "-X", "-D", "cn=Directory Manager", "-w", "password", "-f", path }; if (useAdminPort) { return LDAPModify.run(nullPrintStream(), nullPrintStream(), adminArgs); } return LDAPModify.run(nullPrintStream(), nullPrintStream(), args); } /** * 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(); final String EOL = System.getProperty("line.separator"); try (FileWriter w = new FileWriter(f)) { for (String s : lines) { w.write(s + EOL); } } 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. } } /** Return a Map constructed via alternating key and value pairs. */ public static Map makeMap(String... keyValuePairs) { Map map = new LinkedHashMap<>(); for (int i = 0; i < keyValuePairs.length; i += 2) { map.put(keyValuePairs[i], keyValuePairs[i+1]); } return map; } // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- /** The set of loggers for which the console logger has been disabled. */ private static final Map disabledLogHandlers = new HashMap<>(); /** The original System.err print stream. Use this if you absolutely * must write something to System.err. */ public static final PrintStream originalSystemErr = System.err; /** The original System.out print stream. Use this if you absolutely * must write something to System.out. */ public static final PrintStream originalSystemOut = System.out; /** System.err is redirected to here so that we can only print it out if a test fails. */ private static final ByteArrayOutputStream redirectedSystemErr = new ByteArrayOutputStream(); /** System.out is redirected to here so that we can only print it out if a test fails. */ private static final ByteArrayOutputStream redirectedSystemOut = new ByteArrayOutputStream(); public static synchronized void suppressOutput() { String suppressStr = System.getProperty("org.opends.test.suppressOutput"); if ("true".equalsIgnoreCase(suppressStr)) { System.setOut(new PrintStream(redirectedSystemOut)); System.setErr(new PrintStream(redirectedSystemErr)); LogManager logManager = LogManager.getLogManager(); Enumeration loggerNames = logManager.getLoggerNames(); while (loggerNames.hasMoreElements()) { String loggerName = loggerNames.nextElement(); Logger logger = logManager.getLogger(loggerName); if(logger == null) { break; } for (Handler h : logger.getHandlers()) { if (h instanceof ConsoleHandler) { disabledLogHandlers.put(logger, h); logger.removeHandler(h); break; } } } } } /** * @return everything written to System.out since the last time * clearSystemOutContents was called. */ public static synchronized String getSystemOutContents() { return redirectedSystemOut.toString(); } /** * @return everything written to System.err since the last time * clearSystemErrContents was called. */ public static synchronized String getSystemErrContents() { return redirectedSystemErr.toString(); } /** * Clear everything written to System.out since the last time * clearSystemOutContents was called. */ public static synchronized void clearSystemOutContents() { redirectedSystemOut.reset(); } /** * Clear everything written to System.err since the last time * clearSystemErrContents was called. */ public static synchronized void clearSystemErrContents() { redirectedSystemErr.reset(); } /** Clear everything written to the Access, Error, or Debug loggers. */ public static synchronized void clearLoggersContents() { ACCESS_TEXT_WRITER.clear(); ERROR_TEXT_WRITER.clear(); DEBUG_TEXT_WRITER.clear(); clearSystemOutContents(); clearSystemErrContents(); } /** * Append the contents of the Access Log, Error Log, Debug Loggers, * System.out, System.err to the specified buffer. */ public static void appendLogsContents(StringBuilder logsContents) { appendMessages(logsContents, TestCaseUtils.ERROR_TEXT_WRITER, "Error Log Messages:"); appendMessages(logsContents, TestCaseUtils.DEBUG_TEXT_WRITER, "Debug Log Messages:"); appendMessages(logsContents, TestCaseUtils.ACCESS_TEXT_WRITER, "Access Log Messages:"); appendStreamContent(logsContents, TestCaseUtils.getSystemOutContents(), "System.out"); appendStreamContent(logsContents, TestCaseUtils.getSystemErrContents(), "System.err"); if (new File(paths.testInstanceRoot, "logs").listFiles()!=null) { for (final File logFile : Arrays.asList(new File(paths.testInstanceRoot, "logs").listFiles())) { try { appendStreamContent(logsContents, readFile(logFile.getPath()), logFile.getPath()); } catch (IOException e) { e.printStackTrace(); } } } } private static void appendStreamContent(StringBuilder out, String content, String name) { if (content.length() > 0) { out.append(EOL).append(name).append(" contents:").append(EOL).append(content); } } private static void appendMessages(StringBuilder out, TestTextWriter textWriter, String loggerType) { List messages = textWriter.getMessages(); if (!messages.isEmpty()) { out.append(EOL); out.append(loggerType); out.append(EOL); for (String message : messages) { out.append(message); out.append(EOL); } } } public static synchronized void unsupressOutput() { String suppressStr = System.getProperty("org.opends.test.suppressOutput"); if ("true".equalsIgnoreCase(suppressStr)) { System.setOut(originalSystemOut); System.setErr(originalSystemErr); for (Map.Entry entry : disabledLogHandlers.entrySet()) { Logger l = entry.getKey(); Handler h = entry.getValue(); l.addHandler(h); } disabledLogHandlers.clear(); } } /** Read the contents of a file and return it as a String. */ public static String readFile(String name) throws IOException { return readFile(new File(name)); } /** Read the contents of a file and return it as a String. */ public static String readFile(File file) throws IOException { return new String(readFileBytes(file)); } /** * Returns the contents of file as a List of the lines as defined by * java.io.BufferedReader#readLine() (i.e. the line terminator is not * included). An ArrayList is explicitly returned, so that callers know that * random access is not expensive. */ public static ArrayList readFileToLines(File file) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); ArrayList lines = new ArrayList<>(); String line; while ((line = reader.readLine()) != null) { lines.add(line); } return lines; } /** Read the contents of a file and return it as a String. */ private static byte[] readFileBytes(File file) throws IOException { FileInputStream fis = new FileInputStream(file); return readInputStreamBytes(fis, true); } /** * @param close - if true, close when finished reading. * @return input stream content. */ private static byte[] readInputStreamBytes(InputStream is, boolean close) throws IOException { byte[] bytes = null; if (is != null) { ByteArrayOutputStream bout = new ByteArrayOutputStream(1024); try { byte[] buf = new byte[1024]; int bytesRead; while ((bytesRead = is.read(buf)) != -1) { bout.write(buf, 0, bytesRead); } // end of while ((read(buf) != -1) bytes = bout.toByteArray(); } finally { if (close) { close(is); } } } return bytes; } /** Store the contents of a String in a file. */ public static void writeFile(File file, String contents) throws IOException { writeFile(file.getAbsolutePath(), contents); } /** Store the contents of a String in a file. */ public static void writeFile(String name, String contents) throws IOException { writeFile(name, contents.getBytes()); } /** Store the contents of a String in a file. */ public static void writeFile(String path, byte[] contents) throws IOException { try (FileOutputStream fos = new FileOutputStream(path)) { fos.write(contents); } } /** * Invokes the dsconfig tool with the provided set of arguments. Note that * the address, port, bind DN (cn=Directory Manager), and password will always * be provided, so they should not be included in the argument list. The * given arguments should include only the subcommand and its associated * options, along with any other global options that may not be included by * default. *

* An assertion will be used to ensure that the dsconfig invocation is * successful. If running dsconfig returns a non-zero result, then an * assertion error will be thrown. * * @param args The set of arguments that should be provided when invoking * the dsconfig tool */ public static void dsconfig(String... args) { String[] fullArgs = new String[args.length + 11]; fullArgs[0] = "-h"; fullArgs[1] = hostname; fullArgs[2] = "-p"; fullArgs[3] = String.valueOf(ports.serverAdminPort); fullArgs[4] = "-D"; fullArgs[5] = "cn=Directory Manager"; fullArgs[6] = "-w"; fullArgs[7] = "password"; fullArgs[8] = "-n"; fullArgs[9] = "--noPropertiesFile"; fullArgs[10] = "-X"; System.arraycopy(args, 0, fullArgs, 11, args.length); assertEquals(DSConfig.main(fullArgs, System.out, System.err), 0); } /** * Return a String representation of all of the current threads. * @return a dump of all Threads on the server */ public static String threadStacksToString() { Map threadStacks = Thread.getAllStackTraces(); // Re-arrange all of the elements by thread ID so that there is some logical order. Map> orderedStacks = new TreeMap<>(); for (Map.Entry e : threadStacks.entrySet()) { orderedStacks.put(e.getKey().getId(), e); } final StringBuilder buffer = new StringBuilder(); for (Map.Entry e : orderedStacks.values()) { Thread t = e.getKey(); StackTraceElement[] stackElements = e.getValue(); long id = t.getId(); buffer.append("id="); buffer.append(id); buffer.append(" ---------- "); buffer.append(t.getName()); buffer.append(" ----------"); buffer.append(EOL); if (stackElements != null) { for (StackTraceElement stackElement : stackElements) { buffer.append(" ").append(stackElement.getClassName()); buffer.append("."); buffer.append(stackElement.getMethodName()); buffer.append("("); buffer.append(stackElement.getFileName()); buffer.append(":"); if (stackElement.isNativeMethod()) { buffer.append("native"); } else { buffer.append(stackElement.getLineNumber()); } buffer.append(")").append(EOL); } } buffer.append(EOL); } return buffer.toString(); } public static void enableBackend(String backendID) { setBackendEnabled(backendID, true); } public static void disableBackend(String backendID) { setBackendEnabled(backendID, false); } private static void setBackendEnabled(String backendID, boolean enabled) { dsconfig("set-backend-prop", "--backend-name", backendID, "--set", "enabled:" + enabled); } public static HashSet getPluginTypes(Entry e) { HashSet pluginTypes = new HashSet<>(); for (Attribute a : e.getAllAttributes("ds-cfg-plugin-type")) { for (ByteString v : a) { pluginTypes.add(PluginType.forName(v.toString().toLowerCase())); } } return pluginTypes; } /** Saves a thread dump in a file with the provided id used in file prefix. */ public static void generateThreadDump(String id) { String date = new SimpleDateFormat("yyyyMMdd_hhmmss").format(new Date().getTime()); try (BufferedWriter writer = new BufferedWriter(new FileWriter("/tmp/thread_dump_" + id + "_" + date))) { writer.write(generateThreadDump()); } catch (Exception e) { // do nothing } } /** Generates a thread dump programmatically. */ public static String generateThreadDump() { final StringBuilder dump = new StringBuilder(); final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); final ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100); for (ThreadInfo threadInfo : threadInfos) { dump.append('"'); dump.append(threadInfo.getThreadName()); dump.append("\" "); final Thread.State state = threadInfo.getThreadState(); dump.append("\n java.lang.Thread.State: "); dump.append(state); final StackTraceElement[] stackTraceElements = threadInfo.getStackTrace(); for (final StackTraceElement stackTraceElement : stackTraceElements) { dump.append("\n at "); dump.append(stackTraceElement); } dump.append("\n\n"); } return dump.toString(); } // /** FIXME Replace with {@link Assert#assertNotEquals(Object, Object)} once we upgrade to testng >= 6.1. */ // public static void assertNotEquals(Object actual1, Object actual2) // { // assertNotEquals(actual1, actual2, null); // } // /** FIXME Replace with {@link Assert#assertNotEquals(Object, Object, String)} once we upgrade to testng >= 6.1. */ // public static void assertNotEquals(Object actual1, Object actual2, String message) // { // try // { // Assert.assertEquals(actual1, actual2); // Assert.fail(message); // } // catch (AssertionError e) // { // // this is good: they are not equals // return; // } // } public static int runLdapSearchTrustCertificateForSession(final String[] args) { return runLdapSearchTrustCertificateForSession(nullPrintStream(), System.err, args); } public static int runLdapSearchTrustCertificateForSession(final PrintStream out, final PrintStream err, final String[] args) { final InputStream stdin = System.in; try { // Since hostnames are different between the client.truststore (CN=OpenDJ Test Certificate, O=OpenDJ.org) and the // one given in parameter (127.0.0.1), ldapsearch tool prompt user to know what to do (either untrust the server // certificate, trust it for the session only or trust it permanently). // Default option is session trust, we just hit enter in stdin to have a non blocking unit test. System.setIn(new ByteArrayInputStream(System.lineSeparator().getBytes())); return LDAPSearch.run(nullPrintStream(), System.err, args); } finally { System.setIn(stdin); } } static String testName=null; public static void setTestName(String name) { testName=name; paths=new TestPaths(); //originalSystemErr.println(paths.unitRoot); } }