29 import java.io.ObjectInputStream; |
29 import java.io.ObjectInputStream; |
30 import java.net.PasswordAuthentication; |
30 import java.net.PasswordAuthentication; |
31 import java.net.URL; |
31 import java.net.URL; |
32 import java.util.HashMap; |
32 import java.util.HashMap; |
33 import java.util.Objects; |
33 import java.util.Objects; |
|
34 import java.util.concurrent.locks.Condition; |
|
35 import java.util.concurrent.locks.ReentrantLock; |
34 import java.util.function.Function; |
36 import java.util.function.Function; |
35 |
37 |
36 import sun.net.www.HeaderParser; |
38 import sun.net.www.HeaderParser; |
37 |
39 |
38 |
40 |
124 * i.e. if multiple threads need to get credentials from the user |
126 * i.e. if multiple threads need to get credentials from the user |
125 * at the same time, then all but the first will block until |
127 * at the same time, then all but the first will block until |
126 * the first completes its authentication. |
128 * the first completes its authentication. |
127 */ |
129 */ |
128 private static HashMap<String,Thread> requests = new HashMap<>(); |
130 private static HashMap<String,Thread> requests = new HashMap<>(); |
129 |
131 private static final ReentrantLock requestLock = new ReentrantLock(); |
|
132 private static final Condition requestFinished = requestLock.newCondition(); |
130 /* |
133 /* |
131 * check if AuthenticationInfo is available in the cache. |
134 * check if AuthenticationInfo is available in the cache. |
132 * If not, check if a request for this destination is in progress |
135 * If not, check if a request for this destination is in progress |
133 * and if so block until the other request is finished authenticating |
136 * and if so block until the other request is finished authenticating |
134 * and returns the cached authentication value. |
137 * and returns the cached authentication value. |
140 // either we already have a value in the cache, and we can |
143 // either we already have a value in the cache, and we can |
141 // use that immediately, or the serializeAuth behavior is disabled, |
144 // use that immediately, or the serializeAuth behavior is disabled, |
142 // and we can revert to concurrent requests |
145 // and we can revert to concurrent requests |
143 return cached; |
146 return cached; |
144 } |
147 } |
145 synchronized (requests) { |
148 requestLock.lock(); |
146 // check again after synchronizing, and if available |
149 try { |
|
150 // check again after locking, and if available |
147 // just return the cached value. |
151 // just return the cached value. |
148 cached = cache.apply(key); |
152 cached = cache.apply(key); |
149 if (cached != null) return cached; |
153 if (cached != null) return cached; |
150 |
154 |
151 // Otherwise, if no request is in progress, record this |
155 // Otherwise, if no request is in progress, record this |
162 return cached; |
166 return cached; |
163 } |
167 } |
164 // Otherwise, an other thread is currently performing authentication: |
168 // Otherwise, an other thread is currently performing authentication: |
165 // wait until it finishes. |
169 // wait until it finishes. |
166 while (requests.containsKey(key)) { |
170 while (requests.containsKey(key)) { |
167 try { |
171 requestFinished.awaitUninterruptibly(); |
168 requests.wait (); |
|
169 } catch (InterruptedException e) {} |
|
170 } |
172 } |
|
173 } finally { |
|
174 requestLock.unlock(); |
171 } |
175 } |
172 /* entry may be in cache now. */ |
176 /* entry may be in cache now. */ |
173 return cache.apply(key); |
177 return cache.apply(key); |
174 } |
178 } |
175 |
179 |
176 /* signal completion of an authentication (whether it succeeded or not) |
180 /* signal completion of an authentication (whether it succeeded or not) |
177 * so that other threads can continue. |
181 * so that other threads can continue. |
178 */ |
182 */ |
179 private static void requestCompleted (String key) { |
183 private static void requestCompleted (String key) { |
180 synchronized (requests) { |
184 requestLock.lock(); |
|
185 try { |
181 Thread thread = requests.get(key); |
186 Thread thread = requests.get(key); |
182 if (thread != null && thread == Thread.currentThread()) { |
187 if (thread != null && thread == Thread.currentThread()) { |
183 boolean waspresent = requests.remove(key) != null; |
188 boolean waspresent = requests.remove(key) != null; |
184 assert waspresent; |
189 assert waspresent; |
185 } |
190 } |
186 requests.notifyAll(); |
191 requestFinished.signalAll(); |
|
192 } finally { |
|
193 requestLock.unlock(); |
187 } |
194 } |
188 } |
195 } |
189 |
196 |
190 //public String toString () { |
197 //public String toString () { |
191 //return ("{"+type+":"+authScheme+":"+protocol+":"+host+":"+port+":"+realm+":"+path+"}"); |
198 //return ("{"+type+":"+authScheme+":"+protocol+":"+host+":"+port+":"+realm+":"+path+"}"); |
498 } |
503 } |
499 |
504 |
500 String s1, s2; /* used for serialization of pw */ |
505 String s1, s2; /* used for serialization of pw */ |
501 |
506 |
502 @java.io.Serial |
507 @java.io.Serial |
|
508 // should be safe to keep synchronized here |
503 private synchronized void readObject(ObjectInputStream s) |
509 private synchronized void readObject(ObjectInputStream s) |
504 throws IOException, ClassNotFoundException |
510 throws IOException, ClassNotFoundException |
505 { |
511 { |
506 s.defaultReadObject (); |
512 s.defaultReadObject (); |
507 pw = new PasswordAuthentication (s1, s2.toCharArray()); |
513 pw = new PasswordAuthentication (s1, s2.toCharArray()); |