--- a/test/jdk/com/sun/jndi/ldap/DisconnectNPETest.java Tue Apr 30 12:52:23 2019 +0100
+++ b/test/jdk/com/sun/jndi/ldap/DisconnectNPETest.java Wed May 01 00:06:22 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -25,13 +25,9 @@
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
-import java.io.Closeable;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
-import java.net.Socket;
import java.util.Hashtable;
/*
@@ -41,6 +37,7 @@
* the LDAP directory server sends an (unsolicited)
* "Notice of Disconnection", make sure client handle it correctly,
* no NPE been thrown.
+ * @library lib/
* @run main/othervm DisconnectNPETest
*/
@@ -49,18 +46,27 @@
// case, we set repeat count to 1000 here.
private static final int REPEAT_COUNT = 1000;
+ // "Notice of Disconnection" message
+ private static final byte[] DISCONNECT_MSG = { 0x30, 0x4C, 0x02, 0x01, 0x00,
+ 0x78, 0x47, 0x0A, 0x01, 0x34, 0x04, 0x00, 0x04, 0x28, 0x55, 0x4E,
+ 0x41, 0x56, 0x41, 0x49, 0x4C, 0x41, 0x42, 0x4C, 0x45, 0x3A, 0x20,
+ 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+ 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x64, 0x69, 0x73, 0x63, 0x6F, 0x6E,
+ 0x6E, 0x65, 0x63, 0x74, 0x21, (byte) 0x8A, 0x16, 0x31, 0x2E, 0x33,
+ 0x2E, 0x36, 0x2E, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x31, 0x34,
+ 0x36, 0x36, 0x2E, 0x32, 0x30, 0x30, 0x33, 0x36 };
+ private static final byte[] BIND_RESPONSE = { 0x30, 0x0C, 0x02, 0x01, 0x01,
+ 0x61, 0x07, 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00 };
+
public static void main(String[] args) throws IOException {
new DisconnectNPETest().run();
}
private ServerSocket serverSocket;
private Hashtable<Object, Object> env;
- private TestLDAPServer server;
private void initRes() throws IOException {
serverSocket = new ServerSocket(0, 0, InetAddress.getLoopbackAddress());
- server = new TestLDAPServer();
- server.start();
}
private void initTest() {
@@ -80,7 +86,14 @@
initRes();
initTest();
int count = 0;
- try {
+ try (BaseLdapServer ignored = new BaseLdapServer(serverSocket)
+ .setCommonRequestHandler((msg, out) -> {
+ if (msg.getOperation()
+ == LdapMessage.Operation.BIND_REQUEST) {
+ out.write(BIND_RESPONSE);
+ out.write(DISCONNECT_MSG);
+ }
+ }).startServer()) {
while (count < REPEAT_COUNT) {
count++;
InitialDirContext context = null;
@@ -97,17 +110,9 @@
}
} finally {
System.out.println("Test count: " + count + "/" + REPEAT_COUNT);
- cleanupTest();
}
}
- private void cleanupTest() {
- if (server != null) {
- server.stopServer();
- }
- cleanupClosableRes(serverSocket);
- }
-
private void cleanupContext(DirContext context) {
if (context != null) {
try {
@@ -117,77 +122,4 @@
}
}
}
-
- private static void cleanupClosableRes(Closeable res) {
- if (res != null) {
- try {
- res.close();
- } catch (Exception e) {
- // ignore
- }
- }
- }
-
- class TestLDAPServer extends Thread {
- private volatile boolean isRunning;
-
- TestLDAPServer() {
- isRunning = true;
- }
-
- private void stopServer() {
- isRunning = false;
- }
-
- @Override
- public void run() {
- try {
- while (isRunning) {
- Socket clientSocket = serverSocket.accept();
- Thread handler = new Thread(
- new LDAPServerHandler(clientSocket));
- handler.start();
- }
- } catch (IOException e) {
- if (isRunning) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- static class LDAPServerHandler implements Runnable {
- // "Notice of Disconnection" message
- private static final byte[] DISCONNECT_MSG = { 0x30, 0x4C, 0x02, 0x01,
- 0x00, 0x78, 0x47, 0x0A, 0x01, 0x34, 0x04, 0x00, 0x04, 0x28,
- 0x55, 0x4E, 0x41, 0x56, 0x41, 0x49, 0x4C, 0x41, 0x42, 0x4C,
- 0x45, 0x3A, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72,
- 0x76, 0x65, 0x72, 0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x64,
- 0x69, 0x73, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x21,
- (byte) 0x8A, 0x16, 0x31, 0x2E, 0x33, 0x2E, 0x36, 0x2E, 0x31,
- 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x31, 0x34, 0x36, 0x36, 0x2E,
- 0x32, 0x30, 0x30, 0x33, 0x36 };
- private static final byte[] BIND_RESPONSE = { 0x30, 0x0C, 0x02, 0x01,
- 0x01, 0x61, 0x07, 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00 };
- private final Socket clientSocket;
-
- private LDAPServerHandler(final Socket clientSocket) {
- this.clientSocket = clientSocket;
- }
-
- @Override
- public void run() {
- try (clientSocket;
- OutputStream out = clientSocket.getOutputStream();
- InputStream in = clientSocket.getInputStream()) {
- if (in.read() > 0) {
- in.skip(in.available());
- out.write(BIND_RESPONSE);
- out.write(DISCONNECT_MSG);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
}
--- a/test/jdk/com/sun/jndi/ldap/LdapName/EmptyNameSearch.java Tue Apr 30 12:52:23 2019 +0100
+++ b/test/jdk/com/sun/jndi/ldap/LdapName/EmptyNameSearch.java Wed May 01 00:06:22 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -24,11 +24,10 @@
/**
* @test
* @bug 6997561
+ * @library ../lib/
* @summary A request for better error handling in JNDI
*/
-import java.net.Socket;
-import java.net.ServerSocket;
import java.io.*;
import javax.naming.*;
import javax.naming.directory.*;
@@ -46,12 +45,12 @@
Thread.sleep(3000);
// Setup JNDI parameters
- Hashtable env = new Hashtable();
+ Hashtable<String, Object> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:" + s.getPortNumber());
+ env.put(Context.PROVIDER_URL, "ldap://localhost:" + s.getPort());
- try {
+ try (s) {
// Create initial context
System.out.println("Client: connecting...");
@@ -73,9 +72,8 @@
}
}
- static class Server extends Thread {
+ static class Server extends BaseLdapServer {
- private int serverPort = 0;
private byte[] bindResponse = {
0x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A,
0x01, 0x00, 0x04, 0x00, 0x04, 0x00
@@ -85,57 +83,24 @@
0x01, 0x02, 0x04, 0x00, 0x04, 0x00
};
- Server() {
- }
-
- public int getPortNumber() {
- return serverPort;
- }
-
- public void run() {
- try {
- ServerSocket serverSock = new ServerSocket(0);
- serverPort = serverSock.getLocalPort();
- System.out.println("Server: listening on port " + serverPort);
-
- Socket socket = serverSock.accept();
- System.out.println("Server: connection accepted");
-
- InputStream in = socket.getInputStream();
- OutputStream out = socket.getOutputStream();
-
- // Read the LDAP BindRequest
- System.out.println("Server: reading request...");
- while (in.read() != -1) {
- in.skip(in.available());
- break;
+ Server() throws IOException {
+ setDebugLevel(DebugLevel.FULL);
+ setCommonRequestHandler((msg, out) -> {
+ switch (msg.getOperation()) {
+ case BIND_REQUEST:
+ // Write an LDAP BindResponse
+ debug("writing response...");
+ out.write(bindResponse);
+ break;
+ case SEARCH_REQUEST:
+ // Write an LDAP SearchResponse
+ debug("writing response...");
+ out.write(searchResponse);
+ break;
+ default:
+ break;
}
-
- // Write an LDAP BindResponse
- System.out.println("Server: writing response...");
- out.write(bindResponse);
- out.flush();
-
- // Read the LDAP SearchRequest
- System.out.println("Server: reading request...");
- while (in.read() != -1) {
- in.skip(in.available());
- break;
- }
-
- // Write an LDAP SearchResponse
- System.out.println("Server: writing response...");
- out.write(searchResponse);
- out.flush();
-
- in.close();
- out.close();
- socket.close();
- serverSock.close();
-
- } catch (IOException e) {
- // ignore
- }
+ });
}
}
}
--- a/test/jdk/com/sun/jndi/ldap/NoWaitForReplyTest.java Tue Apr 30 12:52:23 2019 +0100
+++ b/test/jdk/com/sun/jndi/ldap/NoWaitForReplyTest.java Wed May 01 00:06:22 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -24,12 +24,10 @@
/**
* @test
* @bug 6748156
+ * @library lib/
* @summary add an new JNDI property to control the boolean flag WaitForReply
*/
-import java.net.Socket;
-import java.net.ServerSocket;
-import java.io.*;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;
@@ -41,13 +39,13 @@
boolean passed = false;
// start the LDAP server
- DummyServer ldapServer = new DummyServer();
- ldapServer.start();
+ BaseLdapServer ldapServer = new BaseLdapServer()
+ .setDebugLevel(BaseLdapServer.DebugLevel.FULL).startServer();
// Set up the environment for creating the initial context
- Hashtable env = new Hashtable(11);
+ Hashtable<String, Object> env = new Hashtable<>(11);
env.put(Context.PROVIDER_URL, "ldap://localhost:" +
- ldapServer.getPortNumber());
+ ldapServer.getPort());
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
@@ -61,7 +59,7 @@
env.put("java.naming.ldap.version", "3");
- try {
+ try (ldapServer) {
// Create initial context
System.out.println("Client: connecting to the server");
@@ -70,7 +68,7 @@
SearchControls scl = new SearchControls();
scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
System.out.println("Client: performing search");
- NamingEnumeration answer =
+ NamingEnumeration<?> answer =
ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl);
// Server will never reply: either we waited in the call above until
@@ -84,7 +82,6 @@
} catch (NamingException e) {
// timeout (ignore)
}
- ldapServer.interrupt();
if (!passed) {
throw new Exception(
@@ -92,40 +89,4 @@
}
System.out.println("Test PASSED");
}
-
- static class DummyServer extends Thread {
-
- private final ServerSocket serverSocket;
-
- DummyServer() throws IOException {
- this.serverSocket = new ServerSocket(0);
- System.out.println("Server: listening on port " + serverSocket.getLocalPort());
- }
-
- public int getPortNumber() {
- return serverSocket.getLocalPort();
- }
-
- public void run() {
- try (Socket socket = serverSocket.accept()) {
- System.out.println("Server: accepted a connection");
- InputStream in = socket.getInputStream();
-
- while (!isInterrupted()) {
- in.skip(in.available());
- }
-
- } catch (Exception e) {
- // ignore
-
- } finally {
- System.out.println("Server: shutting down");
- try {
- serverSocket.close();
- } catch (IOException e) {
- // ignore
- }
- }
- }
- }
}
--- a/test/jdk/com/sun/jndi/ldap/RemoveNamingListenerTest.java Tue Apr 30 12:52:23 2019 +0100
+++ b/test/jdk/com/sun/jndi/ldap/RemoveNamingListenerTest.java Wed May 01 00:06:22 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -20,13 +20,10 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-import java.io.BufferedInputStream;
+
import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
+import java.net.InetAddress;
import java.net.ServerSocket;
-import java.net.Socket;
-import java.nio.charset.StandardCharsets;
import java.util.ConcurrentModificationException;
import java.util.Hashtable;
import javax.naming.Context;
@@ -44,6 +41,7 @@
* @summary Incorrect usage of Iterator in Java 8 In com.sun.jndi.ldap.
* EventSupport.removeNamingListener
* @modules java.naming
+ * @library lib/
* @run main RemoveNamingListenerTest
*/
public class RemoveNamingListenerTest {
@@ -130,112 +128,26 @@
}
}
-class TestLDAPServer extends Thread {
-
- private final int LDAP_PORT;
- private final ServerSocket serverSocket;
- private volatile boolean isRunning;
+class TestLDAPServer extends BaseLdapServer {
- TestLDAPServer() throws IOException {
- serverSocket = new ServerSocket(0);
- isRunning = true;
- LDAP_PORT = serverSocket.getLocalPort();
- setDaemon(true);
- }
-
- public int getPort() {
- return LDAP_PORT;
- }
+ private byte[] bindResponse = {0x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00};
+ private byte[] searchResponse = {0x30, 0x0C, 0x02, 0x01, 0x02, 0x65, 0x07, 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00};
- public void stopServer() {
- isRunning = false;
- if (serverSocket != null && !serverSocket.isClosed()) {
- try {
- // this will cause ServerSocket.accept() to throw SocketException.
- serverSocket.close();
- } catch (IOException ignored) {
+ public TestLDAPServer() throws IOException {
+ super(new ServerSocket(0, 0, InetAddress.getLoopbackAddress()), true);
+ setCommonRequestHandler((msg, out) -> {
+ switch (msg.getOperation()) {
+ case BIND_REQUEST:
+ // Write an LDAP BindResponse
+ out.write(bindResponse);
+ break;
+ case SEARCH_REQUEST:
+ // Write an LDAP SearchResponse
+ out.write(searchResponse);
+ break;
+ default:
+ break;
}
- }
- }
-
- @Override
- public void run() {
- try {
- while (isRunning) {
- Socket clientSocket = serverSocket.accept();
- Thread handler = new Thread(new LDAPServerHandler(clientSocket));
- handler.setDaemon(true);
- handler.start();
- }
- } catch (IOException iOException) {
- //do not throw exception if server is not running.
- if (isRunning) {
- throw new RuntimeException(iOException);
- }
- } finally {
- stopServer();
- }
+ });
}
}
-
-class LDAPServerHandler implements Runnable {
-
- private final Socket clientSocket;
-
- public LDAPServerHandler(final Socket clientSocket) {
- this.clientSocket = clientSocket;
- }
-
- @Override
- public void run() {
- BufferedInputStream in = null;
- PrintWriter out = null;
- byte[] bindResponse = {0x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00};
- byte[] searchResponse = {0x30, 0x0C, 0x02, 0x01, 0x02, 0x65, 0x07, 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00};
- try {
- in = new BufferedInputStream(clientSocket.getInputStream());
- out = new PrintWriter(new OutputStreamWriter(
- clientSocket.getOutputStream(), StandardCharsets.UTF_8), true);
- while (true) {
-
- // Read the LDAP BindRequest
- while (in.read() != -1) {
- in.skip(in.available());
- break;
- }
-
- // Write an LDAP BindResponse
- out.write(new String(bindResponse));
- out.flush();
-
- // Read the LDAP SearchRequest
- while (in.read() != -1) {
- in.skip(in.available());
- break;
- }
-
- // Write an LDAP SearchResponse
- out.write(new String(searchResponse));
- out.flush();
- }
- } catch (IOException iOException) {
- throw new RuntimeException(iOException);
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException ignored) {
- }
- }
- if (out != null) {
- out.close();
- }
- if (clientSocket != null) {
- try {
- clientSocket.close();
- } catch (IOException ignored) {
- }
- }
- }
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/lib/BaseLdapServer.java Wed May 01 00:06:22 2019 -0700
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
+
+/**
+ * A base dummy ldap server.
+ *
+ * For any ldap tests which required a simple dummy server to support the
+ * test, may use this server directly with specifying ConnectionHandler,
+ * SessionHandler/RequestHandler, or extends to build more complex server logic.
+ *
+ * This server already extends Thread and implements AutoCloseable, so it can
+ * be started in thread and integrated with try-with-resources
+ *
+ * To initiate a instance of this server, valid ServerSocket could be supplied,
+ * it will allow the flexibility for listening address/port customization
+ * and SSL usage, for default no parameter constructor, a ServerSocket which
+ * listen on loopback address will be created.
+ *
+ * To use this dummy server in test, user could customize the processing logic
+ * in three level with below handler interface.
+ * -ConnectionHandler provide connection level handling, server will hand
+ * over accepted socket and processing thread to handler.
+ * By default, DefaultConnectionHandler will be used if no
+ * specified, it reads full ldap request message then
+ * pass it to RequestHandler instance which returned by
+ * SessionHandler per session.
+ *
+ * -SessionHandler provide session level handling when DefaultConnectionHandler
+ * been used, it's to retrieve RequestHandler instance of
+ * current session.
+ * For most of tests, only one session need to be handled
+ * on server or all ldap request could be handled by same
+ * logic whatever current session is, user can use
+ * setCommonRequestHandler to setup one single session
+ * handler which will always return given RequestHandler
+ * instance.
+ *
+ * -RequestHandler provide ldap message request handling when
+ * DefaultConnectionHandler been used.
+ *
+ * @see ConnectionHandler
+ * @see SessionHandler
+ * @see RequestHandler
+ */
+public class BaseLdapServer extends Thread implements AutoCloseable {
+ private volatile boolean isRunning;
+ private final List<Socket> socketList = new ArrayList<>();
+ private ServerSocket serverSocket;
+ private ExecutorService workingPool;
+ private ConnectionHandler connectionHandler;
+ private SessionHandler sessionHandler;
+ private boolean useDaemonThread = false;
+
+ enum DebugLevel {
+ FULL, // all debug message will be printed
+ NONE, // none of debug message will be printed
+ CUSTOMIZE // only specified class debug message will be printed
+ }
+
+ private StackWalker stackWalker = null;
+ private DebugLevel debugLevel = DebugLevel.NONE;
+ private Set<Class<?>> debugOptions = new HashSet<>();
+
+ /**
+ * BaseLdapServer overload default constructor.
+ *
+ * @throws IOException if an I/O error occurs when opening the socket.
+ */
+ public BaseLdapServer() throws IOException {
+ this(new ServerSocket(0, 0, InetAddress.getLoopbackAddress()));
+ }
+
+ /**
+ * BaseLdapServer overload constructor with given server socket.
+ *
+ * @param serverSocket given server socket
+ */
+ public BaseLdapServer(ServerSocket serverSocket) {
+ this(serverSocket, false);
+ }
+
+ /**
+ * BaseLdapServer constructor with given server socket and specify whether
+ * use daemon for each accept connection handling thread.
+ *
+ * @param serverSocket given server socket
+ * @param useDaemonThread <tt>true</tt> if use daemon thread
+ */
+ public BaseLdapServer(ServerSocket serverSocket, boolean useDaemonThread) {
+ this.serverSocket = Objects.requireNonNull(serverSocket);
+ this.useDaemonThread = useDaemonThread;
+ if (useDaemonThread) {
+ workingPool = Executors
+ .newCachedThreadPool(new DefaultDaemonThreadFactory());
+ } else {
+ workingPool = Executors.newCachedThreadPool();
+ }
+ try {
+ stackWalker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
+ } catch (SecurityException se) {
+ // just ignore
+ }
+ }
+
+ @Override
+ public void run() {
+ if (getConnectionHandler() == null) {
+ debug("INFO: No connection handler been specified, try default.");
+ connectionHandler = new DefaultConnectionHandler();
+ }
+ debug("INFO: Using connection handler : " + getConnectionHandler()
+ .getClass().getName());
+ debug("INFO: LdapServer running and listening on port " + getPort());
+ try {
+ while (isRunning) {
+ Socket socket = serverSocket.accept();
+ debug("INFO: Accept new connection " + socket);
+ synchronized (socketList) {
+ socketList.add(socket);
+ }
+ workingPool.submit(() -> getConnectionHandler()
+ .handleConnection(socket));
+ }
+ } catch (IOException | RejectedExecutionException e) {
+ if (isRunning) {
+ throw new RuntimeException(e);
+ } else {
+ debug("INFO: Server exit.");
+ }
+ }
+ }
+
+ /*
+ * Override Thread.start()
+ */
+ @Override
+ public synchronized void start() {
+ super.start();
+ isRunning = true;
+ }
+
+ /**
+ * Start Server thread and return itself for method chaining
+ *
+ * @return current server instance
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends BaseLdapServer> T startServer() {
+ start();
+ return (T) this;
+ }
+
+ /**
+ * Stop server.
+ */
+ public void stopServer() {
+ debug("INFO: Stopping Server.");
+ isRunning = false;
+ workingPool.shutdown();
+ cleanupClosableRes(serverSocket);
+ if (!useDaemonThread) {
+ // let's cleanup thread pool
+ synchronized (socketList) {
+ socketList.forEach(BaseLdapServer::cleanupClosableRes);
+ }
+ try {
+ if (!workingPool.awaitTermination(10, TimeUnit.SECONDS)) {
+ workingPool.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ workingPool.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * Return local port which server is listening.
+ *
+ * @return port which server is listening
+ */
+ public int getPort() {
+ if (serverSocket != null) {
+ return serverSocket.getLocalPort();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Return flag to indicate whether current server is running.
+ *
+ * @return <tt>true</tt> if current server is running.
+ */
+ public boolean isRunning() {
+ return isRunning;
+ }
+
+ /**
+ * Return ConnectionHandler instance
+ *
+ * @return ConnectionHandler instance
+ * @see ConnectionHandler
+ */
+ ConnectionHandler getConnectionHandler() {
+ return connectionHandler;
+ }
+
+ /**
+ * Set ConnectionHandler when server is not running.
+ *
+ * @param connHandler ConnectionHandler instance
+ * @return current server instance for method chaining
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends BaseLdapServer> T setConnectionHandler(
+ ConnectionHandler connHandler) {
+ if (!isRunning) {
+ connectionHandler = connHandler;
+ }
+
+ return (T) this;
+ }
+
+ /**
+ * Return SessionHandler instance
+ *
+ * @return SessionHandler instance
+ * @see SessionHandler
+ */
+ SessionHandler getSessionHandler() {
+ return sessionHandler;
+ }
+
+ /**
+ * Set SessionHandler when server is not running.
+ *
+ * @param sessionHandler given SessionHandler
+ * @return current server instance for method chaining
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends BaseLdapServer> T setSessionHandler(
+ SessionHandler sessionHandler) {
+ if (!isRunning) {
+ this.sessionHandler = sessionHandler;
+ }
+
+ return (T) this;
+ }
+
+ /**
+ * Set one common RequestHandler, it will be used to handle all requests
+ * whatever current session is.
+ *
+ * For most of tests, server only need to handle one session, use this
+ * method will create stateless session handler with given request handler.
+ *
+ * @param requestHandler RequestHandler instance
+ * @return current server instance for method chaining
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends BaseLdapServer> T setCommonRequestHandler(
+ RequestHandler requestHandler) {
+ if (!isRunning) {
+ // ignore any session, always return fixed request handler
+ setSessionHandler(socket -> requestHandler);
+ }
+
+ return (T) this;
+ }
+
+ @Override
+ public void close() {
+ stopServer();
+ }
+
+ /**
+ * Cleanup any given closable resource
+ *
+ * @param res given closable resource
+ */
+ static void cleanupClosableRes(Closeable res) {
+ if (res != null) {
+ try {
+ res.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Set debug level to specify which kinds of debug message will be printed.
+ *
+ * @param debugLevel given debug level
+ * @param opts given opts if debug level is DebugLevel.CUSTOMIZE
+ * @return current server instance for method chaining
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends BaseLdapServer> T setDebugLevel(DebugLevel debugLevel,
+ Class<?>... opts) {
+ Objects.requireNonNull(debugLevel);
+ if (!isRunning) {
+ this.debugLevel = debugLevel;
+ if (debugLevel == DebugLevel.CUSTOMIZE) {
+ debugOptions.clear();
+ Stream.of(opts).filter(Objects::nonNull)
+ .forEach(debugOptions::add);
+ }
+ }
+
+ return (T) this;
+ }
+
+ /**
+ * Print given message if debug enabled.
+ *
+ * @param message given message to print
+ */
+ void debug(String message) {
+ switch (debugLevel) {
+ case FULL:
+ System.out.println((stackWalker != null ?
+ stackWalker.getCallerClass().getName() :
+ "") + ": " + message);
+ break;
+ case CUSTOMIZE:
+ if (stackWalker != null) {
+ if (debugOptions.contains(stackWalker.getCallerClass())) {
+ System.out.println(
+ stackWalker.getCallerClass().getName() + ": "
+ + message);
+ }
+ }
+ break;
+ case NONE:
+ default:
+ break;
+ }
+ }
+
+ class DefaultDaemonThreadFactory implements ThreadFactory {
+
+ private ThreadFactory defaultThreadFactory = Executors
+ .defaultThreadFactory();
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = defaultThreadFactory.newThread(r);
+ thread.setDaemon(true);
+ return thread;
+ }
+ }
+
+ /**
+ * Default connection handler implementation.
+ */
+ class DefaultConnectionHandler implements ConnectionHandler {
+ @Override
+ public void handleConnection(Socket socket) {
+ try (socket;
+ OutputStream out = socket.getOutputStream();
+ InputStream in = socket.getInputStream()) {
+ byte[] inBuffer = new byte[1024];
+ int count;
+ byte[] request;
+
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int msgLen = -1;
+
+ while ((count = in.read(inBuffer)) > 0) {
+ buffer.write(inBuffer, 0, count);
+ if (msgLen <= 0) {
+ msgLen = getMessageLength(buffer.toByteArray());
+ }
+
+ if (msgLen > 0 && buffer.size() >= msgLen) {
+ if (buffer.size() > msgLen) {
+ byte[] tmpBuffer = buffer.toByteArray();
+ request = Arrays.copyOf(tmpBuffer, msgLen);
+ buffer.reset();
+ buffer.write(tmpBuffer, msgLen,
+ tmpBuffer.length - msgLen);
+ } else {
+ request = buffer.toByteArray();
+ buffer.reset();
+ }
+ msgLen = -1;
+ } else {
+ debug("INFO: request msg not complete, received "
+ + buffer.size() + ", expected " + msgLen);
+ continue;
+ }
+
+ if (getSessionHandler() != null) {
+ var handler = getSessionHandler()
+ .getRequestHandler(socket);
+ if (handler != null) {
+ debug("INFO: Process request. Session handler : "
+ + getSessionHandler()
+ + ", Request handler : " + handler);
+ handler.handleRequest(new LdapMessage(request),
+ out);
+ } else {
+ debug("WARNING: no valid request handler returned from "
+ + getSessionHandler() + ", " + socket);
+ }
+ } else {
+ debug("WARNING: no valid session handler been specified, discard request.");
+ }
+ }
+ debug("INFO: Connection Handler exit.");
+ } catch (IOException e) {
+ if (!isRunning()) {
+ debug("INFO: Connection Handler exit : " + e.getMessage());
+ } else {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private int getMessageLength(byte[] encoding) {
+ if (encoding.length < 2) {
+ // no enough data to extract msg len, just return -1
+ return -1;
+ }
+
+ if (encoding[0] != 0x30) {
+ throw new RuntimeException("Error: bad LDAP encoding message: "
+ + "expected ASN.1 SEQUENCE tag (0x30), encountered "
+ + encoding[0]);
+ }
+
+ int len;
+ int index = 1;
+ int payloadLen = 0;
+
+ if ((encoding[1] & 0x80) == 0x80) {
+ len = (encoding[1] & 0x0F);
+ index++;
+ } else {
+ len = 1;
+ }
+
+ if (len > 4) {
+ throw new RuntimeException(
+ "Error: LDAP encoding message payload too large");
+ }
+
+ if (encoding.length < index + len) {
+ // additional data required to extract payload len, return -1
+ return -1;
+ }
+
+ for (byte b : Arrays.copyOfRange(encoding, index, index + len)) {
+ payloadLen = payloadLen << 8 | (b & 0xFF);
+ }
+
+ if (payloadLen <= 0) {
+ throw new RuntimeException(
+ "Error: invalid LDAP encoding message length or payload too large");
+ }
+
+ return index + len + payloadLen;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/lib/ConnectionHandler.java Wed May 01 00:06:22 2019 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.net.Socket;
+
+/**
+ * Interface for Connection Handler.
+ */
+public interface ConnectionHandler {
+ /**
+ * Handle given socket connection.
+ *
+ * @param socket given socket connection
+ */
+ void handleConnection(Socket socket);
+}
--- a/test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java Tue Apr 30 12:52:23 2019 +0100
+++ b/test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java Wed May 01 00:06:22 2019 -0700
@@ -259,14 +259,7 @@
+ "cache file " + fileName);
}
- Thread thread = new Thread(() -> {
- try {
- new test.LDAPServer(serverSocket, fileName);
- } catch (Exception e) {
- System.out.println("Warning: LDAP server running with issue");
- e.printStackTrace();
- }
- });
+ Thread thread = new LdapPlaybackServer(serverSocket, fileName);
thread.start();
return thread;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/lib/LdapMessage.java Wed May 01 00:06:22 2019 -0700
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * Class to present one Ldap message.
+ */
+public class LdapMessage {
+ private final byte[] messages;
+ private int messageID;
+ private Operation operation;
+
+ public enum Operation {
+ BIND_REQUEST(0x60, "BindRequest"), // [APPLICATION 0]
+ BIND_RESPONSE(0x61, "BindResponse"), // [APPLICATION 1]
+ UNBIND_REQUEST(0x42, "UnbindRequest"), // [APPLICATION 2]
+ SEARCH_REQUEST(0x63, "SearchRequest"), // [APPLICATION 3]
+ SEARCH_RESULT_ENTRY(0x64, "SearchResultEntry"), // [APPLICATION 4]
+ SEARCH_RESULT_DONE(0x65, "SearchResultDone"), // [APPLICATION 5]
+ MODIFY_REQUEST(0x66, "ModifyRequest"), // [APPLICATION 6]
+ MODIFY_RESPONSE(0x67, "ModifyResponse"), // [APPLICATION 7]
+ ADD_REQUEST(0x68, "AddRequest"), // [APPLICATION 8]
+ ADD_RESPONSE(0x69, "AddResponse"), // [APPLICATION 9]
+ DELETE_REQUEST(0x4A, "DeleteRequest"), // [APPLICATION 10]
+ DELETE_RESPONSE(0x6B, "DeleteResponse"), // [APPLICATION 11]
+ MODIFY_DN_REQUEST(0x6C, "ModifyDNRequest"), // [APPLICATION 12]
+ MODIFY_DN_RESPONSE(0x6D, "ModifyDNResponse"), // [APPLICATION 13]
+ COMPARE_REQUEST(0x6E, "CompareRequest"), // [APPLICATION 14]
+ COMPARE_RESPONSE(0x6F, "CompareResponse"), // [APPLICATION 15]
+ ABANDON_REQUEST(0x50, "AbandonRequest"), // [APPLICATION 16]
+ SEARCH_RESULT_REFERENCE(0x73,
+ "SearchResultReference"), // [APPLICATION 19]
+ EXTENDED_REQUEST(0x77, "ExtendedRequest"), // [APPLICATION 23]
+ EXTENDED_RESPONSE(0x78, "ExtendedResponse"), // [APPLICATION 24]
+ INTERMEDIATE_RESPONSE(0x79, "IntermediateResponse"); // [APPLICATION 25]
+
+ private final int id;
+ private final String name;
+
+ Operation(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public static Operation fromId(int id) {
+ Optional<Operation> optional = Stream.of(Operation.values())
+ .filter(o -> o.id == id).findFirst();
+ if (optional.isPresent()) {
+ return optional.get();
+ } else {
+ throw new RuntimeException(
+ "Unknown id " + id + " for enum Operation.");
+ }
+ }
+ }
+
+ public LdapMessage(byte[] messages) {
+ this.messages = messages;
+ parse();
+ }
+
+ public LdapMessage(String hexString) {
+ this(parseHexBinary(hexString));
+ }
+
+ // Extracts the message ID and operation ID from an LDAP protocol encoding
+ private void parse() {
+ if (messages == null || messages.length < 2) {
+ throw new RuntimeException(
+ "Invalid ldap messages: " + Arrays.toString(messages));
+ }
+
+ if (messages[0] != 0x30) {
+ throw new RuntimeException("Bad LDAP encoding in messages, "
+ + "expected ASN.1 SEQUENCE tag (0x30), encountered "
+ + messages[0]);
+ }
+
+ int index = 2;
+ if ((messages[1] & 0x80) == 0x80) {
+ index += (messages[1] & 0x0F);
+ }
+
+ if (messages[index] != 0x02) {
+ throw new RuntimeException("Bad LDAP encoding in messages, "
+ + "expected ASN.1 INTEGER tag (0x02), encountered "
+ + messages[index]);
+ }
+ int length = messages[index + 1];
+ index += 2;
+ messageID = new BigInteger(1,
+ Arrays.copyOfRange(messages, index, index + length)).intValue();
+ index += length;
+ int operationID = messages[index];
+ operation = Operation.fromId(operationID);
+ }
+
+ /**
+ * Return original ldap message in byte array.
+ *
+ * @return original ldap message
+ */
+ public byte[] getMessages() {
+ return Arrays.copyOf(messages, messages.length);
+ }
+
+ /**
+ * Return ldap message id.
+ *
+ * @return ldap message id.
+ */
+ public int getMessageID() {
+ return messageID;
+ }
+
+ /**
+ * Return ldap message's operation.
+ *
+ * @return ldap message's operation.
+ */
+ public Operation getOperation() {
+ return operation;
+ }
+
+ private static byte[] parseHexBinary(String s) {
+
+ final int len = s.length();
+
+ // "111" is not a valid hex encoding.
+ if (len % 2 != 0) {
+ throw new IllegalArgumentException(
+ "hexBinary needs to be even-length: " + s);
+ }
+
+ byte[] out = new byte[len / 2];
+
+ for (int i = 0; i < len; i += 2) {
+ int h = hexToBin(s.charAt(i));
+ int l = hexToBin(s.charAt(i + 1));
+ if (h == -1 || l == -1) {
+ throw new IllegalArgumentException(
+ "contains illegal character for hexBinary: " + s);
+ }
+
+ out[i / 2] = (byte) (h * 16 + l);
+ }
+
+ return out;
+ }
+
+ private static int hexToBin(char ch) {
+ if ('0' <= ch && ch <= '9') {
+ return ch - '0';
+ }
+ if ('A' <= ch && ch <= 'F') {
+ return ch - 'A' + 10;
+ }
+ if ('a' <= ch && ch <= 'f') {
+ return ch - 'a' + 10;
+ }
+ return -1;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/lib/LdapPlaybackServer.java Wed May 01 00:06:22 2019 -0700
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.regex.MatchResult;
+
+/*
+ * An enhanced dummy LDAP server which can playback captured LDAP messages.
+ *
+ * Loads a sequence of LDAP messages from a capture file into its cache.
+ * It listens for LDAP requests, finds a match in its cache and sends the
+ * corresponding LDAP responses.
+ *
+ * The capture file contains an LDAP protocol exchange in the hexadecimal
+ * dump format emitted by sun.misc.HexDumpEncoder:
+ *
+ * xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................
+ *
+ * Typically, LDAP protocol exchange is generated by running the LDAP client
+ * application program against a real LDAP server and setting the JNDI/LDAP
+ * environment property: com.sun.jndi.ldap.trace.ber to activate LDAP message
+ * tracing.
+ */
+public class LdapPlaybackServer extends BaseLdapServer {
+
+ /*
+ * A cache of LDAP requests and responses.
+ * Messages with the same ID are stored in a list.
+ * The first element in the list is the LDAP request,
+ * the remaining elements are the LDAP responses.
+ */
+ private final Map<Integer, List<byte[]>> cache = new HashMap<>();
+
+ private String fileName;
+
+ public LdapPlaybackServer(ServerSocket serverSocket, String fileName) {
+ super(serverSocket);
+ this.fileName = fileName;
+ setDebugLevel(DebugLevel.CUSTOMIZE, this.getClass());
+ setCommonRequestHandler(this::handleRequest);
+ }
+
+ /*
+ * Load a capture file containing an LDAP protocol exchange in the
+ * hexadecimal dump format emitted by sun.misc.HexDumpEncoder:
+ *
+ * xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................
+ */
+ private void loadCaptureFile(String filename) throws IOException {
+ StringBuilder hexString = new StringBuilder();
+ String pattern = "(....): (..) (..) (..) (..) (..) (..) (..) (..) (..) (..) (..) (..) (..) (..) (..) (..).*";
+ String preLineNum = "";
+
+ try (Scanner fileScanner = new Scanner(Paths.get(filename))) {
+ while (fileScanner.hasNextLine()) {
+
+ try (Scanner lineScanner = new Scanner(
+ fileScanner.nextLine())) {
+ if (lineScanner.findInLine(pattern) == null) {
+ preLineNum = "";
+ continue;
+ }
+ MatchResult result = lineScanner.match();
+ for (int i = 1; i <= result.groupCount(); i++) {
+ String digits = result.group(i);
+ if (digits.length() == 4) {
+ if (digits.equals("0000") && !preLineNum
+ .equalsIgnoreCase(
+ "FFF0")) { // start-of-message
+ if (hexString.length() > 0) {
+ addToCache(hexString.toString());
+ hexString = new StringBuilder();
+ }
+ }
+ preLineNum = digits;
+ continue;
+ } else if (digits.equals(" ")) { // short message
+ continue;
+ }
+ hexString.append(digits);
+ }
+ }
+ }
+ }
+ if (!hexString.toString().isEmpty()) {
+ addToCache(hexString.toString());
+ }
+ }
+
+ /*
+ * Add an LDAP encoding to the cache (by messageID key).
+ */
+ private void addToCache(String hexString) {
+ LdapMessage message = new LdapMessage(hexString);
+ byte[] encoding = message.getMessages();
+ int messageID = message.getMessageID();
+ List<byte[]> encodings = cache.get(messageID);
+ if (encodings == null) {
+ encodings = new ArrayList<>();
+ }
+ debug(" adding LDAP " + message.getOperation() + " with message ID "
+ + messageID + " to the cache");
+ encodings.add(encoding);
+ cache.put(messageID, encodings);
+ }
+
+ @Override
+ public void stopServer() {
+ debug("force stopping server");
+ super.stopServer();
+ }
+
+ private void handleRequest(LdapMessage request, OutputStream out)
+ throws IOException {
+ int messageID = request.getMessageID();
+ debug("received LDAP " + request.getOperation() + " [message ID "
+ + messageID + "]");
+
+ List<byte[]> encodings = cache.get(messageID);
+ if (encodings == null || (!Arrays
+ .equals(request.getMessages(), encodings.get(0)))) {
+ throw new RuntimeException(
+ "LDAPServer: ERROR: received an LDAP " + request
+ .getOperation() + " (ID=" + messageID
+ + ") not present in cache");
+ }
+
+ for (int i = 1; i < encodings.size(); i++) {
+ // skip the request (at index 0)
+ byte[] response = encodings.get(i);
+ out.write(response, 0, response.length);
+ LdapMessage responseMsg = new LdapMessage(response);
+ debug("Sent LDAP " + responseMsg.getOperation() + " [message ID "
+ + responseMsg.getMessageID() + "]");
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ debug("Loading LDAP cache from: " + fileName);
+ loadCaptureFile(fileName);
+ debug("listening on port " + getPort());
+
+ super.run();
+ } catch (Exception e) {
+ debug("ERROR: " + e);
+ e.printStackTrace();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/lib/RequestHandler.java Wed May 01 00:06:22 2019 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Interface for ldap request handler.
+ */
+public interface RequestHandler {
+ /**
+ * Handle given ldap request message.
+ *
+ * @param request given ldap request message
+ * @param out given OutputStream which could be used to write response
+ * @throws IOException if an I/O error occurs when using OutputStream
+ */
+ void handleRequest(LdapMessage request, OutputStream out)
+ throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/lib/SessionHandler.java Wed May 01 00:06:22 2019 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.net.Socket;
+
+/**
+ * Interface for ldap session handler.
+ */
+public interface SessionHandler {
+ /**
+ * Return request handler instance per session.
+ *
+ * @param socket given socket to identify current session
+ * @return request handler instance per session
+ */
+ RequestHandler getRequestHandler(Socket socket);
+}