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

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

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.
*
* 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

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.
*
* 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);

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

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.
*
* 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) {

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

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.
*
* 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);
}
}
}

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

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.
*
* 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;

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