# HG changeset patch # User xyin # Date 1556694382 25200 # Node ID ff884a2f247be95cf1b2916dbae3b5f0acdb29de # Parent 9a11a7e1c035b745779409a832e5da2d106c339b JDK-8210696-branch: push initial fix change diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/DisconnectNPETest.java --- 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 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(); - } - } - } } diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/LdapName/EmptyNameSearch.java --- 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 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 - } + }); } } } diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/NoWaitForReplyTest.java --- 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 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 - } - } - } - } } diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/RemoveNamingListenerTest.java --- 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) { - } - } - } - } -} diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/lib/BaseLdapServer.java --- /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 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> 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 true 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 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 true 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 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 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 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 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; + } + } +} diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/lib/ConnectionHandler.java --- /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); +} diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java --- 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; diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/lib/LdapMessage.java --- /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 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; + } +} diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/lib/LdapPlaybackServer.java --- /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> 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 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 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(); + } + } +} diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/lib/RequestHandler.java --- /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; +} diff -r 9a11a7e1c035 -r ff884a2f247b test/jdk/com/sun/jndi/ldap/lib/SessionHandler.java --- /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); +}