Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/com/sun/jndi/ldap/lib/BaseLdapServer.java
38867 views
1
/*
2
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
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.logging.*;
39
import static java.util.logging.Level.*;
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 (or handleRequestEx)
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 Logger logger = Logger.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
ConnWrapper connWrapper = new ConnWrapper(socket);
131
try {
132
OutputStream out = socket.getOutputStream();
133
InputStream in = socket.getInputStream();
134
byte[] inBuffer = new byte[1024];
135
int count;
136
byte[] request;
137
138
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
139
int msgLen = -1;
140
141
// As inBuffer.length > 0, at least 1 byte is read
142
while ((count = in.read(inBuffer)) > 0) {
143
buffer.write(inBuffer, 0, count);
144
if (msgLen <= 0) {
145
msgLen = LdapMessage.getMessageLength(buffer.toByteArray());
146
}
147
148
if (msgLen > 0 && buffer.size() >= msgLen) {
149
if (buffer.size() > msgLen) {
150
byte[] tmpBuffer = buffer.toByteArray();
151
request = Arrays.copyOf(tmpBuffer, msgLen);
152
buffer.reset();
153
buffer.write(tmpBuffer, msgLen, tmpBuffer.length - msgLen);
154
} else {
155
request = buffer.toByteArray();
156
buffer.reset();
157
}
158
msgLen = -1;
159
} else {
160
logger.log(INFO, "Request message incomplete, " +
161
"bytes received {0}, expected {1}", new Integer[] { buffer.size(), msgLen});
162
continue;
163
}
164
handleRequestEx(socket, new LdapMessage(request), out, connWrapper);
165
if (connWrapper.updateRequired()) {
166
Socket wrapper = connWrapper.getWrapper();
167
in = wrapper.getInputStream();
168
out = wrapper.getOutputStream();
169
connWrapper.clearFlag();
170
}
171
}
172
} catch (Throwable t) {
173
if (!isRunning()) {
174
logger.log(INFO, "Connection Handler exit {0}", t.getMessage());
175
} else {
176
t.printStackTrace();
177
}
178
} finally {
179
if (socket != null) {
180
try {
181
socket.close();
182
} catch (Exception e) {
183
}
184
}
185
}
186
187
if (connWrapper.getWrapper() != null) {
188
closeSilently(connWrapper.getWrapper());
189
}
190
}
191
192
/*
193
* Called first thing in `handleConnection()`.
194
*
195
* Override to customize the behavior.
196
*/
197
protected void beforeConnectionHandled(Socket socket) { /* empty */ }
198
199
/*
200
* Called after an LDAP request has been read in `handleConnection()`.
201
*
202
* Override to customize the behavior.
203
*/
204
protected void handleRequest(Socket socket,
205
LdapMessage request,
206
OutputStream out)
207
throws IOException
208
{
209
logger().log(INFO, "Discarding message {0} from {1}. "
210
+ "Override {2}.handleRequest to change this behavior.",
211
new Object[] {request, socket, getClass().getName()});
212
}
213
214
/*
215
* Called after an LDAP request has been read in `handleConnection()`.
216
*
217
* Override to customize the behavior if you want to handle starttls
218
* extended op, otherwise override handleRequest method instead.
219
*
220
* This is extended handleRequest method which provide possibility to
221
* wrap current socket connection, that's necessary to handle starttls
222
* extended request, here is sample code about how to wrap current socket
223
*
224
* switch (request.getOperation()) {
225
* ......
226
* case EXTENDED_REQUEST:
227
* if (new String(request.getMessage()).endsWith(STARTTLS_REQ_OID)) {
228
* out.write(STARTTLS_RESPONSE);
229
* SSLSocket sslSocket = (SSLSocket) sslSocketFactory
230
* .createSocket(socket, null, socket.getLocalPort(),
231
* false);
232
* sslSocket.setUseClientMode(false);
233
* connWrapper.setWrapper(sslSocket);
234
* }
235
* break;
236
* ......
237
* }
238
*/
239
protected void handleRequestEx(Socket socket,
240
LdapMessage request,
241
OutputStream out,
242
ConnWrapper connWrapper)
243
throws IOException {
244
// by default, just call handleRequest to keep compatibility
245
handleRequest(socket, request, out);
246
}
247
248
/*
249
* To be used by subclasses.
250
*/
251
protected final Logger logger() {
252
return logger;
253
}
254
255
/*
256
* Starts this server. May be called only once.
257
*/
258
public BaseLdapServer start() {
259
synchronized (lock) {
260
if (state != State.NEW) {
261
throw new IllegalStateException(state.toString());
262
}
263
state = State.STARTED;
264
logger().log(INFO, "Starting server at port {0}", getPort());
265
acceptingThread.start();
266
return this;
267
}
268
}
269
270
/*
271
* Stops this server.
272
*
273
* May be called at any time, even before a call to `start()`. In the latter
274
* case the subsequent call to `start()` will throw an exception. Repeated
275
* calls to this method have no effect.
276
*
277
* Stops accepting new connections, interrupts the threads serving already
278
* accepted connections and closes all the sockets.
279
*/
280
@Override
281
public void close() {
282
synchronized (lock) {
283
if (state == State.STOPPED) {
284
return;
285
}
286
state = State.STOPPED;
287
logger().log(INFO, "Stopping server at port {0}", getPort());
288
acceptingThread.interrupt();
289
closeSilently(serverSocket);
290
// It's important to signal an interruption so that overridden
291
// methods have a chance to return if they use
292
// interruption-sensitive blocking operations. However, blocked I/O
293
// operations on the socket will NOT react on that, hence the socket
294
// also has to be closed to propagate shutting down.
295
connectionsPool.shutdownNow();
296
socketList.forEach(BaseLdapServer.this::closeSilently);
297
}
298
}
299
300
/**
301
* Returns the local port this server is listening at.
302
*
303
* This method can be called at any time.
304
*
305
* @return the port this server is listening at
306
*/
307
public int getPort() {
308
return serverSocket.getLocalPort();
309
}
310
311
/**
312
* Returns the address this server is listening at.
313
*
314
* This method can be called at any time.
315
*
316
* @return the address
317
*/
318
public InetAddress getInetAddress() {
319
return serverSocket.getInetAddress();
320
}
321
322
/*
323
* Returns a flag to indicate whether this server is running or not.
324
*
325
* @return {@code true} if this server is running, {@code false} otherwise.
326
*/
327
public boolean isRunning() {
328
synchronized (lock) {
329
return state == State.STARTED;
330
}
331
}
332
333
/*
334
* To be used by subclasses.
335
*/
336
protected final void closeSilently(Closeable resource) {
337
try {
338
resource.close();
339
} catch (IOException ignored) { }
340
}
341
342
/*
343
* To be used for handling starttls extended request
344
*/
345
protected class ConnWrapper {
346
private Socket original;
347
private Socket wrapper;
348
private boolean flag = false;
349
350
public ConnWrapper(Socket socket) {
351
original = socket;
352
}
353
354
public Socket getWrapper() {
355
return wrapper;
356
}
357
358
public void setWrapper(Socket wrapper) {
359
if (wrapper != null && wrapper != original) {
360
this.wrapper = wrapper;
361
flag = true;
362
}
363
}
364
365
public boolean updateRequired() {
366
return flag;
367
}
368
369
public void clearFlag() {
370
flag = false;
371
}
372
}
373
}
374
375