diff --git a/jdk/test/com/sun/jndi/ldap/LdapDnsProviderTest.java b/jdk/test/com/sun/jndi/ldap/LdapDnsProviderTest.java index 1bc4a30027..2e60f354f5 100644 --- a/jdk/test/com/sun/jndi/ldap/LdapDnsProviderTest.java +++ b/jdk/test/com/sun/jndi/ldap/LdapDnsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,12 @@ import java.io.FileOutputStream; import java.io.IOException; import java.security.Permission; +import java.util.HashSet; import java.util.Hashtable; +import java.util.Random; +import java.util.Set; import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import javax.naming.Context; @@ -37,10 +41,23 @@ import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; +import sun.net.PortConfig; + +import jdk.test.lib.RandomFactory; + /** * @test * @bug 8160768 - * @summary ctx provider tests for ldap + * @key randomness intermittent + * @summary ctx provider tests for ldap. + * Two test cases need to establish connection to the + * unreachable port on localhost. Each tries 5 connection + * attempts with a random port expecting for connection to fail. + * In rare cases it could establish connections due to services + * running on these ports, therefore it can fail intermittently. + * @modules java.naming/com.sun.jndi.ldap java.base/sun.net + * @library /test/lib + * @build jdk.test.lib.RandomFactory * @compile dnsprovider/TestDnsProvider.java * @run main/othervm LdapDnsProviderTest * @run main/othervm LdapDnsProviderTest nosm @@ -51,16 +68,6 @@ class DNSSecurityManager extends SecurityManager { - - - /* run main/othervm LdapDnsProviderTest - - * run main/othervm LdapDnsProviderTest nosm - * run main/othervm LdapDnsProviderTest smnodns - * run main/othervm LdapDnsProviderTest smdns - * run main/othervm LdapDnsProviderTest nosmbaddns - */ - private boolean dnsProvider = false; public void setAllowDnsProvider(boolean allow) { @@ -104,6 +111,13 @@ public Boolean call() { env.put(Context.PROVIDER_URL, url); } + // Set JNDI LDAP connect timeout property. It helps to prevent + // initial bind operation from blocking in case of a local process + // listening on the port specified in the URL. With the property set, + // the bind operation will fail with timeout exception, and then it + // could be retried with another port number. + env.put("com.sun.jndi.ldap.connect.timeout", "1000"); + try { ctx = new InitialDirContext(env); SearchControls scl = new SearchControls(); @@ -112,8 +126,13 @@ public Boolean call() { "ou=People,o=Test", "(objectClass=*)", scl); throw new RuntimeException("Search should not complete"); } catch (NamingException e) { - e.printStackTrace(); passed = e.toString().contains(expected); + System.err.println((passed ? "Expected" : "Unexpected") + + " NamingException observed: " + e.toString()); + // Print stack trace only for unexpected exceptions + if (!passed) { + e.printStackTrace(); + } } finally { shutItDown(ctx); } @@ -193,21 +212,85 @@ public static void main(String[] args) throws Exception { // no SecurityManager runTest("ldap:///dc=example,dc=com", "localhost:389"); runTest("ldap://localhost/dc=example,dc=com", "localhost:389"); - runTest("ldap://localhost:111/dc=example,dc=com", "localhost:111"); - runTest("ldaps://localhost:111/dc=example,dc=com", "localhost:111"); + runLocalHostTestWithRandomPort("ldap", "/dc=example,dc=com", 5); + runLocalHostTestWithRandomPort("ldaps", "/dc=example,dc=com", 5); runTest("ldaps://localhost/dc=example,dc=com", "localhost:636"); runTest(null, "localhost:389"); runTest("", "ConfigurationException"); } } + // Pseudorandom number generator + private static final Random RND = RandomFactory.getRandom(); + // Port numbers already seen to be generated by pseudorandom generator + private static final Set SEEN_PORTS = new HashSet<>(); + + // Get random, previously unseen port number from [1111, PortConfig.getUpper()) range + private static int generateUnseenPort() { + int port; + do { + port = 1111 + RND.nextInt(PortConfig.getUpper() - 1111); + // Seen ports will never contain more than maxAttempts*2 ports + } while (SEEN_PORTS.contains(port)); + SEEN_PORTS.add(port); + return port; + } + + // Run test with ldap connection to localhost and random port. The test is expected to fail + // with CommunicationException that is caused by connection refuse exception. + // But in case if there is a service running on the same port the connection + // will be established and then closed or timed-out. Both cases will generate exception + // messages which differ from the expected one. + // For such cases the test will be repeated with another random port. That will be done + // maxAttempts times. If the expected exception won't be observed - test will be treated + // as failed. + private static void runLocalHostTestWithRandomPort(String scheme, String path, int maxAttempts) { + for (int attempt = 0; attempt <= maxAttempts; attempt++) { + boolean attemptSuccessful = true; + int port = generateUnseenPort(); + + // Construct URL for the current attempt + String url = scheme + "://localhost" + ":" + port + path; + + // Construct text expected to be present in Exception message + String expected = "localhost:" + port; + + System.err.printf("Iteration %d: Testing: %s, %s%n", attempt, url, expected); + + FutureTask future = new FutureTask<>( + new ProviderTest(url, expected)); + new Thread(future).start(); + while (!future.isDone()) { + try { + if (!future.get()) { + if (attempt == maxAttempts) { + throw new RuntimeException("Test failed, ProviderTest" + + " returned false " + maxAttempts + " times"); + } else { + System.err.printf("Iteration %d failed:" + + " ProviderTest returned false%n", attempt); + attemptSuccessful = false; + } + } + } catch (InterruptedException | ExecutionException e) { + System.err.println("Iteration %d failed to execute provider test: " + e.getMessage()); + attemptSuccessful = false; + } + } + if (attemptSuccessful) { + System.err.println("Test passed. It took " + (attempt + 1) + " iterations to complete"); + break; + } + } + } + private static void runTest(String url, String expected) { FutureTask future = new FutureTask<>( new ProviderTest(url, expected)); new Thread(future).start(); - System.err.println("Testing: " + url + ", " + expected); + System.err.printf("Testing: url='%s', expected content='%s'%n", url, expected); while (!future.isDone()) { try { if (!future.get()) {