< prev index next >

test/jdk/com/sun/jndi/ldap/lib/BaseLdapServer.java

Print this page




  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.ByteArrayOutputStream;
  25 import java.io.Closeable;
  26 import java.io.IOException;
  27 import java.io.InputStream;
  28 import java.io.OutputStream;
  29 import java.net.InetAddress;
  30 import java.net.ServerSocket;
  31 import java.net.Socket;
  32 import java.util.ArrayList;
  33 import java.util.Arrays;
  34 import java.util.List;
  35 import java.util.Objects;
  36 import java.util.concurrent.ExecutorService;
  37 import java.util.concurrent.Executors;
  38 import java.util.concurrent.RejectedExecutionException;
  39 
  40 import static java.lang.System.Logger.Level.INFO;
  41 
  42 /*
  43  * A bare-bones (testing aid) server for LDAP scenarios.
  44  *
  45  * Override the following methods to provide customized behavior
  46  *

  47  *     * beforeConnectionHandled
  48  *     * handleRequest
  49  *
  50  * Instances of this class are safe for use by multiple threads.
  51  */
  52 public class BaseLdapServer implements Closeable {
  53 
  54     private static final System.Logger logger = System.getLogger("BaseLdapServer");
  55 
  56     private final Thread acceptingThread = new Thread(this::acceptConnections);
  57     private final ServerSocket serverSocket;
  58     private final List<Socket> socketList = new ArrayList<>();
  59     private final ExecutorService connectionsPool;
  60 
  61     private final Object lock = new Object();
  62     /*
  63      * 3-valued state to detect restarts and other programming errors.
  64      */
  65     private State state = State.NEW;
  66 
  67     private enum State {
  68         NEW,
  69         STARTED,
  70         STOPPED
  71     }
  72 
  73     public BaseLdapServer() throws IOException {
  74         this(new ServerSocket(0, 0, InetAddress.getLoopbackAddress()));
  75     }
  76 
  77     public BaseLdapServer(ServerSocket serverSocket) {
  78         this.serverSocket = Objects.requireNonNull(serverSocket);
  79         this.connectionsPool = Executors.newCachedThreadPool();
  80     }
  81 
  82     private void acceptConnections() {
  83         logger().log(INFO, "Server is accepting connections at port {0}",
  84                      getPort());
  85         try {

  86             while (isRunning()) {
  87                 Socket socket = serverSocket.accept();
  88                 logger().log(INFO, "Accepted new connection at {0}", socket);
  89                 synchronized (lock) {
  90                     // Recheck if the server is still running
  91                     // as someone has to close the `socket`
  92                     if (isRunning()) {
  93                         socketList.add(socket);
  94                     } else {
  95                         closeSilently(socket);
  96                     }
  97                 }
  98                 connectionsPool.submit(() -> handleConnection(socket));
  99             }
 100         } catch (IOException | RejectedExecutionException e) {
 101             if (isRunning()) {
 102                 throw new RuntimeException(
 103                         "Unexpected exception while accepting connections", e);
 104             }
 105         } finally {
 106             logger().log(INFO, "Server stopped accepting connections at port {0}",
 107                                 getPort());
 108         }
 109     }
 110 
 111     /*







 112      * A "Template Method" describing how a connection (represented by a socket)
 113      * is handled.
 114      *
 115      * The socket is closed immediately before the method returns (normally or
 116      * abruptly).
 117      */
 118     private void handleConnection(Socket socket) {
 119         // No need to close socket's streams separately, they will be closed
 120         // automatically when `socket.close()` is called
 121         beforeConnectionHandled(socket);
 122         try (socket) {
 123             OutputStream out = socket.getOutputStream();
 124             InputStream in = socket.getInputStream();
 125             byte[] inBuffer = new byte[1024];
 126             int count;
 127             byte[] request;
 128 
 129             ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 130             int msgLen = -1;
 131 


 223             if (state == State.STOPPED) {
 224                 return;
 225             }
 226             state = State.STOPPED;
 227             logger().log(INFO, "Stopping server at port {0}", getPort());
 228             acceptingThread.interrupt();
 229             closeSilently(serverSocket);
 230             // It's important to signal an interruption so that overridden
 231             // methods have a chance to return if they use
 232             // interruption-sensitive blocking operations. However, blocked I/O
 233             // operations on the socket will NOT react on that, hence the socket
 234             // also has to be closed to propagate shutting down.
 235             connectionsPool.shutdownNow();
 236             socketList.forEach(BaseLdapServer.this::closeSilently);
 237         }
 238     }
 239 
 240     /**
 241      * Returns the local port this server is listening at.
 242      *


 243      * @return the port this server is listening at
 244      */
 245     public int getPort() {
 246         return serverSocket.getLocalPort();
 247     }
 248 











 249     /*
 250      * Returns a flag to indicate whether this server is running or not.
 251      *
 252      * @return {@code true} if this server is running, {@code false} otherwise.
 253      */
 254     public boolean isRunning() {
 255         synchronized (lock) {
 256             return state == State.STARTED;
 257         }
 258     }
 259 
 260     /*
 261      * To be used by subclasses.
 262      */
 263     protected final void closeSilently(Closeable resource) {
 264         try {
 265             resource.close();
 266         } catch (IOException ignored) { }
 267     }
 268 }


  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.ByteArrayOutputStream;
  25 import java.io.Closeable;
  26 import java.io.IOException;
  27 import java.io.InputStream;
  28 import java.io.OutputStream;
  29 import java.net.InetAddress;
  30 import java.net.ServerSocket;
  31 import java.net.Socket;
  32 import java.util.ArrayList;
  33 import java.util.Arrays;
  34 import java.util.List;
  35 import java.util.Objects;
  36 import java.util.concurrent.ExecutorService;
  37 import java.util.concurrent.Executors;

  38 
  39 import static java.lang.System.Logger.Level.INFO;
  40 
  41 /*
  42  * A bare-bones (testing aid) server for LDAP scenarios.
  43  *
  44  * Override the following methods to provide customized behavior
  45  *
  46  *     * beforeAcceptingConnections
  47  *     * beforeConnectionHandled
  48  *     * handleRequest
  49  *
  50  * Instances of this class are safe for use by multiple threads.
  51  */
  52 public class BaseLdapServer implements Closeable {
  53 
  54     private static final System.Logger logger = System.getLogger("BaseLdapServer");
  55 
  56     private final Thread acceptingThread = new Thread(this::acceptConnections);
  57     private final ServerSocket serverSocket;
  58     private final List<Socket> socketList = new ArrayList<>();
  59     private final ExecutorService connectionsPool;
  60 
  61     private final Object lock = new Object();
  62     /*
  63      * 3-valued state to detect restarts and other programming errors.
  64      */
  65     private State state = State.NEW;
  66 
  67     private enum State {
  68         NEW,
  69         STARTED,
  70         STOPPED
  71     }
  72 
  73     public BaseLdapServer() throws IOException {
  74         this(new ServerSocket(0, 0, InetAddress.getLoopbackAddress()));
  75     }
  76 
  77     public BaseLdapServer(ServerSocket serverSocket) {
  78         this.serverSocket = Objects.requireNonNull(serverSocket);
  79         this.connectionsPool = Executors.newCachedThreadPool();
  80     }
  81 
  82     private void acceptConnections() {
  83         logger().log(INFO, "Server is accepting connections at port {0}",
  84                      getPort());
  85         try {
  86             beforeAcceptingConnections();
  87             while (isRunning()) {
  88                 Socket socket = serverSocket.accept();
  89                 logger().log(INFO, "Accepted new connection at {0}", socket);
  90                 synchronized (lock) {
  91                     // Recheck if the server is still running
  92                     // as someone has to close the `socket`
  93                     if (isRunning()) {
  94                         socketList.add(socket);
  95                     } else {
  96                         closeSilently(socket);
  97                     }
  98                 }
  99                 connectionsPool.submit(() -> handleConnection(socket));
 100             }
 101         } catch (Throwable t) {
 102             if (isRunning()) {
 103                 throw new RuntimeException(
 104                         "Unexpected exception while accepting connections", t);
 105             }
 106         } finally {
 107             logger().log(INFO, "Server stopped accepting connections at port {0}",
 108                                 getPort());
 109         }
 110     }
 111 
 112     /*
 113      * Called once immediately preceding the server accepting connections.
 114      *
 115      * Override to customize the behavior.
 116      */
 117     protected void beforeAcceptingConnections() { }
 118 
 119     /*
 120      * A "Template Method" describing how a connection (represented by a socket)
 121      * is handled.
 122      *
 123      * The socket is closed immediately before the method returns (normally or
 124      * abruptly).
 125      */
 126     private void handleConnection(Socket socket) {
 127         // No need to close socket's streams separately, they will be closed
 128         // automatically when `socket.close()` is called
 129         beforeConnectionHandled(socket);
 130         try (socket) {
 131             OutputStream out = socket.getOutputStream();
 132             InputStream in = socket.getInputStream();
 133             byte[] inBuffer = new byte[1024];
 134             int count;
 135             byte[] request;
 136 
 137             ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 138             int msgLen = -1;
 139 


 231             if (state == State.STOPPED) {
 232                 return;
 233             }
 234             state = State.STOPPED;
 235             logger().log(INFO, "Stopping server at port {0}", getPort());
 236             acceptingThread.interrupt();
 237             closeSilently(serverSocket);
 238             // It's important to signal an interruption so that overridden
 239             // methods have a chance to return if they use
 240             // interruption-sensitive blocking operations. However, blocked I/O
 241             // operations on the socket will NOT react on that, hence the socket
 242             // also has to be closed to propagate shutting down.
 243             connectionsPool.shutdownNow();
 244             socketList.forEach(BaseLdapServer.this::closeSilently);
 245         }
 246     }
 247 
 248     /**
 249      * Returns the local port this server is listening at.
 250      *
 251      * This method can be called at any time.
 252      *
 253      * @return the port this server is listening at
 254      */
 255     public int getPort() {
 256         return serverSocket.getLocalPort();
 257     }
 258 
 259     /**
 260      * Returns the address this server is listening at.
 261      *
 262      * This method can be called at any time.
 263      *
 264      * @return the address
 265      */
 266     public InetAddress getInetAddress() {
 267         return serverSocket.getInetAddress();
 268     }
 269 
 270     /*
 271      * Returns a flag to indicate whether this server is running or not.
 272      *
 273      * @return {@code true} if this server is running, {@code false} otherwise.
 274      */
 275     public boolean isRunning() {
 276         synchronized (lock) {
 277             return state == State.STARTED;
 278         }
 279     }
 280 
 281     /*
 282      * To be used by subclasses.
 283      */
 284     protected final void closeSilently(Closeable resource) {
 285         try {
 286             resource.close();
 287         } catch (IOException ignored) { }
 288     }
 289 }
< prev index next >