Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java
38855 views
/*1* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @library /lib/testlibrary26* lib/27* @run testng/othervm LdapTimeoutTest28* @bug 7094377 8000487 6176036 7056489 815167829* @summary Timeout tests for ldap30*/3132import org.testng.Assert;33import org.testng.annotations.BeforeTest;34import org.testng.annotations.Test;3536import javax.naming.Context;37import javax.naming.NamingException;38import javax.naming.directory.InitialDirContext;39import javax.naming.directory.SearchControls;40import java.io.IOException;41import java.io.OutputStream;42import java.net.Socket;43import java.util.ArrayList;44import java.util.Hashtable;45import java.util.List;46import java.util.Objects;47import java.util.concurrent.Callable;48import java.util.concurrent.CompletableFuture;49import java.util.concurrent.ExecutionException;50import java.util.concurrent.ExecutorService;51import java.util.concurrent.Executors;52import java.util.concurrent.Future;53import java.util.concurrent.FutureTask;54import java.util.concurrent.SynchronousQueue;55import java.util.concurrent.TimeUnit;56import java.util.concurrent.TimeoutException;5758import static java.lang.String.format;59import static java.util.concurrent.TimeUnit.MILLISECONDS;60import static java.util.concurrent.TimeUnit.NANOSECONDS;61import static jdk.testlibrary.Utils.adjustTimeout;62import static org.testng.Assert.assertTrue;63import static org.testng.Assert.expectThrows;6465public class LdapTimeoutTest {6667// ------ configure test timeouts here ------6869/*70* Practical representation of an infinite timeout.71*/72private static final long INFINITY_MILLIS = adjustTimeout(20_000);73/*74* The acceptable variation in timeout measurements.75*/76private static final long TOLERANCE = adjustTimeout( 3_500);7778private static final long CONNECT_MILLIS = adjustTimeout( 3_000);79private static final long READ_MILLIS = adjustTimeout(10_000);8081static {82// a series of checks to make sure this timeouts configuration is83// consistent and the timeouts do not overlap8485assert (TOLERANCE >= 0);86// context creation87assert (2 * CONNECT_MILLIS + TOLERANCE < READ_MILLIS);88// context creation immediately followed by search89assert (2 * CONNECT_MILLIS + READ_MILLIS + TOLERANCE < INFINITY_MILLIS);90}9192@BeforeTest93public void beforeTest() {94startAuxiliaryDiagnosticOutput();95}9697/*98* These are timeout tests and they are run in parallel to reduce the total99* amount of run time.100*101* Currently it doesn't seem possible to instruct JTREG to run TestNG test102* methods in parallel. That said, this JTREG test is still103* a "TestNG-flavored" test for the sake of having org.testng.Assert104* capability.105*/106@Test107public void test() throws Exception {108List<Future<?>> futures = new ArrayList<>();109ExecutorService executorService = Executors.newCachedThreadPool();110try {111futures.add(executorService.submit(() -> { test1(); return null; }));112futures.add(executorService.submit(() -> { test2(); return null; }));113futures.add(executorService.submit(() -> { test3(); return null; }));114futures.add(executorService.submit(() -> { test4(); return null; }));115futures.add(executorService.submit(() -> { test5(); return null; }));116futures.add(executorService.submit(() -> { test6(); return null; }));117futures.add(executorService.submit(() -> { test7(); return null; }));118} finally {119executorService.shutdown();120}121int failedCount = 0;122for (Future f : futures) {123try {124f.get();125} catch (ExecutionException e) {126failedCount++;127e.getCause().printStackTrace(System.out);128}129}130if (failedCount > 0)131throw new RuntimeException(failedCount + " (sub)tests failed");132}133134static void test1() throws Exception {135Hashtable<Object, Object> env = new Hashtable<>();136env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");137// Here and in the other tests it's important to close the server as138// calling `thread.interrupt` from assertion may not be enough139// (depending on where the blocking call has stuck)140try (TestServer server = new NotBindableServer()) {141env.put(Context.PROVIDER_URL, urlTo(server));142server.start();143// Here and in the other tests joining done purely to reduce timing144// jitter. Commenting out or removing that should not make the test145// incorrect. (ServerSocket can accept connection as soon as it is146// bound, not need to call `accept` before that.)147server.starting().join();148assertIncompletion(INFINITY_MILLIS, () -> new InitialDirContext(env));149}150}151152static void test2() throws Exception {153Hashtable<Object, Object> env = new Hashtable<>();154env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");155env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));156try (TestServer server = new BindableButNotReadableServer()) {157env.put(Context.PROVIDER_URL, urlTo(server));158server.start();159server.starting().join();160InitialDirContext ctx = new InitialDirContext(env);161SearchControls scl = new SearchControls();162scl.setSearchScope(SearchControls.SUBTREE_SCOPE);163assertIncompletion(INFINITY_MILLIS,164() -> ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl));165}166}167168static void test3() throws Exception {169Hashtable<Object, Object> env = new Hashtable<>();170env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");171try (TestServer server = new BindableButNotReadableServer()) {172env.put(Context.PROVIDER_URL, urlTo(server));173server.start();174server.starting().join();175InitialDirContext ctx = new InitialDirContext(env);176SearchControls scl = new SearchControls();177scl.setSearchScope(SearchControls.SUBTREE_SCOPE);178assertIncompletion(INFINITY_MILLIS,179() -> ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl));180}181}182183static void test4() throws Exception {184Hashtable<Object, Object> env = new Hashtable<>();185env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");186env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));187env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));188try (TestServer server = new NotBindableServer()) {189env.put(Context.PROVIDER_URL, urlTo(server));190server.start();191server.starting().join();192Assert.ThrowingRunnable completion =193() -> assertCompletion(CONNECT_MILLIS,1942 * CONNECT_MILLIS + TOLERANCE,195() -> new InitialDirContext(env));196NamingException e = expectThrows(NamingException.class, completion);197String msg = e.getMessage();198assertTrue(msg != null && msg.contains("timeout")199&& msg.contains(String.valueOf(CONNECT_MILLIS)),200msg);201}202}203204static void test5() throws Exception {205Hashtable<Object, Object> env = new Hashtable<>();206env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");207env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));208env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));209try (TestServer server = new BindableButNotReadableServer()) {210env.put(Context.PROVIDER_URL, urlTo(server));211server.start();212server.starting().join();213InitialDirContext ctx = new InitialDirContext(env);214SearchControls scl = new SearchControls();215scl.setSearchScope(SearchControls.SUBTREE_SCOPE);216Assert.ThrowingRunnable completion =217() -> assertCompletion(READ_MILLIS,218READ_MILLIS + TOLERANCE,219() -> ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl));220NamingException e = expectThrows(NamingException.class, completion);221String msg = e.getMessage();222assertTrue(msg != null && msg.contains("timeout")223&& msg.contains(String.valueOf(READ_MILLIS)),224msg);225}226}227228static void test6() throws Exception {229Hashtable<Object, Object> env = new Hashtable<>();230env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");231env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));232env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));233try (TestServer server = new NotBindableServer()) {234env.put(Context.PROVIDER_URL, urlTo(server));235server.start();236server.starting().join();237Assert.ThrowingRunnable completion =238() -> assertCompletion(CONNECT_MILLIS,2392 * CONNECT_MILLIS + TOLERANCE,240() -> new InitialDirContext(env));241NamingException e = expectThrows(NamingException.class, completion);242String msg = e.getMessage();243assertTrue(msg != null && msg.contains("timeout")244&& msg.contains(String.valueOf(CONNECT_MILLIS)),245msg);246}247}248249static void test7() throws Exception {250// 8000487: Java JNDI connection library on ldap conn is251// not honoring configured timeout252Hashtable<Object, Object> env = new Hashtable<>();253env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");254env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));255env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));256env.put(Context.SECURITY_AUTHENTICATION, "simple");257env.put(Context.SECURITY_PRINCIPAL, "user");258env.put(Context.SECURITY_CREDENTIALS, "password");259try (TestServer server = new NotBindableServer()) {260env.put(Context.PROVIDER_URL, urlTo(server));261server.start();262server.starting().join();263Assert.ThrowingRunnable completion =264() -> assertCompletion(CONNECT_MILLIS,2652 * CONNECT_MILLIS + TOLERANCE,266() -> new InitialDirContext(env));267NamingException e = expectThrows(NamingException.class, completion);268String msg = e.getMessage();269assertTrue(msg != null && msg.contains("timeout")270&& msg.contains(String.valueOf(CONNECT_MILLIS)),271msg);272}273}274275// ------ test stub servers ------276277static class TestServer extends BaseLdapServer {278279private final CompletableFuture<Void> starting = new CompletableFuture<>();280281TestServer() throws IOException { }282283@Override284protected void beforeAcceptingConnections() {285CompletableFuture.supplyAsync(() -> null)286.whenComplete((input, exception) -> {287starting.complete(null);288});289}290291public CompletableFuture<Void> starting() {292return starting.thenApply((x) -> x);293}294}295296static class BindableButNotReadableServer extends TestServer {297298BindableButNotReadableServer() throws IOException { }299300private static final byte[] bindResponse = {3010x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A,3020x01, 0x00, 0x04, 0x00, 0x04, 0x00303};304305@Override306protected void handleRequest(Socket socket,307LdapMessage msg,308OutputStream out)309throws IOException {310switch (msg.getOperation()) {311case BIND_REQUEST:312out.write(bindResponse);313out.flush();314default:315break;316}317}318}319320static class NotBindableServer extends TestServer {321322NotBindableServer() throws IOException { }323324@Override325protected void beforeConnectionHandled(Socket socket) {326try {327TimeUnit.DAYS.sleep(Integer.MAX_VALUE);328} catch (InterruptedException e) {329Thread.currentThread().interrupt();330}331}332}333334// ------ timeouts check utilities ------335336/*337* Asserts that the specified executable yields a result or an exception338* within the specified time frame. Interrupts the executable339* unconditionally.340*341* If the executable yields a result or an exception within the specified342* time frame, the result will be returned and the exception will be343* rethrown respectively in a transparent fashion as if the executable was344* executed directly.345*/346public static <T> T assertCompletion(long loMillis,347long hiMillis,348Callable<T> code)349throws Throwable {350if (loMillis < 0 || hiMillis < 0 || loMillis > hiMillis) {351throw new IllegalArgumentException("loMillis=" + loMillis +352", hiMillis=" + hiMillis);353}354Objects.requireNonNull(code);355356// this queue acts both as an exchange point and a barrier357SynchronousQueue<Long> startTime = new SynchronousQueue<>();358359Callable<T> wrappedTask = () -> {360// by the time this value reaches the "stopwatch" thread it might be361// well outdated and that's okay, we will adjust the wait time362startTime.put(System.nanoTime());363return code.call();364};365366FutureTask<T> task = new FutureTask<>(wrappedTask);367Thread t = new Thread(task);368t.start();369370final long startNanos;371try {372startNanos = startTime.take(); // (1) wait for the initial time mark373} catch (Throwable e) {374t.interrupt();375throw e;376}377378final long waitTime = hiMillis -379NANOSECONDS.toMillis(System.nanoTime() - startNanos); // (2) adjust wait time380381try {382T r = task.get(waitTime, MILLISECONDS); // (3) wait for the task to complete383long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);384if (elapsed < loMillis || elapsed > hiMillis) {385throw new RuntimeException(format(386"After %s ms. (waitTime %s ms.) returned result '%s'", elapsed, waitTime, r));387}388return r;389} catch (ExecutionException e) {390long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);391if (elapsed < loMillis || elapsed > hiMillis) {392throw new RuntimeException(format(393"After %s ms. (waitTime %s ms.) thrown exception", elapsed, waitTime), e);394}395throw e.getCause();396} catch (TimeoutException e) {397// We trust timed get not to throw TimeoutException prematurely398// (i.e. before the wait time elapses)399long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);400throw new RuntimeException(format(401"After %s ms. (waitTime %s ms.) is incomplete", elapsed, waitTime));402} finally {403t.interrupt();404}405}406407/*408* Asserts that the specified executable yields no result and no exception409* for at least the specified amount of time. Interrupts the executable410* unconditionally.411*/412public static void assertIncompletion(long millis, Callable<?> code)413throws Exception414{415if (millis < 0) {416throw new IllegalArgumentException("millis=" + millis);417}418Objects.requireNonNull(code);419420// this queue acts both as an exchange point and a barrier421SynchronousQueue<Long> startTime = new SynchronousQueue<>();422423Callable<?> wrappedTask = () -> {424// by the time this value reaches the "stopwatch" thread it might be425// well outdated and that's okay, we will adjust the wait time426startTime.put(System.nanoTime());427return code.call();428};429430FutureTask<?> task = new FutureTask<>(wrappedTask);431Thread t = new Thread(task);432t.start();433434final long startNanos;435try {436startNanos = startTime.take(); // (1) wait for the initial time mark437} catch (Throwable e) {438t.interrupt();439throw e;440}441442final long waitTime = millis -443NANOSECONDS.toMillis(System.nanoTime() - startNanos); // (2) adjust wait time444445try {446Object r = task.get(waitTime, MILLISECONDS); // (3) wait for the task to complete447long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);448if (elapsed < waitTime) {449throw new RuntimeException(format(450"After %s ms. (waitTime %s ms.) returned result '%s'", elapsed, waitTime, r));451}452} catch (ExecutionException e) {453long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);454if (elapsed < waitTime) {455throw new RuntimeException(format(456"After %s ms. (waitTime %s ms.) thrown exception", elapsed, waitTime), e);457}458} catch (TimeoutException expected) {459} finally {460t.interrupt();461}462}463464// ------ miscellaneous utilities ------465466private static String urlTo(TestServer server) {467String hostAddress = server.getInetAddress().getHostAddress();468String addr;469if (hostAddress.contains(":")) { // IPv6470addr = '[' + hostAddress + ']';471} else { // IPv4472addr = hostAddress;473}474return "ldap://" + addr + ":" + server.getPort();475}476477/*478* A diagnostic aid that might help with debugging timeout issues. The idea479* is to continuously measure accuracy and responsiveness of the system that480* runs this test. If the system is overwhelmed (with something else), it481* might affect the test run. At the very least we will have traces of that482* in the logs.483*484* This utility does not automatically scale up test timeouts, it simply485* gathers information.486*/487private static void startAuxiliaryDiagnosticOutput() {488System.out.printf("Starting diagnostic output (probe)%n");489Thread t = new Thread(() -> {490for (int i = 0; ; i = ((i % 20) + 1)) {491// 500, 1_000, 1_500, ..., 9_500, 10_000, 500, 1_000, ...492long expected = i * 500;493long start = System.nanoTime();494try {495MILLISECONDS.sleep(expected);496} catch (InterruptedException e) {497return;498}499long stop = System.nanoTime();500long actual = NANOSECONDS.toMillis(stop - start);501System.out.printf("(probe) expected [ms.]: %s, actual [ms.]: %s%n",502expected, actual);503504}505}, "probe");506t.setDaemon(true);507t.start();508}509}510511512