diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java index 33e4df43325..deccd70f878 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java @@ -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() diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java b/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java index 841ab6bf265..29652dc5e6f 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java @@ -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 vector) { - QueueElement newElt = new QueueElement(event, vector); + void enqueue(EventObject event, Vector 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; } /** diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/EventSupport.java b/src/java.naming/share/classes/com/sun/jndi/ldap/EventSupport.java index 755905db084..43c5cbaa7a8 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/EventSupport.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/EventSupport.java @@ -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 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 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 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 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 v = - (Vector)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 v = + (Vector) vector.clone(); + eventQueue.enqueue(event, v); + } finally { + lock.unlock(); + } } // No finalize() needed because EventSupport is always owned by diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapClient.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapClient.java index 24950ae6e0f..fce6d932735 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapClient.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapClient.java @@ -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 unsolicited = new Vector<>(3); + private final Vector 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 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); diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java index ba0ad07e05c..27ecb9e4ca1 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java @@ -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(); } } } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java index 62191c01d0e..9330782f48c 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java @@ -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 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 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) { diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapRequest.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapRequest.java index b1ce5254802..2242d17ebf7 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapRequest.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapRequest.java @@ -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(); + } } /** diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSchemaCtx.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSchemaCtx.java index 4da3b70d3f5..8f481266c73 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSchemaCtx.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSchemaCtx.java @@ -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); } } } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/pool/ConnectionDesc.java b/src/java.naming/share/classes/com/sun/jndi/ldap/pool/ConnectionDesc.java index 970e5e0f117..dd6dc0da228 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/pool/ConnectionDesc.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/pool/ConnectionDesc.java @@ -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(); } } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java b/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java index d485288ccac..253dfff674c 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java @@ -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; diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java b/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java index b7bc3ba98bf..cc3de321c31 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java @@ -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 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 copy; - synchronized (map) { + mapLock.lock(); + try { copy = new ArrayList<>(map.values()); + } finally { + mapLock.unlock(); } ArrayList 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 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(); } }