8277954: Replace use of monitors with explicit locks in the JDK LDAP provider implementation
Reviewed-by: dfuchs
This commit is contained in:
parent
4cb4637b79
commit
66b6a5a84f
@ -51,6 +51,8 @@ import java.security.cert.X509Certificate;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import javax.net.ssl.SSLParameters;
|
import javax.net.ssl.SSLParameters;
|
||||||
import javax.net.ssl.HandshakeCompletedEvent;
|
import javax.net.ssl.HandshakeCompletedEvent;
|
||||||
@ -174,7 +176,10 @@ public final class Connection implements Runnable {
|
|||||||
private volatile boolean isUpgradedToStartTls;
|
private volatile boolean isUpgradedToStartTls;
|
||||||
|
|
||||||
// Lock to maintain isUpgradedToStartTls state
|
// Lock to maintain isUpgradedToStartTls state
|
||||||
final Object startTlsLock = new Object();
|
final ReentrantLock startTlsLock = new ReentrantLock();
|
||||||
|
|
||||||
|
// Connection instance lock
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
private static final boolean IS_HOSTNAME_VERIFICATION_DISABLED
|
private static final boolean IS_HOSTNAME_VERIFICATION_DISABLED
|
||||||
= hostnameVerificationDisabledValue();
|
= hostnameVerificationDisabledValue();
|
||||||
@ -373,8 +378,13 @@ public final class Connection implements Runnable {
|
|||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
synchronized int getMsgId() {
|
int getMsgId() {
|
||||||
return ++outMsgId;
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return ++outMsgId;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LdapRequest writeRequest(BerEncoder ber, int msgId) throws IOException {
|
LdapRequest writeRequest(BerEncoder ber, int msgId) throws IOException {
|
||||||
@ -408,9 +418,12 @@ public final class Connection implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
synchronized (this) {
|
lock.lock();
|
||||||
|
try {
|
||||||
outStream.write(ber.getBuf(), 0, ber.getDataLen());
|
outStream.write(ber.getBuf(), 0, ber.getDataLen());
|
||||||
outStream.flush();
|
outStream.flush();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
cleanup(null, true);
|
cleanup(null, true);
|
||||||
@ -427,11 +440,14 @@ public final class Connection implements Runnable {
|
|||||||
BerDecoder rber;
|
BerDecoder rber;
|
||||||
|
|
||||||
// If socket closed, don't even try
|
// If socket closed, don't even try
|
||||||
synchronized (this) {
|
lock.lock();
|
||||||
|
try {
|
||||||
if (sock == null) {
|
if (sock == null) {
|
||||||
throw new ServiceUnavailableException(host + ":" + port +
|
throw new ServiceUnavailableException(host + ":" + port +
|
||||||
"; socket closed");
|
"; socket closed");
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
IOException ioException = null;
|
IOException ioException = null;
|
||||||
@ -476,48 +492,61 @@ public final class Connection implements Runnable {
|
|||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private synchronized void addRequest(LdapRequest ldapRequest) {
|
private void addRequest(LdapRequest ldapRequest) {
|
||||||
|
lock.lock();
|
||||||
LdapRequest ldr = pendingRequests;
|
try {
|
||||||
if (ldr == null) {
|
LdapRequest ldr = pendingRequests;
|
||||||
pendingRequests = ldapRequest;
|
if (ldr == null) {
|
||||||
ldapRequest.next = null;
|
pendingRequests = ldapRequest;
|
||||||
} else {
|
ldapRequest.next = null;
|
||||||
ldapRequest.next = pendingRequests;
|
} else {
|
||||||
pendingRequests = ldapRequest;
|
ldapRequest.next = pendingRequests;
|
||||||
}
|
pendingRequests = ldapRequest;
|
||||||
}
|
|
||||||
|
|
||||||
synchronized LdapRequest findRequest(int msgId) {
|
|
||||||
|
|
||||||
LdapRequest ldr = pendingRequests;
|
|
||||||
while (ldr != null) {
|
|
||||||
if (ldr.msgId == msgId) {
|
|
||||||
return ldr;
|
|
||||||
}
|
}
|
||||||
ldr = ldr.next;
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void removeRequest(LdapRequest req) {
|
LdapRequest findRequest(int msgId) {
|
||||||
LdapRequest ldr = pendingRequests;
|
lock.lock();
|
||||||
LdapRequest ldrprev = null;
|
try {
|
||||||
|
LdapRequest ldr = pendingRequests;
|
||||||
while (ldr != null) {
|
while (ldr != null) {
|
||||||
if (ldr == req) {
|
if (ldr.msgId == msgId) {
|
||||||
ldr.cancel();
|
return ldr;
|
||||||
|
|
||||||
if (ldrprev != null) {
|
|
||||||
ldrprev.next = ldr.next;
|
|
||||||
} else {
|
|
||||||
pendingRequests = ldr.next;
|
|
||||||
}
|
}
|
||||||
ldr.next = null;
|
ldr = ldr.next;
|
||||||
}
|
}
|
||||||
ldrprev = ldr;
|
return null;
|
||||||
ldr = ldr.next;
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeRequest(LdapRequest req) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
LdapRequest ldr = pendingRequests;
|
||||||
|
LdapRequest ldrprev = null;
|
||||||
|
|
||||||
|
while (ldr != null) {
|
||||||
|
if (ldr == req) {
|
||||||
|
ldr.cancel();
|
||||||
|
|
||||||
|
if (ldrprev != null) {
|
||||||
|
ldrprev.next = ldr.next;
|
||||||
|
} else {
|
||||||
|
pendingRequests = ldr.next;
|
||||||
|
}
|
||||||
|
ldr.next = null;
|
||||||
|
}
|
||||||
|
ldrprev = ldr;
|
||||||
|
ldr = ldr.next;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,9 +575,12 @@ public final class Connection implements Runnable {
|
|||||||
ber.getDataLen());
|
ber.getDataLen());
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (this) {
|
lock.lock();
|
||||||
|
try {
|
||||||
outStream.write(ber.getBuf(), 0, ber.getDataLen());
|
outStream.write(ber.getBuf(), 0, ber.getDataLen());
|
||||||
outStream.flush();
|
outStream.flush();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@ -558,12 +590,17 @@ public final class Connection implements Runnable {
|
|||||||
// Don't expect any response for the abandon request.
|
// Don't expect any response for the abandon request.
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void abandonOutstandingReqs(Control[] reqCtls) {
|
void abandonOutstandingReqs(Control[] reqCtls) {
|
||||||
LdapRequest ldr = pendingRequests;
|
lock.lock();
|
||||||
|
try {
|
||||||
|
LdapRequest ldr = pendingRequests;
|
||||||
|
|
||||||
while (ldr != null) {
|
while (ldr != null) {
|
||||||
abandonRequest(ldr, reqCtls);
|
abandonRequest(ldr, reqCtls);
|
||||||
pendingRequests = ldr = ldr.next;
|
pendingRequests = ldr = ldr.next;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,9 +638,12 @@ public final class Connection implements Runnable {
|
|||||||
0, ber.getDataLen());
|
0, ber.getDataLen());
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (this) {
|
lock.lock();
|
||||||
|
try {
|
||||||
outStream.write(ber.getBuf(), 0, ber.getDataLen());
|
outStream.write(ber.getBuf(), 0, ber.getDataLen());
|
||||||
outStream.flush();
|
outStream.flush();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@ -625,8 +665,8 @@ public final class Connection implements Runnable {
|
|||||||
*/
|
*/
|
||||||
void cleanup(Control[] reqCtls, boolean notifyParent) {
|
void cleanup(Control[] reqCtls, boolean notifyParent) {
|
||||||
boolean nparent = false;
|
boolean nparent = false;
|
||||||
|
lock.lock();
|
||||||
synchronized (this) {
|
try {
|
||||||
useable = false;
|
useable = false;
|
||||||
|
|
||||||
if (sock != null) {
|
if (sock != null) {
|
||||||
@ -671,10 +711,12 @@ public final class Connection implements Runnable {
|
|||||||
LdapRequest ldr = pendingRequests;
|
LdapRequest ldr = pendingRequests;
|
||||||
while (ldr != null) {
|
while (ldr != null) {
|
||||||
ldr.close();
|
ldr.close();
|
||||||
ldr = ldr.next;
|
ldr = ldr.next;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
if (nparent) {
|
if (nparent) {
|
||||||
parent.processConnectionClosure();
|
parent.processConnectionClosure();
|
||||||
}
|
}
|
||||||
@ -723,33 +765,46 @@ public final class Connection implements Runnable {
|
|||||||
// "synchronize" might lead to deadlock so don't synchronize method
|
// "synchronize" might lead to deadlock so don't synchronize method
|
||||||
// Use streamLock instead for synchronizing update to stream
|
// Use streamLock instead for synchronizing update to stream
|
||||||
|
|
||||||
public synchronized void replaceStreams(InputStream newIn, OutputStream newOut) {
|
public void replaceStreams(InputStream newIn, OutputStream newOut) {
|
||||||
if (debug) {
|
lock.lock();
|
||||||
System.err.println("Replacing " + inStream + " with: " + newIn);
|
|
||||||
System.err.println("Replacing " + outStream + " with: " + newOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
inStream = newIn;
|
|
||||||
|
|
||||||
// Cleanup old stream
|
|
||||||
try {
|
try {
|
||||||
outStream.flush();
|
if (debug) {
|
||||||
} catch (IOException ie) {
|
System.err.println("Replacing " + inStream + " with: " + newIn);
|
||||||
if (debug)
|
System.err.println("Replacing " + outStream + " with: " + newOut);
|
||||||
System.err.println("Connection: cannot flush outstream: " + ie);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Replace stream
|
inStream = newIn;
|
||||||
outStream = newOut;
|
|
||||||
|
// Cleanup old stream
|
||||||
|
try {
|
||||||
|
outStream.flush();
|
||||||
|
} catch (IOException ie) {
|
||||||
|
if (debug)
|
||||||
|
System.err.println("Connection: cannot flush outstream: " + ie);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace stream
|
||||||
|
outStream = newOut;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Replace streams and set isUpdradedToStartTls flag to the provided value
|
* Replace streams and set isUpdradedToStartTls flag to the provided value
|
||||||
*/
|
*/
|
||||||
public synchronized void replaceStreams(InputStream newIn, OutputStream newOut, boolean isStartTls) {
|
public void replaceStreams(InputStream newIn, OutputStream newOut, boolean isStartTls) {
|
||||||
synchronized (startTlsLock) {
|
lock.lock();
|
||||||
replaceStreams(newIn, newOut);
|
try {
|
||||||
isUpgradedToStartTls = isStartTls;
|
startTlsLock.lock();
|
||||||
|
try {
|
||||||
|
replaceStreams(newIn, newOut);
|
||||||
|
isUpgradedToStartTls = isStartTls;
|
||||||
|
} finally {
|
||||||
|
startTlsLock.unlock();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -765,8 +820,13 @@ public final class Connection implements Runnable {
|
|||||||
* This ensures that there is no contention between the main thread
|
* This ensures that there is no contention between the main thread
|
||||||
* and the Connection thread when the main thread updates inStream.
|
* and the Connection thread when the main thread updates inStream.
|
||||||
*/
|
*/
|
||||||
private synchronized InputStream getInputStream() {
|
private InputStream getInputStream() {
|
||||||
return inStream;
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return inStream;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -817,31 +877,37 @@ public final class Connection implements Runnable {
|
|||||||
* the safest thing to do is to shut it down.
|
* the safest thing to do is to shut it down.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private final Object pauseLock = new Object(); // lock for reader to wait on while paused
|
// lock for reader to wait on while paused
|
||||||
|
private final ReentrantLock pauseLock = new ReentrantLock();
|
||||||
|
private final Condition pauseCondition = pauseLock.newCondition();
|
||||||
private boolean paused = false; // paused state of reader
|
private boolean paused = false; // paused state of reader
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unpauses reader thread if it was paused
|
* Unpauses reader thread if it was paused
|
||||||
*/
|
*/
|
||||||
private void unpauseReader() throws IOException {
|
private void unpauseReader() throws IOException {
|
||||||
synchronized (pauseLock) {
|
pauseLock.lock();
|
||||||
|
try {
|
||||||
if (paused) {
|
if (paused) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
System.err.println("Unpausing reader; read from: " +
|
System.err.println("Unpausing reader; read from: " +
|
||||||
inStream);
|
inStream);
|
||||||
}
|
}
|
||||||
paused = false;
|
paused = false;
|
||||||
pauseLock.notify();
|
pauseCondition.signal();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
pauseLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pauses reader so that it stops reading from the input stream.
|
* Pauses reader so that it stops reading from the input stream.
|
||||||
* Reader blocks on pauseLock instead of read().
|
* Reader blocks on pauseLock instead of read().
|
||||||
* MUST be called from within synchronized (pauseLock) clause.
|
* MUST be called with pauseLock locked.
|
||||||
*/
|
*/
|
||||||
private void pauseReader() throws IOException {
|
private void pauseReader() throws IOException {
|
||||||
|
assert pauseLock.isHeldByCurrentThread();
|
||||||
if (debug) {
|
if (debug) {
|
||||||
System.err.println("Pausing reader; was reading from: " +
|
System.err.println("Pausing reader; was reading from: " +
|
||||||
inStream);
|
inStream);
|
||||||
@ -849,7 +915,7 @@ public final class Connection implements Runnable {
|
|||||||
paused = true;
|
paused = true;
|
||||||
try {
|
try {
|
||||||
while (paused) {
|
while (paused) {
|
||||||
pauseLock.wait(); // notified by unpauseReader
|
pauseCondition.await(); // notified by unpauseReader
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new InterruptedIOException(
|
throw new InterruptedIOException(
|
||||||
@ -985,7 +1051,8 @@ public final class Connection implements Runnable {
|
|||||||
* to ensure that reader goes into paused state
|
* to ensure that reader goes into paused state
|
||||||
* before writer can attempt to unpause reader
|
* before writer can attempt to unpause reader
|
||||||
*/
|
*/
|
||||||
synchronized (pauseLock) {
|
pauseLock.lock();
|
||||||
|
try {
|
||||||
needPause = ldr.addReplyBer(retBer);
|
needPause = ldr.addReplyBer(retBer);
|
||||||
if (needPause) {
|
if (needPause) {
|
||||||
/*
|
/*
|
||||||
@ -996,6 +1063,8 @@ public final class Connection implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// else release pauseLock
|
// else release pauseLock
|
||||||
|
} finally {
|
||||||
|
pauseLock.unlock();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// System.err.println("Cannot find" +
|
// System.err.println("Cannot find" +
|
||||||
@ -1077,12 +1146,17 @@ public final class Connection implements Runnable {
|
|||||||
*/
|
*/
|
||||||
private volatile HandshakeListener tlsHandshakeListener;
|
private volatile HandshakeListener tlsHandshakeListener;
|
||||||
|
|
||||||
public synchronized void setHandshakeCompletedListener(SSLSocket sslSocket) {
|
public void setHandshakeCompletedListener(SSLSocket sslSocket) {
|
||||||
if (tlsHandshakeListener != null)
|
lock.lock();
|
||||||
tlsHandshakeListener.tlsHandshakeCompleted.cancel(false);
|
try {
|
||||||
|
if (tlsHandshakeListener != null)
|
||||||
|
tlsHandshakeListener.tlsHandshakeCompleted.cancel(false);
|
||||||
|
|
||||||
tlsHandshakeListener = new HandshakeListener();
|
tlsHandshakeListener = new HandshakeListener();
|
||||||
sslSocket.addHandshakeCompletedListener(tlsHandshakeListener);
|
sslSocket.addHandshakeCompletedListener(tlsHandshakeListener);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public X509Certificate getTlsServerCertificate()
|
public X509Certificate getTlsServerCertificate()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,6 +27,8 @@ package com.sun.jndi.ldap;
|
|||||||
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import javax.naming.event.NamingEvent;
|
import javax.naming.event.NamingEvent;
|
||||||
import javax.naming.event.NamingExceptionEvent;
|
import javax.naming.event.NamingExceptionEvent;
|
||||||
@ -47,6 +49,10 @@ import javax.naming.ldap.UnsolicitedNotificationListener;
|
|||||||
final class EventQueue implements Runnable {
|
final class EventQueue implements Runnable {
|
||||||
private static final boolean debug = false;
|
private static final boolean debug = false;
|
||||||
|
|
||||||
|
// EventQueue instance lock
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
private final Condition condition = lock.newCondition();
|
||||||
|
|
||||||
private static class QueueElement {
|
private static class QueueElement {
|
||||||
QueueElement next = null;
|
QueueElement next = null;
|
||||||
QueueElement prev = null;
|
QueueElement prev = null;
|
||||||
@ -86,18 +92,23 @@ final class EventQueue implements Runnable {
|
|||||||
* are notified.
|
* are notified.
|
||||||
* @param vector List of NamingListeners that will be notified of event.
|
* @param vector List of NamingListeners that will be notified of event.
|
||||||
*/
|
*/
|
||||||
synchronized void enqueue(EventObject event, Vector<NamingListener> vector) {
|
void enqueue(EventObject event, Vector<NamingListener> vector) {
|
||||||
QueueElement newElt = new QueueElement(event, vector);
|
lock.lock();
|
||||||
|
try {
|
||||||
|
QueueElement newElt = new QueueElement(event, vector);
|
||||||
|
|
||||||
if (head == null) {
|
if (head == null) {
|
||||||
head = newElt;
|
head = newElt;
|
||||||
tail = newElt;
|
tail = newElt;
|
||||||
} else {
|
} else {
|
||||||
newElt.next = head;
|
newElt.next = head;
|
||||||
head.prev = newElt;
|
head.prev = newElt;
|
||||||
head = newElt;
|
head = newElt;
|
||||||
|
}
|
||||||
|
condition.signal();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,19 +119,23 @@ final class EventQueue implements Runnable {
|
|||||||
* @exception java.lang.InterruptedException if any thread has
|
* @exception java.lang.InterruptedException if any thread has
|
||||||
* interrupted this thread.
|
* interrupted this thread.
|
||||||
*/
|
*/
|
||||||
private synchronized QueueElement dequeue()
|
private QueueElement dequeue() throws InterruptedException {
|
||||||
throws InterruptedException {
|
lock.lock();
|
||||||
while (tail == null)
|
try {
|
||||||
wait();
|
while (tail == null)
|
||||||
QueueElement elt = tail;
|
condition.await();
|
||||||
tail = elt.prev;
|
QueueElement elt = tail;
|
||||||
if (tail == null) {
|
tail = elt.prev;
|
||||||
head = null;
|
if (tail == null) {
|
||||||
} else {
|
head = null;
|
||||||
tail.next = null;
|
} else {
|
||||||
|
tail.next = null;
|
||||||
|
}
|
||||||
|
elt.prev = elt.next = null;
|
||||||
|
return elt;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
elt.prev = elt.next = null;
|
|
||||||
return elt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,6 +30,7 @@ import java.util.Vector;
|
|||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import javax.naming.*;
|
import javax.naming.*;
|
||||||
import javax.naming.event.*;
|
import javax.naming.event.*;
|
||||||
@ -117,6 +118,9 @@ final class EventSupport {
|
|||||||
|
|
||||||
private LdapCtx ctx;
|
private LdapCtx ctx;
|
||||||
|
|
||||||
|
// EventSupport instance lock. Accessed by LdapCtx
|
||||||
|
final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NamingEventNotifiers; hashed by search arguments;
|
* NamingEventNotifiers; hashed by search arguments;
|
||||||
*/
|
*/
|
||||||
@ -149,28 +153,33 @@ final class EventSupport {
|
|||||||
* 2. ensure that NamingEventNotifier thread's access to 'notifiers'
|
* 2. ensure that NamingEventNotifier thread's access to 'notifiers'
|
||||||
* is safe
|
* is safe
|
||||||
*/
|
*/
|
||||||
synchronized void addNamingListener(String nm, int scope,
|
void addNamingListener(String nm, int scope,
|
||||||
NamingListener l) throws NamingException {
|
NamingListener l) throws NamingException {
|
||||||
|
|
||||||
if (l instanceof ObjectChangeListener ||
|
lock.lock();
|
||||||
l instanceof NamespaceChangeListener) {
|
try {
|
||||||
NotifierArgs args = new NotifierArgs(nm, scope, l);
|
if (l instanceof ObjectChangeListener ||
|
||||||
|
l instanceof NamespaceChangeListener) {
|
||||||
|
NotifierArgs args = new NotifierArgs(nm, scope, l);
|
||||||
|
|
||||||
NamingEventNotifier notifier = notifiers.get(args);
|
NamingEventNotifier notifier = notifiers.get(args);
|
||||||
if (notifier == null) {
|
if (notifier == null) {
|
||||||
notifier = new NamingEventNotifier(this, ctx, args, l);
|
notifier = new NamingEventNotifier(this, ctx, args, l);
|
||||||
notifiers.put(args, notifier);
|
notifiers.put(args, notifier);
|
||||||
} else {
|
} else {
|
||||||
notifier.addNamingListener(l);
|
notifier.addNamingListener(l);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (l instanceof UnsolicitedNotificationListener) {
|
|
||||||
// Add listener to this's list of unsolicited notifiers
|
|
||||||
if (unsolicited == null) {
|
|
||||||
unsolicited = new Vector<>(3);
|
|
||||||
}
|
}
|
||||||
|
if (l instanceof UnsolicitedNotificationListener) {
|
||||||
|
// Add listener to this's list of unsolicited notifiers
|
||||||
|
if (unsolicited == null) {
|
||||||
|
unsolicited = new Vector<>(3);
|
||||||
|
}
|
||||||
|
|
||||||
unsolicited.addElement((UnsolicitedNotificationListener)l);
|
unsolicited.addElement((UnsolicitedNotificationListener) l);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,67 +187,82 @@ final class EventSupport {
|
|||||||
* Adds {@code l} to list of listeners interested in {@code nm}
|
* Adds {@code l} to list of listeners interested in {@code nm}
|
||||||
* and filter.
|
* and filter.
|
||||||
*/
|
*/
|
||||||
synchronized void addNamingListener(String nm, String filter,
|
void addNamingListener(String nm, String filter,
|
||||||
SearchControls ctls, NamingListener l) throws NamingException {
|
SearchControls ctls, NamingListener l) throws NamingException {
|
||||||
|
|
||||||
if (l instanceof ObjectChangeListener ||
|
lock.lock();
|
||||||
l instanceof NamespaceChangeListener) {
|
try {
|
||||||
NotifierArgs args = new NotifierArgs(nm, filter, ctls, l);
|
if (l instanceof ObjectChangeListener ||
|
||||||
|
l instanceof NamespaceChangeListener) {
|
||||||
|
NotifierArgs args = new NotifierArgs(nm, filter, ctls, l);
|
||||||
|
|
||||||
NamingEventNotifier notifier = notifiers.get(args);
|
NamingEventNotifier notifier = notifiers.get(args);
|
||||||
if (notifier == null) {
|
if (notifier == null) {
|
||||||
notifier = new NamingEventNotifier(this, ctx, args, l);
|
notifier = new NamingEventNotifier(this, ctx, args, l);
|
||||||
notifiers.put(args, notifier);
|
notifiers.put(args, notifier);
|
||||||
} else {
|
} else {
|
||||||
notifier.addNamingListener(l);
|
notifier.addNamingListener(l);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (l instanceof UnsolicitedNotificationListener) {
|
||||||
if (l instanceof UnsolicitedNotificationListener) {
|
// Add listener to this's list of unsolicited notifiers
|
||||||
// Add listener to this's list of unsolicited notifiers
|
if (unsolicited == null) {
|
||||||
if (unsolicited == null) {
|
unsolicited = new Vector<>(3);
|
||||||
unsolicited = new Vector<>(3);
|
}
|
||||||
|
unsolicited.addElement((UnsolicitedNotificationListener) l);
|
||||||
}
|
}
|
||||||
unsolicited.addElement((UnsolicitedNotificationListener)l);
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes {@code l} from all notifiers in this context.
|
* Removes {@code l} from all notifiers in this context.
|
||||||
*/
|
*/
|
||||||
synchronized void removeNamingListener(NamingListener l) {
|
void removeNamingListener(NamingListener l) {
|
||||||
if (debug) {
|
lock.lock();
|
||||||
System.err.println("EventSupport removing listener");
|
try {
|
||||||
}
|
if (debug) {
|
||||||
// Go through list of notifiers, remove 'l' from each.
|
System.err.println("EventSupport removing listener");
|
||||||
// If 'l' is notifier's only listener, remove notifier too.
|
}
|
||||||
Iterator<NamingEventNotifier> iterator = notifiers.values().iterator();
|
// Go through list of notifiers, remove 'l' from each.
|
||||||
while (iterator.hasNext()) {
|
// If 'l' is notifier's only listener, remove notifier too.
|
||||||
NamingEventNotifier notifier = iterator.next();
|
Iterator<NamingEventNotifier> iterator = notifiers.values().iterator();
|
||||||
if (notifier != null) {
|
while (iterator.hasNext()) {
|
||||||
if (debug) {
|
NamingEventNotifier notifier = iterator.next();
|
||||||
System.err.println("EventSupport removing listener from notifier");
|
if (notifier != null) {
|
||||||
}
|
|
||||||
notifier.removeNamingListener(l);
|
|
||||||
if (!notifier.hasNamingListeners()) {
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
System.err.println("EventSupport stopping notifier");
|
System.err.println("EventSupport removing listener from notifier");
|
||||||
|
}
|
||||||
|
notifier.removeNamingListener(l);
|
||||||
|
if (!notifier.hasNamingListeners()) {
|
||||||
|
if (debug) {
|
||||||
|
System.err.println("EventSupport stopping notifier");
|
||||||
|
}
|
||||||
|
notifier.stop();
|
||||||
|
iterator.remove();
|
||||||
}
|
}
|
||||||
notifier.stop();
|
|
||||||
iterator.remove();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Remove from list of unsolicited notifier
|
||||||
// Remove from list of unsolicited notifier
|
if (debug) {
|
||||||
if (debug) {
|
System.err.println("EventSupport removing unsolicited: " + unsolicited);
|
||||||
System.err.println("EventSupport removing unsolicited: " + unsolicited);
|
}
|
||||||
}
|
if (unsolicited != null) {
|
||||||
if (unsolicited != null) {
|
unsolicited.removeElement(l);
|
||||||
unsolicited.removeElement(l);
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized boolean hasUnsolicited() {
|
boolean hasUnsolicited() {
|
||||||
return (unsolicited != null && unsolicited.size() > 0);
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return (unsolicited != null && unsolicited.size() > 0);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,15 +270,20 @@ final class EventSupport {
|
|||||||
* Called by NamingEventNotifier to remove itself when it encounters
|
* Called by NamingEventNotifier to remove itself when it encounters
|
||||||
* a NamingException.
|
* a NamingException.
|
||||||
*/
|
*/
|
||||||
synchronized void removeDeadNotifier(NotifierArgs info) {
|
void removeDeadNotifier(NotifierArgs info) {
|
||||||
if (debug) {
|
lock.lock();
|
||||||
System.err.println("EventSupport.removeDeadNotifier: " + info.name);
|
try {
|
||||||
}
|
if (debug) {
|
||||||
if (notifiers != null) {
|
System.err.println("EventSupport.removeDeadNotifier: " + info.name);
|
||||||
// Only do this if cleanup() not been triggered, otherwise here
|
}
|
||||||
// will throw NullPointerException since notifiers will be set to
|
if (notifiers != null) {
|
||||||
// null in cleanup()
|
// Only do this if cleanup() not been triggered, otherwise here
|
||||||
notifiers.remove(info);
|
// will throw NullPointerException since notifiers will be set to
|
||||||
|
// null in cleanup()
|
||||||
|
notifiers.remove(info);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,41 +292,46 @@ final class EventSupport {
|
|||||||
* package private;
|
* package private;
|
||||||
* Called by LdapCtx when its clnt receives an unsolicited notification.
|
* Called by LdapCtx when its clnt receives an unsolicited notification.
|
||||||
*/
|
*/
|
||||||
synchronized void fireUnsolicited(Object obj) {
|
void fireUnsolicited(Object obj) {
|
||||||
if (debug) {
|
lock.lock();
|
||||||
System.err.println("EventSupport.fireUnsolicited: " + obj + " "
|
try {
|
||||||
+ unsolicited);
|
if (debug) {
|
||||||
}
|
System.err.println("EventSupport.fireUnsolicited: " + obj + " "
|
||||||
if (unsolicited == null || unsolicited.size() == 0) {
|
+ unsolicited);
|
||||||
// This shouldn't really happen, but might in case
|
}
|
||||||
// there is a timing problem that removes a listener
|
if (unsolicited == null || unsolicited.size() == 0) {
|
||||||
// before a fired event reaches here.
|
// This shouldn't really happen, but might in case
|
||||||
return;
|
// there is a timing problem that removes a listener
|
||||||
}
|
// before a fired event reaches here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (obj instanceof UnsolicitedNotification) {
|
if (obj instanceof UnsolicitedNotification) {
|
||||||
|
|
||||||
// Fire UnsolicitedNotification to unsolicited listeners
|
// Fire UnsolicitedNotification to unsolicited listeners
|
||||||
|
|
||||||
UnsolicitedNotificationEvent evt =
|
UnsolicitedNotificationEvent evt =
|
||||||
new UnsolicitedNotificationEvent(ctx, (UnsolicitedNotification)obj);
|
new UnsolicitedNotificationEvent(ctx, (UnsolicitedNotification) obj);
|
||||||
queueEvent(evt, unsolicited);
|
queueEvent(evt, unsolicited);
|
||||||
|
|
||||||
} else if (obj instanceof NamingException) {
|
} else if (obj instanceof NamingException) {
|
||||||
|
|
||||||
// Fire NamingExceptionEvent to unsolicited listeners.
|
// Fire NamingExceptionEvent to unsolicited listeners.
|
||||||
|
|
||||||
NamingExceptionEvent evt =
|
NamingExceptionEvent evt =
|
||||||
new NamingExceptionEvent(ctx, (NamingException)obj);
|
new NamingExceptionEvent(ctx, (NamingException) obj);
|
||||||
queueEvent(evt, unsolicited);
|
queueEvent(evt, unsolicited);
|
||||||
|
|
||||||
// When an exception occurs, the unsolicited listeners
|
// When an exception occurs, the unsolicited listeners
|
||||||
// are automatically deregistered.
|
// are automatically deregistered.
|
||||||
// When LdapClient.processUnsolicited() fires a NamingException,
|
// When LdapClient.processUnsolicited() fires a NamingException,
|
||||||
// it will update its listener list so we don't have to.
|
// it will update its listener list so we don't have to.
|
||||||
// Likewise for LdapCtx.
|
// Likewise for LdapCtx.
|
||||||
|
|
||||||
unsolicited = null;
|
unsolicited = null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,19 +340,24 @@ final class EventSupport {
|
|||||||
* stops the event queue from dispatching events.
|
* stops the event queue from dispatching events.
|
||||||
* Package private; used by LdapCtx.
|
* Package private; used by LdapCtx.
|
||||||
*/
|
*/
|
||||||
synchronized void cleanup() {
|
void cleanup() {
|
||||||
if (debug) System.err.println("EventSupport clean up");
|
lock.lock();
|
||||||
if (notifiers != null) {
|
try {
|
||||||
for (NamingEventNotifier notifier : notifiers.values()) {
|
if (debug) System.err.println("EventSupport clean up");
|
||||||
notifier.stop();
|
if (notifiers != null) {
|
||||||
|
for (NamingEventNotifier notifier : notifiers.values()) {
|
||||||
|
notifier.stop();
|
||||||
|
}
|
||||||
|
notifiers = null;
|
||||||
}
|
}
|
||||||
notifiers = null;
|
if (eventQueue != null) {
|
||||||
|
eventQueue.stop();
|
||||||
|
eventQueue = null;
|
||||||
|
}
|
||||||
|
// %%% Should we fire NamingExceptionEvents to unsolicited listeners?
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
if (eventQueue != null) {
|
|
||||||
eventQueue.stop();
|
|
||||||
eventQueue = null;
|
|
||||||
}
|
|
||||||
// %%% Should we fire NamingExceptionEvents to unsolicited listeners?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -332,28 +371,33 @@ final class EventSupport {
|
|||||||
* them to the registered listeners.
|
* them to the registered listeners.
|
||||||
* Package private; used by NamingEventNotifier to fire events
|
* Package private; used by NamingEventNotifier to fire events
|
||||||
*/
|
*/
|
||||||
synchronized void queueEvent(EventObject event,
|
void queueEvent(EventObject event,
|
||||||
Vector<? extends NamingListener> vector) {
|
Vector<? extends NamingListener> vector) {
|
||||||
if (notifiers == null) {
|
lock.lock();
|
||||||
// That means cleanup() already done, not queue event anymore,
|
try {
|
||||||
// otherwise, new created EventQueue will not been cleanup.
|
if (notifiers == null) {
|
||||||
return;
|
// That means cleanup() already done, not queue event anymore,
|
||||||
}
|
// otherwise, new created EventQueue will not been cleanup.
|
||||||
if (eventQueue == null)
|
return;
|
||||||
eventQueue = new EventQueue();
|
}
|
||||||
|
if (eventQueue == null)
|
||||||
|
eventQueue = new EventQueue();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the vector in order to freeze the state of the set
|
* Copy the vector in order to freeze the state of the set
|
||||||
* of EventListeners the event should be delivered to prior
|
* of EventListeners the event should be delivered to prior
|
||||||
* to delivery. This ensures that any changes made to the
|
* to delivery. This ensures that any changes made to the
|
||||||
* Vector from a target listener's method during the delivery
|
* Vector from a target listener's method during the delivery
|
||||||
* of this event will not take effect until after the event is
|
* of this event will not take effect until after the event is
|
||||||
* delivered.
|
* delivered.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked") // clone()
|
@SuppressWarnings("unchecked") // clone()
|
||||||
Vector<NamingListener> v =
|
Vector<NamingListener> v =
|
||||||
(Vector<NamingListener>)vector.clone();
|
(Vector<NamingListener>) vector.clone();
|
||||||
eventQueue.enqueue(event, v);
|
eventQueue.enqueue(event, v);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No finalize() needed because EventSupport is always owned by
|
// No finalize() needed because EventSupport is always owned by
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,6 +30,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import javax.naming.*;
|
import javax.naming.*;
|
||||||
import javax.naming.directory.*;
|
import javax.naming.directory.*;
|
||||||
@ -125,6 +126,8 @@ public final class LdapClient implements PooledConnection {
|
|||||||
private final PoolCallback pcb;
|
private final PoolCallback pcb;
|
||||||
private final boolean pooled;
|
private final boolean pooled;
|
||||||
private boolean authenticateCalled = false;
|
private boolean authenticateCalled = false;
|
||||||
|
// LdapClient instance lock, accessed by LdapCtx
|
||||||
|
final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@ -145,163 +148,170 @@ public final class LdapClient implements PooledConnection {
|
|||||||
pooled = (pcb != null);
|
pooled = (pcb != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized boolean authenticateCalled() {
|
boolean authenticateCalled() {
|
||||||
return authenticateCalled;
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return authenticateCalled;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized LdapResult
|
LdapResult authenticate(boolean initial, String name, Object pw, int version,
|
||||||
authenticate(boolean initial, String name, Object pw, int version,
|
|
||||||
String authMechanism, Control[] ctls, Hashtable<?,?> env)
|
String authMechanism, Control[] ctls, Hashtable<?,?> env)
|
||||||
throws NamingException {
|
throws NamingException {
|
||||||
|
lock.lock();
|
||||||
int readTimeout = conn.readTimeout;
|
|
||||||
conn.readTimeout = conn.connectTimeout;
|
|
||||||
LdapResult res = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authenticateCalled = true;
|
int readTimeout = conn.readTimeout;
|
||||||
|
conn.readTimeout = conn.connectTimeout;
|
||||||
|
LdapResult res;
|
||||||
try {
|
try {
|
||||||
ensureOpen();
|
authenticateCalled = true;
|
||||||
} catch (IOException e) {
|
|
||||||
NamingException ne = new CommunicationException();
|
|
||||||
ne.setRootCause(e);
|
|
||||||
throw ne;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (version) {
|
try {
|
||||||
case LDAP_VERSION3_VERSION2:
|
ensureOpen();
|
||||||
case LDAP_VERSION3:
|
} catch (IOException e) {
|
||||||
isLdapv3 = true;
|
NamingException ne = new CommunicationException();
|
||||||
break;
|
ne.setRootCause(e);
|
||||||
case LDAP_VERSION2:
|
throw ne;
|
||||||
isLdapv3 = false;
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new CommunicationException("Protocol version " + version +
|
|
||||||
" not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authMechanism.equalsIgnoreCase("none") ||
|
switch (version) {
|
||||||
authMechanism.equalsIgnoreCase("anonymous")) {
|
case LDAP_VERSION3_VERSION2:
|
||||||
|
case LDAP_VERSION3:
|
||||||
|
isLdapv3 = true;
|
||||||
|
break;
|
||||||
|
case LDAP_VERSION2:
|
||||||
|
isLdapv3 = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new CommunicationException("Protocol version " + version +
|
||||||
|
" not supported");
|
||||||
|
}
|
||||||
|
|
||||||
// Perform LDAP bind if we are reauthenticating, using LDAPv2,
|
if (authMechanism.equalsIgnoreCase("none") ||
|
||||||
// supporting failover to LDAPv2, or controls have been supplied.
|
authMechanism.equalsIgnoreCase("anonymous")) {
|
||||||
if (!initial ||
|
|
||||||
(version == LDAP_VERSION2) ||
|
// Perform LDAP bind if we are reauthenticating, using LDAPv2,
|
||||||
(version == LDAP_VERSION3_VERSION2) ||
|
// supporting failover to LDAPv2, or controls have been supplied.
|
||||||
((ctls != null) && (ctls.length > 0))) {
|
if (!initial ||
|
||||||
|
(version == LDAP_VERSION2) ||
|
||||||
|
(version == LDAP_VERSION3_VERSION2) ||
|
||||||
|
((ctls != null) && (ctls.length > 0))) {
|
||||||
|
try {
|
||||||
|
// anonymous bind; update name/pw for LDAPv2 retry
|
||||||
|
res = ldapBind(name = null, (byte[]) (pw = null), ctls, null,
|
||||||
|
false);
|
||||||
|
if (res.status == LdapClient.LDAP_SUCCESS) {
|
||||||
|
conn.setBound();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
NamingException ne =
|
||||||
|
new CommunicationException("anonymous bind failed: " +
|
||||||
|
conn.host + ":" + conn.port);
|
||||||
|
ne.setRootCause(e);
|
||||||
|
throw ne;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Skip LDAP bind for LDAPv3 anonymous bind
|
||||||
|
res = new LdapResult();
|
||||||
|
res.status = LdapClient.LDAP_SUCCESS;
|
||||||
|
}
|
||||||
|
} else if (authMechanism.equalsIgnoreCase("simple")) {
|
||||||
|
// simple authentication
|
||||||
|
byte[] encodedPw = null;
|
||||||
try {
|
try {
|
||||||
// anonymous bind; update name/pw for LDAPv2 retry
|
encodedPw = encodePassword(pw, isLdapv3);
|
||||||
res = ldapBind(name=null, (byte[])(pw=null), ctls, null,
|
res = ldapBind(name, encodedPw, ctls, null, false);
|
||||||
false);
|
|
||||||
if (res.status == LdapClient.LDAP_SUCCESS) {
|
if (res.status == LdapClient.LDAP_SUCCESS) {
|
||||||
conn.setBound();
|
conn.setBound();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
NamingException ne =
|
NamingException ne =
|
||||||
new CommunicationException("anonymous bind failed: " +
|
new CommunicationException("simple bind failed: " +
|
||||||
conn.host + ":" + conn.port);
|
conn.host + ":" + conn.port);
|
||||||
|
ne.setRootCause(e);
|
||||||
|
throw ne;
|
||||||
|
} finally {
|
||||||
|
// If pw was copied to a new array, clear that array as
|
||||||
|
// a security precaution.
|
||||||
|
if (encodedPw != pw && encodedPw != null) {
|
||||||
|
for (int i = 0; i < encodedPw.length; i++) {
|
||||||
|
encodedPw[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isLdapv3) {
|
||||||
|
// SASL authentication
|
||||||
|
try {
|
||||||
|
res = LdapSasl.saslBind(this, conn, conn.host, name, pw,
|
||||||
|
authMechanism, env, ctls);
|
||||||
|
if (res.status == LdapClient.LDAP_SUCCESS) {
|
||||||
|
conn.setBound();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
NamingException ne =
|
||||||
|
new CommunicationException("SASL bind failed: " +
|
||||||
|
conn.host + ":" + conn.port);
|
||||||
ne.setRootCause(e);
|
ne.setRootCause(e);
|
||||||
throw ne;
|
throw ne;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Skip LDAP bind for LDAPv3 anonymous bind
|
throw new AuthenticationNotSupportedException(authMechanism);
|
||||||
res = new LdapResult();
|
|
||||||
res.status = LdapClient.LDAP_SUCCESS;
|
|
||||||
}
|
}
|
||||||
} else if (authMechanism.equalsIgnoreCase("simple")) {
|
|
||||||
// simple authentication
|
//
|
||||||
byte[] encodedPw = null;
|
// re-try login using v2 if failing over
|
||||||
try {
|
//
|
||||||
encodedPw = encodePassword(pw, isLdapv3);
|
if (initial &&
|
||||||
res = ldapBind(name, encodedPw, ctls, null, false);
|
(res.status == LdapClient.LDAP_PROTOCOL_ERROR) &&
|
||||||
if (res.status == LdapClient.LDAP_SUCCESS) {
|
(version == LdapClient.LDAP_VERSION3_VERSION2) &&
|
||||||
conn.setBound();
|
(authMechanism.equalsIgnoreCase("none") ||
|
||||||
}
|
authMechanism.equalsIgnoreCase("anonymous") ||
|
||||||
} catch (IOException e) {
|
authMechanism.equalsIgnoreCase("simple"))) {
|
||||||
NamingException ne =
|
|
||||||
new CommunicationException("simple bind failed: " +
|
byte[] encodedPw = null;
|
||||||
conn.host + ":" + conn.port);
|
try {
|
||||||
ne.setRootCause(e);
|
isLdapv3 = false;
|
||||||
throw ne;
|
encodedPw = encodePassword(pw, false);
|
||||||
} finally {
|
res = ldapBind(name, encodedPw, ctls, null, false);
|
||||||
// If pw was copied to a new array, clear that array as
|
if (res.status == LdapClient.LDAP_SUCCESS) {
|
||||||
// a security precaution.
|
conn.setBound();
|
||||||
if (encodedPw != pw && encodedPw != null) {
|
}
|
||||||
for (int i = 0; i < encodedPw.length; i++) {
|
} catch (IOException e) {
|
||||||
encodedPw[i] = 0;
|
NamingException ne =
|
||||||
|
new CommunicationException(authMechanism + ":" +
|
||||||
|
conn.host + ":" + conn.port);
|
||||||
|
ne.setRootCause(e);
|
||||||
|
throw ne;
|
||||||
|
} finally {
|
||||||
|
// If pw was copied to a new array, clear that array as
|
||||||
|
// a security precaution.
|
||||||
|
if (encodedPw != pw && encodedPw != null) {
|
||||||
|
for (int i = 0; i < encodedPw.length; i++) {
|
||||||
|
encodedPw[i] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isLdapv3) {
|
|
||||||
// SASL authentication
|
// principal name not found
|
||||||
try {
|
// (map NameNotFoundException to AuthenticationException)
|
||||||
res = LdapSasl.saslBind(this, conn, conn.host, name, pw,
|
// %%% This is a workaround for Netscape servers returning
|
||||||
authMechanism, env, ctls);
|
// %%% no such object when the principal name is not found
|
||||||
if (res.status == LdapClient.LDAP_SUCCESS) {
|
// %%% Note that when this workaround is applied, it does not allow
|
||||||
conn.setBound();
|
// %%% response controls to be recorded by the calling context
|
||||||
}
|
if (res.status == LdapClient.LDAP_NO_SUCH_OBJECT) {
|
||||||
} catch (IOException e) {
|
throw new AuthenticationException(
|
||||||
NamingException ne =
|
getErrorMessage(res.status, res.errorMessage));
|
||||||
new CommunicationException("SASL bind failed: " +
|
|
||||||
conn.host + ":" + conn.port);
|
|
||||||
ne.setRootCause(e);
|
|
||||||
throw ne;
|
|
||||||
}
|
}
|
||||||
} else {
|
conn.setV3(isLdapv3);
|
||||||
throw new AuthenticationNotSupportedException(authMechanism);
|
return res;
|
||||||
|
} finally {
|
||||||
|
conn.readTimeout = readTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// re-try login using v2 if failing over
|
|
||||||
//
|
|
||||||
if (initial &&
|
|
||||||
(res.status == LdapClient.LDAP_PROTOCOL_ERROR) &&
|
|
||||||
(version == LdapClient.LDAP_VERSION3_VERSION2) &&
|
|
||||||
(authMechanism.equalsIgnoreCase("none") ||
|
|
||||||
authMechanism.equalsIgnoreCase("anonymous") ||
|
|
||||||
authMechanism.equalsIgnoreCase("simple"))) {
|
|
||||||
|
|
||||||
byte[] encodedPw = null;
|
|
||||||
try {
|
|
||||||
isLdapv3 = false;
|
|
||||||
encodedPw = encodePassword(pw, false);
|
|
||||||
res = ldapBind(name, encodedPw, ctls, null, false);
|
|
||||||
if (res.status == LdapClient.LDAP_SUCCESS) {
|
|
||||||
conn.setBound();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
NamingException ne =
|
|
||||||
new CommunicationException(authMechanism + ":" +
|
|
||||||
conn.host + ":" + conn.port);
|
|
||||||
ne.setRootCause(e);
|
|
||||||
throw ne;
|
|
||||||
} finally {
|
|
||||||
// If pw was copied to a new array, clear that array as
|
|
||||||
// a security precaution.
|
|
||||||
if (encodedPw != pw && encodedPw != null) {
|
|
||||||
for (int i = 0; i < encodedPw.length; i++) {
|
|
||||||
encodedPw[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// principal name not found
|
|
||||||
// (map NameNotFoundException to AuthenticationException)
|
|
||||||
// %%% This is a workaround for Netscape servers returning
|
|
||||||
// %%% no such object when the principal name is not found
|
|
||||||
// %%% Note that when this workaround is applied, it does not allow
|
|
||||||
// %%% response controls to be recorded by the calling context
|
|
||||||
if (res.status == LdapClient.LDAP_NO_SUCH_OBJECT) {
|
|
||||||
throw new AuthenticationException(
|
|
||||||
getErrorMessage(res.status, res.errorMessage));
|
|
||||||
}
|
|
||||||
conn.setV3(isLdapv3);
|
|
||||||
return res;
|
|
||||||
} finally {
|
} finally {
|
||||||
conn.readTimeout = readTimeout;
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,81 +323,86 @@ public final class LdapClient implements PooledConnection {
|
|||||||
* @param auth The authentication mechanism
|
* @param auth The authentication mechanism
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public synchronized LdapResult ldapBind(String dn, byte[]toServer,
|
public LdapResult ldapBind(String dn, byte[]toServer,
|
||||||
Control[] bindCtls, String auth, boolean pauseAfterReceipt)
|
Control[] bindCtls, String auth, boolean pauseAfterReceipt)
|
||||||
throws java.io.IOException, NamingException {
|
throws IOException, NamingException {
|
||||||
|
|
||||||
ensureOpen();
|
lock.lock();
|
||||||
|
try {
|
||||||
|
ensureOpen();
|
||||||
|
|
||||||
// flush outstanding requests
|
// flush outstanding requests
|
||||||
conn.abandonOutstandingReqs(null);
|
conn.abandonOutstandingReqs(null);
|
||||||
|
|
||||||
BerEncoder ber = new BerEncoder();
|
BerEncoder ber = new BerEncoder();
|
||||||
int curMsgId = conn.getMsgId();
|
int curMsgId = conn.getMsgId();
|
||||||
LdapResult res = new LdapResult();
|
LdapResult res = new LdapResult();
|
||||||
res.status = LDAP_OPERATIONS_ERROR;
|
res.status = LDAP_OPERATIONS_ERROR;
|
||||||
|
|
||||||
//
|
//
|
||||||
// build the bind request.
|
// build the bind request.
|
||||||
//
|
//
|
||||||
ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
||||||
ber.encodeInt(curMsgId);
|
ber.encodeInt(curMsgId);
|
||||||
ber.beginSeq(LdapClient.LDAP_REQ_BIND);
|
ber.beginSeq(LdapClient.LDAP_REQ_BIND);
|
||||||
ber.encodeInt(isLdapv3 ? LDAP_VERSION3 : LDAP_VERSION2);
|
ber.encodeInt(isLdapv3 ? LDAP_VERSION3 : LDAP_VERSION2);
|
||||||
ber.encodeString(dn, isLdapv3);
|
ber.encodeString(dn, isLdapv3);
|
||||||
|
|
||||||
// if authentication mechanism specified, it is SASL
|
// if authentication mechanism specified, it is SASL
|
||||||
if (auth != null) {
|
if (auth != null) {
|
||||||
ber.beginSeq(Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3);
|
ber.beginSeq(Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3);
|
||||||
ber.encodeString(auth, isLdapv3); // SASL mechanism
|
ber.encodeString(auth, isLdapv3); // SASL mechanism
|
||||||
if (toServer != null) {
|
if (toServer != null) {
|
||||||
ber.encodeOctetString(toServer,
|
ber.encodeOctetString(toServer,
|
||||||
Ber.ASN_OCTET_STR);
|
Ber.ASN_OCTET_STR);
|
||||||
}
|
|
||||||
ber.endSeq();
|
|
||||||
} else {
|
|
||||||
if (toServer != null) {
|
|
||||||
ber.encodeOctetString(toServer, Ber.ASN_CONTEXT);
|
|
||||||
} else {
|
|
||||||
ber.encodeOctetString(null, Ber.ASN_CONTEXT, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ber.endSeq();
|
||||||
|
} else {
|
||||||
|
if (toServer != null) {
|
||||||
|
ber.encodeOctetString(toServer, Ber.ASN_CONTEXT);
|
||||||
|
} else {
|
||||||
|
ber.encodeOctetString(null, Ber.ASN_CONTEXT, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
ber.endSeq();
|
ber.endSeq();
|
||||||
|
|
||||||
// Encode controls
|
// Encode controls
|
||||||
if (isLdapv3) {
|
if (isLdapv3) {
|
||||||
encodeControls(ber, bindCtls);
|
encodeControls(ber, bindCtls);
|
||||||
}
|
}
|
||||||
ber.endSeq();
|
ber.endSeq();
|
||||||
|
|
||||||
LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt);
|
LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt);
|
||||||
if (toServer != null) {
|
if (toServer != null) {
|
||||||
ber.reset(); // clear internally-stored password
|
ber.reset(); // clear internally-stored password
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read reply
|
// Read reply
|
||||||
BerDecoder rber = conn.readReply(req);
|
BerDecoder rber = conn.readReply(req);
|
||||||
|
|
||||||
rber.parseSeq(null); // init seq
|
rber.parseSeq(null); // init seq
|
||||||
rber.parseInt(); // msg id
|
rber.parseInt(); // msg id
|
||||||
if (rber.parseByte() != LDAP_REP_BIND) {
|
if (rber.parseByte() != LDAP_REP_BIND) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
rber.parseLength();
|
||||||
|
parseResult(rber, res, isLdapv3);
|
||||||
|
|
||||||
|
// handle server's credentials (if present)
|
||||||
|
if (isLdapv3 &&
|
||||||
|
(rber.bytesLeft() > 0) &&
|
||||||
|
(rber.peekByte() == (Ber.ASN_CONTEXT | 7))) {
|
||||||
|
res.serverCreds = rber.parseOctetString((Ber.ASN_CONTEXT | 7), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.resControls = isLdapv3 ? parseControls(rber) : null;
|
||||||
|
|
||||||
|
conn.removeRequest(req);
|
||||||
return res;
|
return res;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
rber.parseLength();
|
|
||||||
parseResult(rber, res, isLdapv3);
|
|
||||||
|
|
||||||
// handle server's credentials (if present)
|
|
||||||
if (isLdapv3 &&
|
|
||||||
(rber.bytesLeft() > 0) &&
|
|
||||||
(rber.peekByte() == (Ber.ASN_CONTEXT | 7))) {
|
|
||||||
res.serverCreds = rber.parseOctetString((Ber.ASN_CONTEXT | 7), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.resControls = isLdapv3 ? parseControls(rber) : null;
|
|
||||||
|
|
||||||
conn.removeRequest(req);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -406,12 +421,16 @@ public final class LdapClient implements PooledConnection {
|
|||||||
return conn.isUpgradedToStartTls();
|
return conn.isUpgradedToStartTls();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void incRefCount() {
|
void incRefCount() {
|
||||||
++referenceCount;
|
lock.lock();
|
||||||
if (debug > 1) {
|
try {
|
||||||
System.err.println("LdapClient.incRefCount: " + referenceCount + " " + this);
|
++referenceCount;
|
||||||
|
if (debug > 1) {
|
||||||
|
System.err.println("LdapClient.incRefCount: " + referenceCount + " " + this);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -434,30 +453,35 @@ public final class LdapClient implements PooledConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void close(Control[] reqCtls, boolean hardClose) {
|
void close(Control[] reqCtls, boolean hardClose) {
|
||||||
--referenceCount;
|
lock.lock();
|
||||||
|
try {
|
||||||
|
--referenceCount;
|
||||||
|
|
||||||
if (debug > 1) {
|
if (debug > 1) {
|
||||||
System.err.println("LdapClient: " + this);
|
System.err.println("LdapClient: " + this);
|
||||||
System.err.println("LdapClient: close() called: " + referenceCount);
|
System.err.println("LdapClient: close() called: " + referenceCount);
|
||||||
(new Throwable()).printStackTrace();
|
(new Throwable()).printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (referenceCount <= 0) {
|
if (referenceCount <= 0) {
|
||||||
if (debug > 0) System.err.println("LdapClient: closed connection " + this);
|
if (debug > 0) System.err.println("LdapClient: closed connection " + this);
|
||||||
if (!pooled) {
|
if (!pooled) {
|
||||||
// Not being pooled; continue with closing
|
// Not being pooled; continue with closing
|
||||||
conn.cleanup(reqCtls, false);
|
|
||||||
} else {
|
|
||||||
// Pooled
|
|
||||||
// Is this a real close or a request to return conn to pool
|
|
||||||
if (hardClose) {
|
|
||||||
conn.cleanup(reqCtls, false);
|
conn.cleanup(reqCtls, false);
|
||||||
pcb.removePooledConnection(this);
|
|
||||||
} else {
|
} else {
|
||||||
pcb.releasePooledConnection(this);
|
// Pooled
|
||||||
|
// Is this a real close or a request to return conn to pool
|
||||||
|
if (hardClose) {
|
||||||
|
conn.cleanup(reqCtls, false);
|
||||||
|
pcb.removePooledConnection(this);
|
||||||
|
} else {
|
||||||
|
pcb.releasePooledConnection(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,8 +511,13 @@ public final class LdapClient implements PooledConnection {
|
|||||||
/*
|
/*
|
||||||
* Used by connection pooling to close physical connection.
|
* Used by connection pooling to close physical connection.
|
||||||
*/
|
*/
|
||||||
public synchronized void closeConnection() {
|
public void closeConnection() {
|
||||||
forceClose(false); // this is a pool callback so no need to clean pool
|
lock.lock();
|
||||||
|
try {
|
||||||
|
forceClose(false); // this is a pool callback so no need to clean pool
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1491,7 +1520,8 @@ public final class LdapClient implements PooledConnection {
|
|||||||
// removeUnsolicited() is invoked to remove an LdapCtx from this client.
|
// removeUnsolicited() is invoked to remove an LdapCtx from this client.
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
private Vector<LdapCtx> unsolicited = new Vector<>(3);
|
private final Vector<LdapCtx> unsolicited = new Vector<>(3);
|
||||||
|
private final ReentrantLock unsolicitedLock = new ReentrantLock();
|
||||||
void addUnsolicited(LdapCtx ctx) {
|
void addUnsolicited(LdapCtx ctx) {
|
||||||
if (debug > 0) {
|
if (debug > 0) {
|
||||||
System.err.println("LdapClient.addUnsolicited" + ctx);
|
System.err.println("LdapClient.addUnsolicited" + ctx);
|
||||||
@ -1533,7 +1563,8 @@ public final class LdapClient implements PooledConnection {
|
|||||||
LdapCtx first = null;
|
LdapCtx first = null;
|
||||||
UnsolicitedNotification notice = null;
|
UnsolicitedNotification notice = null;
|
||||||
|
|
||||||
synchronized (unsolicited) {
|
unsolicitedLock.lock();
|
||||||
|
try {
|
||||||
if (unsolicited.size() > 0) {
|
if (unsolicited.size() > 0) {
|
||||||
first = unsolicited.elementAt(0);
|
first = unsolicited.elementAt(0);
|
||||||
|
|
||||||
@ -1551,6 +1582,8 @@ public final class LdapClient implements PooledConnection {
|
|||||||
first.convertControls(res.resControls) :
|
first.convertControls(res.resControls) :
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
unsolicitedLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notice != null) {
|
if (notice != null) {
|
||||||
@ -1579,11 +1612,14 @@ public final class LdapClient implements PooledConnection {
|
|||||||
|
|
||||||
private void notifyUnsolicited(Object e) {
|
private void notifyUnsolicited(Object e) {
|
||||||
ArrayList<LdapCtx> unsolicitedCopy;
|
ArrayList<LdapCtx> unsolicitedCopy;
|
||||||
synchronized (unsolicited) {
|
unsolicitedLock.lock();
|
||||||
|
try {
|
||||||
unsolicitedCopy = new ArrayList<>(unsolicited);
|
unsolicitedCopy = new ArrayList<>(unsolicited);
|
||||||
if (e instanceof NamingException) {
|
if (e instanceof NamingException) {
|
||||||
unsolicited.setSize(0); // no more listeners after exception
|
unsolicited.setSize(0); // no more listeners after exception
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
unsolicitedLock.unlock();
|
||||||
}
|
}
|
||||||
for (int i = 0; i < unsolicitedCopy.size(); i++) {
|
for (int i = 0; i < unsolicitedCopy.size(); i++) {
|
||||||
unsolicitedCopy.get(i).fireUnsolicited(e);
|
unsolicitedCopy.get(i).fireUnsolicited(e);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -43,6 +43,7 @@ import java.util.Hashtable;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -300,6 +301,7 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
private EventSupport eventSupport; // Event support helper for this ctx
|
private EventSupport eventSupport; // Event support helper for this ctx
|
||||||
private boolean unsolicited = false; // if there unsolicited listeners
|
private boolean unsolicited = false; // if there unsolicited listeners
|
||||||
private boolean sharable = true; // can share connection with other ctx
|
private boolean sharable = true; // can share connection with other ctx
|
||||||
|
private final ReentrantLock lock = new ReentrantLock(); // LdapCtx instance lock
|
||||||
|
|
||||||
// -------------- Constructors -----------------------------------
|
// -------------- Constructors -----------------------------------
|
||||||
|
|
||||||
@ -2649,26 +2651,31 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void close() throws NamingException {
|
public void close() throws NamingException {
|
||||||
if (debug) {
|
lock.lock();
|
||||||
System.err.println("LdapCtx: close() called " + this);
|
try {
|
||||||
(new Throwable()).printStackTrace();
|
if (debug) {
|
||||||
}
|
System.err.println("LdapCtx: close() called " + this);
|
||||||
|
(new Throwable()).printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
// Event (normal and unsolicited)
|
// Event (normal and unsolicited)
|
||||||
if (eventSupport != null) {
|
if (eventSupport != null) {
|
||||||
eventSupport.cleanup(); // idempotent
|
eventSupport.cleanup(); // idempotent
|
||||||
removeUnsolicited();
|
removeUnsolicited();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enumerations that are keeping the connection alive
|
// Enumerations that are keeping the connection alive
|
||||||
if (enumCount > 0) {
|
if (enumCount > 0) {
|
||||||
if (debug)
|
if (debug)
|
||||||
System.err.println("LdapCtx: close deferred");
|
System.err.println("LdapCtx: close deferred");
|
||||||
closeRequested = true;
|
closeRequested = true;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
closeConnection(SOFT_CLOSE);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
closeConnection(SOFT_CLOSE);
|
|
||||||
|
|
||||||
// %%%: RL: There is no need to set these to null, as they're just
|
// %%%: RL: There is no need to set these to null, as they're just
|
||||||
// variables whose contents and references will automatically
|
// variables whose contents and references will automatically
|
||||||
@ -2785,13 +2792,17 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
|
|
||||||
} else if (!sharable || startTLS) {
|
} else if (!sharable || startTLS) {
|
||||||
|
|
||||||
synchronized (clnt) {
|
ReentrantLock clientLock = clnt.lock;
|
||||||
|
clientLock.lock();
|
||||||
|
try {
|
||||||
if (!clnt.isLdapv3
|
if (!clnt.isLdapv3
|
||||||
|| clnt.referenceCount > 1
|
|| clnt.referenceCount > 1
|
||||||
|| clnt.usingSaslStreams()
|
|| clnt.usingSaslStreams()
|
||||||
|| !clnt.conn.useable) {
|
|| !clnt.conn.useable) {
|
||||||
closeConnection(SOFT_CLOSE);
|
closeConnection(SOFT_CLOSE);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
clientLock.unlock();
|
||||||
}
|
}
|
||||||
// reset the cache before a new connection is established
|
// reset the cache before a new connection is established
|
||||||
schemaTrees = new Hashtable<>(11, 0.75f);
|
schemaTrees = new Hashtable<>(11, 0.75f);
|
||||||
@ -2891,10 +2902,14 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
LdapResult answer;
|
LdapResult answer;
|
||||||
synchronized (clnt.conn.startTlsLock) {
|
ReentrantLock startTlsLock = clnt.conn.startTlsLock;
|
||||||
|
startTlsLock.lock();
|
||||||
|
try {
|
||||||
ensureCanTransmitCredentials(authMechanism);
|
ensureCanTransmitCredentials(authMechanism);
|
||||||
answer = clnt.authenticate(initial, user, passwd, ldapVersion,
|
answer = clnt.authenticate(initial, user, passwd, ldapVersion,
|
||||||
authMechanism, bindCtls, envprops);
|
authMechanism, bindCtls, envprops);
|
||||||
|
} finally {
|
||||||
|
startTlsLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
respCtls = answer.resControls; // retrieve (bind) response controls
|
respCtls = answer.resControls; // retrieve (bind) response controls
|
||||||
@ -2967,21 +2982,31 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
private int enumCount = 0;
|
private int enumCount = 0;
|
||||||
private boolean closeRequested = false;
|
private boolean closeRequested = false;
|
||||||
|
|
||||||
synchronized void incEnumCount() {
|
void incEnumCount() {
|
||||||
++enumCount;
|
lock.lock();
|
||||||
if (debug) System.err.println("LdapCtx: " + this + " enum inc: " + enumCount);
|
try {
|
||||||
|
++enumCount;
|
||||||
|
if (debug) System.err.println("LdapCtx: " + this + " enum inc: " + enumCount);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void decEnumCount() {
|
void decEnumCount() {
|
||||||
--enumCount;
|
lock.lock();
|
||||||
if (debug) System.err.println("LdapCtx: " + this + " enum dec: " + enumCount);
|
try {
|
||||||
|
--enumCount;
|
||||||
|
if (debug) System.err.println("LdapCtx: " + this + " enum dec: " + enumCount);
|
||||||
|
|
||||||
if (enumCount == 0 && closeRequested) {
|
if (enumCount == 0 && closeRequested) {
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
} catch (NamingException e) {
|
} catch (NamingException e) {
|
||||||
// ignore failures
|
// ignore failures
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3576,17 +3601,20 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addNamingListener(String nm, String filter, SearchControls ctls,
|
public void addNamingListener(String nm, String filter,
|
||||||
NamingListener l) throws NamingException {
|
SearchControls ctls, NamingListener l)
|
||||||
if (eventSupport == null)
|
throws NamingException {
|
||||||
eventSupport = new EventSupport(this);
|
|
||||||
eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
|
if (eventSupport == null)
|
||||||
|
eventSupport = new EventSupport(this);
|
||||||
|
|
||||||
|
eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
|
||||||
filter, cloneSearchControls(ctls), l);
|
filter, cloneSearchControls(ctls), l);
|
||||||
|
|
||||||
// If first time asking for unsol
|
// If first time asking for unsol
|
||||||
if (l instanceof UnsolicitedNotificationListener && !unsolicited) {
|
if (l instanceof UnsolicitedNotificationListener && !unsolicited) {
|
||||||
addUnsolicited();
|
addUnsolicited();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addNamingListener(Name nm, String filter, SearchControls ctls,
|
public void addNamingListener(Name nm, String filter, SearchControls ctls,
|
||||||
@ -3653,9 +3681,13 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
|
|
||||||
// addNamingListener must have created EventSupport already
|
// addNamingListener must have created EventSupport already
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
synchronized (eventSupport) {
|
ReentrantLock eventSupportLock = eventSupport.lock;
|
||||||
|
eventSupportLock.lock();
|
||||||
|
try {
|
||||||
clnt.addUnsolicited(this);
|
clnt.addUnsolicited(this);
|
||||||
unsolicited = true;
|
unsolicited = true;
|
||||||
|
} finally {
|
||||||
|
eventSupportLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3682,11 +3714,15 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addNamingListener must have created EventSupport already
|
// addNamingListener must have created EventSupport already
|
||||||
synchronized(eventSupport) {
|
ReentrantLock eventSupportLock = eventSupport.lock;
|
||||||
|
eventSupportLock.lock();
|
||||||
|
try {
|
||||||
if (unsolicited && clnt != null) {
|
if (unsolicited && clnt != null) {
|
||||||
clnt.removeUnsolicited(this);
|
clnt.removeUnsolicited(this);
|
||||||
}
|
}
|
||||||
unsolicited = false;
|
unsolicited = false;
|
||||||
|
} finally {
|
||||||
|
eventSupportLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3699,7 +3735,9 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
System.out.println("LdapCtx.fireUnsolicited: " + obj);
|
System.out.println("LdapCtx.fireUnsolicited: " + obj);
|
||||||
}
|
}
|
||||||
// addNamingListener must have created EventSupport already
|
// addNamingListener must have created EventSupport already
|
||||||
synchronized(eventSupport) {
|
ReentrantLock eventSupportLock = eventSupport.lock;
|
||||||
|
eventSupportLock.lock();
|
||||||
|
try {
|
||||||
if (unsolicited) {
|
if (unsolicited) {
|
||||||
eventSupport.fireUnsolicited(obj);
|
eventSupport.fireUnsolicited(obj);
|
||||||
|
|
||||||
@ -3710,6 +3748,8 @@ public final class LdapCtx extends ComponentDirContext
|
|||||||
// unsol listeners and it will handle its own cleanup
|
// unsol listeners and it will handle its own cleanup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
eventSupportLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -28,6 +28,7 @@ package com.sun.jndi.ldap;
|
|||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import javax.naming.NamingException;
|
import javax.naming.NamingException;
|
||||||
import javax.naming.ldap.spi.LdapDnsProvider;
|
import javax.naming.ldap.spi.LdapDnsProvider;
|
||||||
import javax.naming.ldap.spi.LdapDnsProviderResult;
|
import javax.naming.ldap.spi.LdapDnsProviderResult;
|
||||||
@ -43,7 +44,7 @@ import sun.security.util.SecurityConstants;
|
|||||||
final class LdapDnsProviderService {
|
final class LdapDnsProviderService {
|
||||||
|
|
||||||
private static volatile LdapDnsProviderService service;
|
private static volatile LdapDnsProviderService service;
|
||||||
private static final Object LOCK = new int[0];
|
private static final ReentrantLock LOCK = new ReentrantLock();
|
||||||
private final ServiceLoader<LdapDnsProvider> providers;
|
private final ServiceLoader<LdapDnsProvider> providers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,9 +76,12 @@ final class LdapDnsProviderService {
|
|||||||
*/
|
*/
|
||||||
static LdapDnsProviderService getInstance() {
|
static LdapDnsProviderService getInstance() {
|
||||||
if (service != null) return service;
|
if (service != null) return service;
|
||||||
synchronized (LOCK) {
|
LOCK.lock();
|
||||||
|
try {
|
||||||
if (service != null) return service;
|
if (service != null) return service;
|
||||||
service = new LdapDnsProviderService();
|
service = new LdapDnsProviderService();
|
||||||
|
} finally {
|
||||||
|
LOCK.unlock();
|
||||||
}
|
}
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
@ -96,13 +100,16 @@ final class LdapDnsProviderService {
|
|||||||
{
|
{
|
||||||
LdapDnsProviderResult result = null;
|
LdapDnsProviderResult result = null;
|
||||||
Hashtable<?, ?> envCopy = new Hashtable<>(env);
|
Hashtable<?, ?> envCopy = new Hashtable<>(env);
|
||||||
synchronized (LOCK) {
|
LOCK.lock();
|
||||||
|
try {
|
||||||
Iterator<LdapDnsProvider> iterator = providers.iterator();
|
Iterator<LdapDnsProvider> iterator = providers.iterator();
|
||||||
while (result == null && iterator.hasNext()) {
|
while (result == null && iterator.hasNext()) {
|
||||||
result = iterator.next().lookupEndpoints(url, envCopy)
|
result = iterator.next().lookupEndpoints(url, envCopy)
|
||||||
.filter(r -> !r.getEndpoints().isEmpty())
|
.filter(r -> !r.getEndpoints().isEmpty())
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
LOCK.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -29,8 +29,8 @@ import java.io.IOException;
|
|||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import javax.naming.CommunicationException;
|
import javax.naming.CommunicationException;
|
||||||
import javax.naming.NamingException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
final class LdapRequest {
|
final class LdapRequest {
|
||||||
|
|
||||||
@ -46,6 +46,8 @@ final class LdapRequest {
|
|||||||
private volatile boolean closed;
|
private volatile boolean closed;
|
||||||
private volatile boolean completed;
|
private volatile boolean completed;
|
||||||
private final boolean pauseAfterReceipt;
|
private final boolean pauseAfterReceipt;
|
||||||
|
// LdapRequest instance lock
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
LdapRequest(int msgId, boolean pause, int replyQueueCapacity) {
|
LdapRequest(int msgId, boolean pause, int replyQueueCapacity) {
|
||||||
this.msgId = msgId;
|
this.msgId = msgId;
|
||||||
@ -62,40 +64,50 @@ final class LdapRequest {
|
|||||||
replies.offer(EOF);
|
replies.offer(EOF);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void close() {
|
void close() {
|
||||||
closed = true;
|
lock.lock();
|
||||||
replies.offer(EOF);
|
try {
|
||||||
|
closed = true;
|
||||||
|
replies.offer(EOF);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isClosed() {
|
private boolean isClosed() {
|
||||||
return closed && (replies.size() == 0 || replies.peek() == EOF);
|
return closed && (replies.size() == 0 || replies.peek() == EOF);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized boolean addReplyBer(BerDecoder ber) {
|
boolean addReplyBer(BerDecoder ber) {
|
||||||
// check the closed boolean value here as we don't want anything
|
lock.lock();
|
||||||
// to be added to the queue after close() has been called.
|
|
||||||
if (cancelled || closed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek at the BER buffer to check if it is a SearchResultDone PDU
|
|
||||||
try {
|
try {
|
||||||
ber.parseSeq(null);
|
// check the closed boolean value here as we don't want anything
|
||||||
ber.parseInt();
|
// to be added to the queue after close() has been called.
|
||||||
completed = (ber.peekByte() == LdapClient.LDAP_REP_RESULT);
|
if (cancelled || closed) {
|
||||||
} catch (IOException e) {
|
return false;
|
||||||
// ignore
|
}
|
||||||
}
|
|
||||||
ber.reset();
|
|
||||||
|
|
||||||
// Add a new reply to the queue of unprocessed replies.
|
// peek at the BER buffer to check if it is a SearchResultDone PDU
|
||||||
try {
|
try {
|
||||||
replies.put(ber);
|
ber.parseSeq(null);
|
||||||
} catch (InterruptedException e) {
|
ber.parseInt();
|
||||||
// ignore
|
completed = (ber.peekByte() == LdapClient.LDAP_REP_RESULT);
|
||||||
}
|
} catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
ber.reset();
|
||||||
|
|
||||||
return pauseAfterReceipt;
|
// Add a new reply to the queue of unprocessed replies.
|
||||||
|
try {
|
||||||
|
replies.put(ber);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return pauseAfterReceipt;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -28,6 +28,8 @@ package com.sun.jndi.ldap;
|
|||||||
import javax.naming.*;
|
import javax.naming.*;
|
||||||
import javax.naming.directory.*;
|
import javax.naming.directory.*;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import com.sun.jndi.toolkit.dir.HierMemDirCtx;
|
import com.sun.jndi.toolkit.dir.HierMemDirCtx;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -394,6 +396,9 @@ final class LdapSchemaCtx extends HierMemDirCtx {
|
|||||||
private int port;
|
private int port;
|
||||||
private boolean hasLdapsScheme;
|
private boolean hasLdapsScheme;
|
||||||
|
|
||||||
|
// SchemaInfo instance lock
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
SchemaInfo(String schemaEntryName, LdapCtx schemaEntry,
|
SchemaInfo(String schemaEntryName, LdapCtx schemaEntry,
|
||||||
LdapSchemaParser parser) {
|
LdapSchemaParser parser) {
|
||||||
this.schemaEntryName = schemaEntryName;
|
this.schemaEntryName = schemaEntryName;
|
||||||
@ -404,10 +409,15 @@ final class LdapSchemaCtx extends HierMemDirCtx {
|
|||||||
this.hasLdapsScheme = schemaEntry.hasLdapsScheme;
|
this.hasLdapsScheme = schemaEntry.hasLdapsScheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void close() throws NamingException {
|
void close() throws NamingException {
|
||||||
if (schemaEntry != null) {
|
lock.lock();
|
||||||
schemaEntry.close();
|
try {
|
||||||
schemaEntry = null;
|
if (schemaEntry != null) {
|
||||||
|
schemaEntry.close();
|
||||||
|
schemaEntry = null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,21 +427,31 @@ final class LdapSchemaCtx extends HierMemDirCtx {
|
|||||||
env, hasLdapsScheme);
|
env, hasLdapsScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void modifyAttributes(Hashtable<?,?> env,
|
void modifyAttributes(Hashtable<?,?> env,
|
||||||
ModificationItem[] mods)
|
ModificationItem[] mods)
|
||||||
throws NamingException {
|
throws NamingException {
|
||||||
if (schemaEntry == null) {
|
lock.lock();
|
||||||
schemaEntry = reopenEntry(env);
|
try {
|
||||||
|
if (schemaEntry == null) {
|
||||||
|
schemaEntry = reopenEntry(env);
|
||||||
|
}
|
||||||
|
schemaEntry.modifyAttributes("", mods);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
schemaEntry.modifyAttributes("", mods);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void modifyAttributes(Hashtable<?,?> env, int mod,
|
void modifyAttributes(Hashtable<?,?> env, int mod,
|
||||||
Attributes attrs) throws NamingException {
|
Attributes attrs) throws NamingException {
|
||||||
if (schemaEntry == null) {
|
lock.lock();
|
||||||
schemaEntry = reopenEntry(env);
|
try {
|
||||||
|
if (schemaEntry == null) {
|
||||||
|
schemaEntry = reopenEntry(env);
|
||||||
|
}
|
||||||
|
schemaEntry.modifyAttributes("", mod, attrs);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
schemaEntry.modifyAttributes("", mod, attrs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
package com.sun.jndi.ldap.pool;
|
package com.sun.jndi.ldap.pool;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a description of PooledConnection in Connections.
|
* Represents a description of PooledConnection in Connections.
|
||||||
* Contains a PooledConnection, its state (busy, idle, expired), and idle time.
|
* Contains a PooledConnection, its state (busy, idle, expired), and idle time.
|
||||||
@ -46,6 +48,8 @@ final class ConnectionDesc {
|
|||||||
private byte state = IDLE; // initial state
|
private byte state = IDLE; // initial state
|
||||||
private long idleSince;
|
private long idleSince;
|
||||||
private long useCount = 0; // for stats & debugging only
|
private long useCount = 0; // for stats & debugging only
|
||||||
|
// ConnectionDesc instance lock
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
ConnectionDesc(PooledConnection conn) {
|
ConnectionDesc(PooledConnection conn) {
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
@ -82,15 +86,20 @@ final class ConnectionDesc {
|
|||||||
* records the current time so that we will know how long it has been idle.
|
* records the current time so that we will know how long it has been idle.
|
||||||
* @return true if state change occurred.
|
* @return true if state change occurred.
|
||||||
*/
|
*/
|
||||||
synchronized boolean release() {
|
boolean release() {
|
||||||
d("release()");
|
lock.lock();
|
||||||
if (state == BUSY) {
|
try {
|
||||||
state = IDLE;
|
d("release()");
|
||||||
|
if (state == BUSY) {
|
||||||
|
state = IDLE;
|
||||||
|
|
||||||
idleSince = System.currentTimeMillis();
|
idleSince = System.currentTimeMillis();
|
||||||
return true; // Connection released, ready for reuse
|
return true; // Connection released, ready for reuse
|
||||||
} else {
|
} else {
|
||||||
return false; // Connection wasn't busy to begin with
|
return false; // Connection wasn't busy to begin with
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,16 +109,21 @@ final class ConnectionDesc {
|
|||||||
*
|
*
|
||||||
* @return ConnectionDesc's PooledConnection if it was idle; null otherwise.
|
* @return ConnectionDesc's PooledConnection if it was idle; null otherwise.
|
||||||
*/
|
*/
|
||||||
synchronized PooledConnection tryUse() {
|
PooledConnection tryUse() {
|
||||||
d("tryUse()");
|
lock.lock();
|
||||||
|
try {
|
||||||
|
d("tryUse()");
|
||||||
|
|
||||||
if (state == IDLE) {
|
if (state == IDLE) {
|
||||||
state = BUSY;
|
state = BUSY;
|
||||||
++useCount;
|
++useCount;
|
||||||
return conn;
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,18 +135,23 @@ final class ConnectionDesc {
|
|||||||
*
|
*
|
||||||
* @return true if entry is idle and has expired; false otherwise.
|
* @return true if entry is idle and has expired; false otherwise.
|
||||||
*/
|
*/
|
||||||
synchronized boolean expire(long threshold) {
|
boolean expire(long threshold) {
|
||||||
if (state == IDLE && idleSince < threshold) {
|
lock.lock();
|
||||||
|
try {
|
||||||
|
if (state == IDLE && idleSince < threshold) {
|
||||||
|
|
||||||
d("expire(): expired");
|
d("expire(): expired");
|
||||||
|
|
||||||
state = EXPIRED;
|
state = EXPIRED;
|
||||||
conn.closeConnection(); // Close real connection
|
conn.closeConnection(); // Close real connection
|
||||||
|
|
||||||
return true; // Expiration successful
|
return true; // Expiration successful
|
||||||
} else {
|
} else {
|
||||||
d("expire(): not expired");
|
d("expire(): not expired");
|
||||||
return false; // Expiration did not occur
|
return false; // Expiration did not occur
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -36,7 +36,6 @@ import java.lang.ref.SoftReference;
|
|||||||
|
|
||||||
import javax.naming.NamingException;
|
import javax.naming.NamingException;
|
||||||
import javax.naming.InterruptedNamingException;
|
import javax.naming.InterruptedNamingException;
|
||||||
import javax.naming.CommunicationException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a list of PooledConnections (actually, ConnectionDescs) with the
|
* Represents a list of PooledConnections (actually, ConnectionDescs) with the
|
||||||
@ -318,9 +317,14 @@ final class Connections implements PoolCallback {
|
|||||||
* and leave indicator so that any in-use connections will be closed upon
|
* and leave indicator so that any in-use connections will be closed upon
|
||||||
* their return.
|
* their return.
|
||||||
*/
|
*/
|
||||||
synchronized void close() {
|
void close() {
|
||||||
expire(System.currentTimeMillis()); // Expire idle connections
|
lock.lock();
|
||||||
closed = true; // Close in-use connections when they are returned
|
try {
|
||||||
|
expire(System.currentTimeMillis()); // Expire idle connections
|
||||||
|
closed = true; // Close in-use connections when they are returned
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getStats() {
|
String getStats() {
|
||||||
@ -330,7 +334,8 @@ final class Connections implements PoolCallback {
|
|||||||
long use = 0;
|
long use = 0;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
synchronized (this) {
|
lock.lock();
|
||||||
|
try {
|
||||||
len = conns.size();
|
len = conns.size();
|
||||||
|
|
||||||
ConnectionDesc entry;
|
ConnectionDesc entry;
|
||||||
@ -348,6 +353,8 @@ final class Connections implements PoolCallback {
|
|||||||
++expired;
|
++expired;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
return "size=" + len + "; use=" + use + "; busy=" + busy
|
return "size=" + len + "; use=" + use + "; busy=" + busy
|
||||||
+ "; idle=" + idle + "; expired=" + expired;
|
+ "; idle=" + idle + "; expired=" + expired;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -94,6 +94,7 @@ public final class Pool {
|
|||||||
private final int prefSize; // preferred num of identical conn per pool
|
private final int prefSize; // preferred num of identical conn per pool
|
||||||
private final int initSize; // initial number of identical conn to create
|
private final int initSize; // initial number of identical conn to create
|
||||||
private final Map<Object, ConnectionsRef> map;
|
private final Map<Object, ConnectionsRef> map;
|
||||||
|
private final ReentrantLock mapLock = new ReentrantLock();
|
||||||
|
|
||||||
public Pool(int initSize, int prefSize, int maxSize) {
|
public Pool(int initSize, int prefSize, int maxSize) {
|
||||||
map = new WeakHashMap<>();
|
map = new WeakHashMap<>();
|
||||||
@ -125,8 +126,11 @@ public final class Pool {
|
|||||||
|
|
||||||
d("get(): ", id);
|
d("get(): ", id);
|
||||||
if (debug) {
|
if (debug) {
|
||||||
synchronized (map) {
|
mapLock.lock();
|
||||||
|
try {
|
||||||
d("size: ", map.size());
|
d("size: ", map.size());
|
||||||
|
} finally {
|
||||||
|
mapLock.unlock();
|
||||||
}
|
}
|
||||||
remaining = checkRemaining(start, remaining);
|
remaining = checkRemaining(start, remaining);
|
||||||
}
|
}
|
||||||
@ -159,7 +163,8 @@ public final class Pool {
|
|||||||
throws NamingException {
|
throws NamingException {
|
||||||
|
|
||||||
Connections conns;
|
Connections conns;
|
||||||
synchronized (map) {
|
mapLock.lock();
|
||||||
|
try {
|
||||||
ConnectionsRef ref = map.get(id);
|
ConnectionsRef ref = map.get(id);
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
return ref.getConnections();
|
return ref.getConnections();
|
||||||
@ -179,6 +184,8 @@ public final class Pool {
|
|||||||
|
|
||||||
// Keep the weak reference through the element of a linked list
|
// Keep the weak reference through the element of a linked list
|
||||||
weakRefs.add(weakRef);
|
weakRefs.add(weakRef);
|
||||||
|
} finally {
|
||||||
|
mapLock.unlock();
|
||||||
}
|
}
|
||||||
return conns;
|
return conns;
|
||||||
}
|
}
|
||||||
@ -234,8 +241,11 @@ public final class Pool {
|
|||||||
*/
|
*/
|
||||||
public void expire(long threshold) {
|
public void expire(long threshold) {
|
||||||
Collection<ConnectionsRef> copy;
|
Collection<ConnectionsRef> copy;
|
||||||
synchronized (map) {
|
mapLock.lock();
|
||||||
|
try {
|
||||||
copy = new ArrayList<>(map.values());
|
copy = new ArrayList<>(map.values());
|
||||||
|
} finally {
|
||||||
|
mapLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<ConnectionsRef> removed = new ArrayList<>();
|
ArrayList<ConnectionsRef> removed = new ArrayList<>();
|
||||||
@ -248,8 +258,11 @@ public final class Pool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (map) {
|
mapLock.lock();
|
||||||
|
try {
|
||||||
map.values().removeAll(removed);
|
map.values().removeAll(removed);
|
||||||
|
} finally {
|
||||||
|
mapLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
expungeStaleConnections();
|
expungeStaleConnections();
|
||||||
@ -288,7 +301,8 @@ public final class Pool {
|
|||||||
out.println("preferred pool size: " + prefSize);
|
out.println("preferred pool size: " + prefSize);
|
||||||
out.println("initial pool size: " + initSize);
|
out.println("initial pool size: " + initSize);
|
||||||
|
|
||||||
synchronized (map) {
|
mapLock.lock();
|
||||||
|
try {
|
||||||
out.println("current pool size: " + map.size());
|
out.println("current pool size: " + map.size());
|
||||||
|
|
||||||
for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) {
|
for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) {
|
||||||
@ -296,14 +310,19 @@ public final class Pool {
|
|||||||
conns = entry.getValue().getConnections();
|
conns = entry.getValue().getConnections();
|
||||||
out.println(" " + id + ":" + conns.getStats());
|
out.println(" " + id + ":" + conns.getStats());
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
mapLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
out.println("====== Pool end =====================");
|
out.println("====== Pool end =====================");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
synchronized (map) {
|
mapLock.lock();
|
||||||
return super.toString() + " " + map.toString();
|
try {
|
||||||
|
return super.toString() + " " + map;
|
||||||
|
} finally {
|
||||||
|
mapLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user