8277954: Replace use of monitors with explicit locks in the JDK LDAP provider implementation

Reviewed-by: dfuchs
This commit is contained in:
Aleksei Efimov 2023-09-11 14:05:48 +00:00
parent 4cb4637b79
commit 66b6a5a84f
11 changed files with 872 additions and 579 deletions

View File

@ -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()

View File

@ -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;
} }
/** /**

View File

@ -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

View File

@ -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);

View File

@ -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();
} }
} }
} }

View File

@ -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) {

View File

@ -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();
}
} }
/** /**

View File

@ -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);
} }
} }
} }

View File

@ -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();
} }
} }

View File

@ -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;

View File

@ -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();
} }
} }