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