Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8237834: com/sun/jndi/ldap/LdapDnsProviderTest.java failing with LDAP response read timeout #606

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 99 additions & 16 deletions jdk/test/com/sun/jndi/ldap/LdapDnsProviderTest.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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();
Expand All @@ -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);
}
Expand Down Expand Up @@ -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<Integer> 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<Boolean> 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<Boolean> 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()) {
Expand Down