Replace synchronized blocks with Locks (#5565)
Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
index 047bba8..3d07d3f 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -56,6 +56,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -92,18 +93,23 @@
private final MultivaluedMap<TYPE, InstanceContext<?>> singletonInstances = new MultivaluedHashMap<>();
private final ThreadLocal<MultivaluedMap<TYPE, InstanceContext<?>>> threadInstances = new ThreadLocal<>();
private final List<Object> threadPredestroyables = Collections.synchronizedList(new LinkedList<>());
+ private final ReentrantLock singletonInstancesLock = new ReentrantLock();
private <T> List<InstanceContext<?>> _getSingletons(TYPE clazz) {
List<InstanceContext<?>> si;
- synchronized (singletonInstances) {
+ singletonInstancesLock.lock();
+ try {
si = singletonInstances.get(clazz);
+ } finally {
+ singletonInstancesLock.unlock();
}
return si;
}
@SuppressWarnings("unchecked")
<T> T _addSingleton(TYPE clazz, T instance, Binding<?, ?> binding, Annotation[] qualifiers) {
- synchronized (singletonInstances) {
+ singletonInstancesLock.lock();
+ try {
// check existing singleton with a qualifier already created by another thread io a meantime
List<InstanceContext<?>> values = singletonInstances.get(clazz);
if (values != null) {
@@ -118,6 +124,8 @@
singletonInstances.add(clazz, new InstanceContext<>(instance, binding, qualifiers));
threadPredestroyables.add(instance);
return instance;
+ } finally {
+ singletonInstancesLock.unlock();
}
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
index cb7545f..77ab1e1 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -37,7 +37,9 @@
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
@@ -79,6 +81,7 @@
private final Map<Long, Map<String, Callable<List<Class<?>>>>> factories =
new HashMap<Long, Map<String, Callable<List<Class<?>>>>>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ private static final Lock INSTANCE_LOCK = new ReentrantLock();
private static OsgiRegistry instance;
@@ -90,16 +93,21 @@
*
* @return an {@code OsgiRegistry} instance.
*/
- public static synchronized OsgiRegistry getInstance() {
- if (instance == null) {
- final ClassLoader classLoader = AccessController
- .doPrivileged(ReflectionHelper.getClassLoaderPA(ReflectionHelper.class));
- if (classLoader instanceof BundleReference) {
- final BundleContext context = FrameworkUtil.getBundle(OsgiRegistry.class).getBundleContext();
- if (context != null) { // context could be still null if the current bundle has not been started
- instance = new OsgiRegistry(context);
+ public static OsgiRegistry getInstance() {
+ INSTANCE_LOCK.lock();
+ try {
+ if (instance == null) {
+ final ClassLoader classLoader = AccessController
+ .doPrivileged(ReflectionHelper.getClassLoaderPA(ReflectionHelper.class));
+ if (classLoader instanceof BundleReference) {
+ final BundleContext context = FrameworkUtil.getBundle(OsgiRegistry.class).getBundleContext();
+ if (context != null) { // context could be still null if the current bundle has not been started
+ instance = new OsgiRegistry(context);
+ }
}
}
+ } finally {
+ INSTANCE_LOCK.unlock();
}
return instance;
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
index 46184d8..03d15d3 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -33,6 +33,8 @@
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -797,17 +799,20 @@
public abstract static class ServiceIteratorProvider {
private static volatile ServiceIteratorProvider sip;
- private static final Object sipLock = new Object();
+ private static final Lock sipLock = new ReentrantLock();
private static ServiceIteratorProvider getInstance() {
// TODO: check the following is a good practice: Double-check idiom for lazy initialization of fields.
ServiceIteratorProvider result = sip;
if (result == null) { // First check (no locking)
- synchronized (sipLock) {
+ sipLock.lock();
+ try {
result = sip;
if (result == null) { // Second check (with locking)
sip = result = new DefaultServiceIteratorProvider();
}
+ } finally {
+ sipLock.unlock();
}
}
return result;
@@ -819,8 +824,11 @@
final ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
security.checkPermission(rp);
}
- synchronized (sipLock) {
+ sipLock.lock();
+ try {
ServiceIteratorProvider.sip = sip;
+ } finally {
+ sipLock.unlock();
}
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
index 4761644..5c3a747 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,6 +16,9 @@
package org.glassfish.jersey.internal.util.collection;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
/**
* A collection of {@link Value Value provider} factory & utility methods.
*
@@ -297,25 +300,28 @@
private static class LazyValueImpl<T> implements LazyValue<T> {
- private final Object lock;
+ private final Lock lock;
private final Value<T> delegate;
private volatile Value<T> value;
public LazyValueImpl(final Value<T> delegate) {
this.delegate = delegate;
- this.lock = new Object();
+ this.lock = new ReentrantLock();
}
@Override
public T get() {
Value<T> result = value;
if (result == null) {
- synchronized (lock) {
+ lock.lock();
+ try {
result = value;
if (result == null) {
value = result = Values.of(delegate.get());
}
+ } finally {
+ lock.unlock();
}
}
return result.get();
@@ -380,21 +386,22 @@
private static class LazyUnsafeValueImpl<T, E extends Throwable> implements LazyUnsafeValue<T, E> {
- private final Object lock;
+ private final Lock lock;
private final UnsafeValue<T, E> delegate;
private volatile UnsafeValue<T, E> value;
public LazyUnsafeValueImpl(final UnsafeValue<T, E> delegate) {
this.delegate = delegate;
- this.lock = new Object();
+ this.lock = new ReentrantLock();
}
@Override
public T get() throws E {
UnsafeValue<T, E> result = value;
if (result == null) {
- synchronized (lock) {
+ lock.lock();
+ try {
result = value;
//noinspection ConstantConditions
if (result == null) {
@@ -406,6 +413,8 @@
}
value = result;
}
+ } finally {
+ lock.unlock();
}
}
return result.get();
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
index 75479e6..0f76eed 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -50,13 +50,7 @@
private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");
- private static final ThreadLocal<List<SimpleDateFormat>> dateFormats = new ThreadLocal<List<SimpleDateFormat>>() {
-
- @Override
- protected synchronized List<SimpleDateFormat> initialValue() {
- return createDateFormats();
- }
- };
+ private static final ThreadLocal<List<SimpleDateFormat>> dateFormats = ThreadLocal.withInitial(() -> createDateFormats());
private static List<SimpleDateFormat> createDateFormats() {
final SimpleDateFormat[] formats = new SimpleDateFormat[]{
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
index fe5a249..62df83f 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -28,6 +28,8 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.MediaType;
@@ -622,6 +624,7 @@
private abstract static class ListReader<T> {
private final LRU<String, List<T>> LIST_CACHE = LRU.create();
+ private final Lock lock = new ReentrantLock();
protected final ListElementCreator<T> creator;
protected ListReader(ListElementCreator<T> creator) {
@@ -639,7 +642,8 @@
List<T> list = LIST_CACHE.getIfPresent(header);
if (list == null) {
- synchronized (LIST_CACHE) {
+ lock.lock();
+ try {
list = LIST_CACHE.getIfPresent(header);
if (list == null) {
HttpHeaderReader reader = new HttpHeaderReaderImpl(header);
@@ -655,6 +659,8 @@
}
LIST_CACHE.put(header, list);
}
+ } finally {
+ lock.unlock();
}
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
index 91738c1..7a1a4a5 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,6 +22,8 @@
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -252,6 +254,7 @@
private final InputStream original;
private final MessageBodyReader reader;
+ private final Lock markLock = new ReentrantLock();
private UnCloseableInputStream(final InputStream original, final MessageBodyReader reader) {
this.original = original;
@@ -284,13 +287,23 @@
}
@Override
- public synchronized void mark(final int i) {
- original.mark(i);
+ public void mark(final int i) {
+ markLock.lock();
+ try {
+ original.mark(i);
+ } finally {
+ markLock.unlock();
+ }
}
@Override
- public synchronized void reset() throws IOException {
- original.reset();
+ public void reset() throws IOException {
+ markLock.lock();
+ try {
+ original.reset();
+ } finally {
+ markLock.unlock();
+ }
}
@Override
diff --git a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
index ced41cd..4d92447 100644
--- a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
+++ b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -244,6 +244,7 @@
*/
protected void release(RequestContext context) {
context.release();
+ currentRequestContext.remove();
}
/**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
index 2ce0542..b671e68 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -26,6 +26,8 @@
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import jakarta.ws.rs.container.ConnectionCallback;
import jakarta.ws.rs.core.GenericType;
@@ -54,7 +56,7 @@
private final BlockingDeque<T> queue = new LinkedBlockingDeque<>();
private final byte[] chunkDelimiter;
private final AtomicBoolean resumed = new AtomicBoolean(false);
- private final Object lock = new Object();
+ private final Lock lock = new ReentrantLock();
// the following flushing and touchingEntityStream variables are used in a synchronized block exclusively
private boolean flushing = false;
@@ -202,7 +204,8 @@
boolean shouldClose;
T t;
- synchronized (lock) {
+ lock.lock();
+ try {
if (flushing) {
// if another thread is already flushing the queue, we don't have to do anything
return null;
@@ -220,13 +223,15 @@
// and they don't have to bother
flushing = true;
}
+ } finally {
+ lock.unlock();
}
while (t != null) {
try {
- synchronized (lock) {
- touchingEntityStream = true;
- }
+ lock.lock();
+ touchingEntityStream = true;
+ lock.unlock();
final OutputStream origStream = responseContext.getEntityStream();
final OutputStream writtenStream = requestContext.getWorkers().writeTo(
@@ -265,14 +270,15 @@
}
throw mpe;
} finally {
- synchronized (lock) {
- touchingEntityStream = false;
- }
+ lock.lock();
+ touchingEntityStream = false;
+ lock.unlock();
}
t = queue.poll();
if (t == null) {
- synchronized (lock) {
+ lock.lock();
+ try {
// queue seems empty
// check again in the synchronized block before clearing the flushing flag
// first remember the closed flag (this has to be before polling the queue,
@@ -290,6 +296,8 @@
flushing = shouldClose;
break;
}
+ } finally {
+ lock.unlock();
}
}
}
@@ -303,18 +311,20 @@
onClose(e);
} finally {
if (closed) {
+ lock.lock();
try {
- synchronized (lock) {
- if (!touchingEntityStream) {
- responseContext.close();
- } // else the next thread will close responseContext
- }
+ if (!touchingEntityStream) {
+ responseContext.close();
+ } // else the next thread will close responseContext
} catch (final Exception e) {
// if no exception remembered before, remember this one
// otherwise the previously remembered exception (from catch clause) takes precedence
ex = ex == null ? e : ex;
+ } finally {
+ lock.unlock();
}
+
requestScopeContext.release();
// rethrow remembered exception (if any)
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
index 6f74427..d6eb5a6 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
@@ -34,6 +34,8 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -800,7 +802,7 @@
}
};
- private final Object stateLock = new Object();
+ private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
private State state = RUNNING;
private boolean cancelled = false;
@@ -832,11 +834,13 @@
@Override
public void onTimeout(final ContainerResponseWriter responseWriter) {
final TimeoutHandler handler = timeoutHandler;
+ stateLock.readLock().lock();
try {
- synchronized (stateLock) {
- if (state == SUSPENDED) {
- handler.handleTimeout(this);
- }
+ if (state == SUSPENDED) {
+ stateLock.readLock().unlock(); // unlock before handleTimeout to prevent write lock in handleTimeout
+ handler.handleTimeout(this);
+ } else {
+ stateLock.readLock().unlock();
}
} catch (final Throwable throwable) {
resume(throwable);
@@ -845,9 +849,9 @@
@Override
public void onComplete(final Throwable throwable) {
- synchronized (stateLock) {
- state = COMPLETED;
- }
+ stateLock.writeLock().lock();
+ state = COMPLETED;
+ stateLock.writeLock().unlock();
}
@Override
@@ -875,14 +879,19 @@
@Override
public boolean suspend() {
- synchronized (stateLock) {
- if (state == RUNNING) {
- if (responder.processingContext.request().getResponseWriter().suspend(
- AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS, this)) {
- state = SUSPENDED;
- return true;
- }
+ stateLock.readLock().lock();
+ if (state == RUNNING) {
+ stateLock.readLock().unlock();
+ if (responder.processingContext.request().getResponseWriter().suspend(
+ AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS, this)) {
+ // Must release read lock before acquiring write lock
+ stateLock.writeLock().lock();
+ state = SUSPENDED;
+ stateLock.writeLock().unlock();
+ return true;
}
+ } else {
+ stateLock.readLock().unlock();
}
return false;
}
@@ -925,12 +934,17 @@
}
private boolean resume(final Runnable handler) {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
if (state != SUSPENDED) {
return false;
}
- state = RESUMED;
+ } finally {
+ stateLock.readLock().unlock();
}
+ stateLock.writeLock().lock();
+ state = RESUMED;
+ stateLock.writeLock().unlock();
try {
responder.runtime.requestScope.runInScope(requestContext, handler);
@@ -978,7 +992,8 @@
}
private boolean cancel(final Value<Response> responseValue) {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
if (cancelled) {
return true;
}
@@ -986,10 +1001,15 @@
if (state != SUSPENDED) {
return false;
}
- state = RESUMED;
- cancelled = true;
+ } finally {
+ stateLock.readLock().unlock();
}
+ stateLock.writeLock().lock();
+ state = RESUMED;
+ cancelled = true;
+ stateLock.writeLock().unlock();
+
responder.runtime.requestScope.runInScope(requestContext, new Runnable() {
@Override
public void run() {
@@ -1006,29 +1026,41 @@
}
public boolean isRunning() {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
return state == RUNNING;
+ } finally {
+ stateLock.readLock().unlock();
}
}
@Override
public boolean isSuspended() {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
return state == SUSPENDED;
+ } finally {
+ stateLock.readLock().unlock();
}
}
@Override
public boolean isCancelled() {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
return cancelled;
+ } finally {
+ stateLock.readLock().unlock();
}
}
@Override
public boolean isDone() {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
return state == COMPLETED;
+ } finally {
+ stateLock.readLock().unlock();
}
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
index 761065e..91ec565 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,6 +19,8 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -42,7 +44,7 @@
private ScheduledFuture<?> timeoutTask = null; // guarded by runtimeLock
private ContainerResponseWriter.TimeoutHandler timeoutHandler = null; // guarded by runtimeLock
private boolean suspended = false; // guarded by runtimeLock
- private final Object runtimeLock = new Object();
+ private final Lock runtimeLock = new ReentrantLock();
private final ContainerResponseWriter containerResponseWriter;
private final ScheduledExecutorService executor;
@@ -71,7 +73,8 @@
* @see ContainerResponseWriter#suspend(long, TimeUnit, ContainerResponseWriter.TimeoutHandler)
*/
public boolean suspend(final long timeOut, final TimeUnit unit, final TimeoutHandler handler) {
- synchronized (runtimeLock) {
+ runtimeLock.lock();
+ try {
if (suspended) {
return false;
}
@@ -81,6 +84,8 @@
containerResponseWriter.setSuspendTimeout(timeOut, unit);
return true;
+ } finally {
+ runtimeLock.unlock();
}
}
@@ -94,7 +99,8 @@
* @see ContainerResponseWriter#setSuspendTimeout(long, TimeUnit)
*/
public void setSuspendTimeout(final long timeOut, final TimeUnit unit) throws IllegalStateException {
- synchronized (runtimeLock) {
+ runtimeLock.lock();
+ try {
if (!suspended) {
throw new IllegalStateException(LocalizationMessages.SUSPEND_NOT_SUSPENDED());
}
@@ -110,18 +116,21 @@
@Override
public void run() {
+ runtimeLock.lock();
try {
- synchronized (runtimeLock) {
- timeoutHandler.onTimeout(containerResponseWriter);
- }
+ timeoutHandler.onTimeout(containerResponseWriter);
} catch (final Throwable throwable) {
LOGGER.log(Level.WARNING, LocalizationMessages.SUSPEND_HANDLER_EXECUTION_FAILED(), throwable);
+ } finally {
+ runtimeLock.unlock();
}
}
}, timeOut, unit);
} catch (final IllegalStateException ex) {
LOGGER.log(Level.WARNING, LocalizationMessages.SUSPEND_SCHEDULING_ERROR(), ex);
}
+ } finally {
+ runtimeLock.unlock();
}
}
@@ -132,10 +141,15 @@
close(false);
}
- private synchronized void close(final boolean interruptIfRunning) {
- if (timeoutTask != null) {
- timeoutTask.cancel(interruptIfRunning);
- timeoutTask = null;
+ private void close(final boolean interruptIfRunning) {
+ runtimeLock.lock();
+ try {
+ if (timeoutTask != null) {
+ timeoutTask.cancel(interruptIfRunning);
+ timeoutTask = null;
+ }
+ } finally {
+ runtimeLock.unlock();
}
}
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
index f8abbed..4e95a47 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Level;
@@ -54,7 +56,7 @@
private final Consumer<Binding> registerBinding;
private final Set<Class<?>> bindingCache;
- private final Object bindingCacheLock;
+ private final Lock bindingCacheLock;
private volatile ResourceModel resourceModel;
@@ -73,7 +75,7 @@
this.injectInstance = injectInstance;
this.registerBinding = registerBinding;
this.bindingCache = Collections.newSetFromMap(new IdentityHashMap<>());
- this.bindingCacheLock = new Object();
+ this.bindingCacheLock = new ReentrantLock();
}
@Override
@@ -110,11 +112,14 @@
return;
}
- synchronized (bindingCacheLock) {
+ bindingCacheLock.lock();
+ try {
if (bindingCache.contains(resourceClass)) {
return;
}
unsafeBindResource(resourceClass, null);
+ } finally {
+ bindingCacheLock.unlock();
}
}
@@ -135,7 +140,8 @@
return;
}
- synchronized (bindingCacheLock) {
+ bindingCacheLock.lock();
+ try {
if (bindingCache.contains(resourceClass)) {
return;
}
@@ -144,6 +150,8 @@
}
bindingCache.add(resourceClass);
+ } finally {
+ bindingCacheLock.unlock();
}
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
index a7d5829..dc99a5d 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -60,7 +62,7 @@
private volatile ResourcesMBeanGroup resourceClassStatsGroup;
private volatile ExceptionMapperMXBeanImpl exceptionMapperMXBean;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
- private final Object LOCK = new Object();
+ private final Lock LOCK = new ReentrantLock();
/**
* Name of domain that will prefix mbeans {@link ObjectName}. The code uses this
@@ -110,45 +112,47 @@
void registerMBean(Object mbean, String namePostfix) {
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
final String name = domain + namePostfix;
+ LOCK.lock();
try {
- synchronized (LOCK) {
- if (destroyed.get()) {
- // already destroyed
- return;
- }
- final ObjectName objectName = new ObjectName(name);
- if (mBeanServer.isRegistered(objectName)) {
-
- LOGGER.log(Level.WARNING,
- LocalizationMessages.WARNING_MONITORING_MBEANS_BEAN_ALREADY_REGISTERED(objectName));
- mBeanServer.unregisterMBean(objectName);
- }
- mBeanServer.registerMBean(mbean, objectName);
+ if (destroyed.get()) {
+ // already destroyed
+ return;
}
+ final ObjectName objectName = new ObjectName(name);
+ if (mBeanServer.isRegistered(objectName)) {
+
+ LOGGER.log(Level.WARNING,
+ LocalizationMessages.WARNING_MONITORING_MBEANS_BEAN_ALREADY_REGISTERED(objectName));
+ mBeanServer.unregisterMBean(objectName);
+ }
+ mBeanServer.registerMBean(mbean, objectName);
} catch (JMException e) {
throw new ProcessingException(LocalizationMessages.ERROR_MONITORING_MBEANS_REGISTRATION(name), e);
+ } finally {
+ LOCK.unlock();
}
}
private void unregisterJerseyMBeans(boolean destroy) {
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+ LOCK.lock();
try {
- synchronized (LOCK) {
- if (destroy) {
- destroyed.set(true); // do not register new beans since now
- }
+ if (destroy) {
+ destroyed.set(true); // do not register new beans since now
+ }
- if (domain == null) {
- // No bean has been registered yet.
- return;
- }
- final Set<ObjectName> names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null);
- for (ObjectName name : names) {
- mBeanServer.unregisterMBean(name);
- }
+ if (domain == null) {
+ // No bean has been registered yet.
+ return;
+ }
+ final Set<ObjectName> names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null);
+ for (ObjectName name : names) {
+ mBeanServer.unregisterMBean(name);
}
} catch (Exception e) {
throw new ProcessingException(LocalizationMessages.ERROR_MONITORING_MBEANS_UNREGISTRATION_DESTROY(), e);
+ } finally {
+ LOCK.unlock();
}
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
index 14e86e3..818bdbe 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,6 +19,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.NoSuchElementException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.logging.Level;
@@ -110,6 +112,7 @@
public InputStream open() {
//noinspection NullableProblems
return new InputStream() {
+ private final Lock markLock = new ReentrantLock();
@Override
public int read() throws IOException {
@@ -142,13 +145,23 @@
}
@Override
- public synchronized void mark(final int i) {
- jarInputStream.mark(i);
+ public void mark(final int i) {
+ markLock.lock();
+ try {
+ jarInputStream.mark(i);
+ } finally {
+ markLock.unlock();
+ }
}
@Override
- public synchronized void reset() throws IOException {
- jarInputStream.reset();
+ public void reset() throws IOException {
+ markLock.lock();
+ try {
+ jarInputStream.reset();
+ } finally {
+ markLock.unlock();
+ }
}
@Override
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
index 06a9432..d482dde 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -27,6 +27,8 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import org.glassfish.jersey.internal.OsgiRegistry;
import org.glassfish.jersey.internal.util.ReflectionHelper;
@@ -54,7 +56,7 @@
* URI schemes.
* <p>
* Further schemes may be registered by registering an implementation of
- * {@link UriSchemeResourceFinderFactory} in the META-INF/services file whose name is the
+ * {@link UriSchemeResourceFinderFactory} in the META-INF/services file whose name is
* the fully qualified class name of {@link UriSchemeResourceFinderFactory}.
* <p>
* If a URI scheme is not supported a {@link ResourceFinderException} will be thrown
@@ -195,13 +197,15 @@
public abstract static class ResourcesProvider {
private static volatile ResourcesProvider provider;
+ private static final Lock RESOURCE_PROVIDER_LOCK = new ReentrantLock();
private static ResourcesProvider getInstance() {
// Double-check idiom for lazy initialization
ResourcesProvider result = provider;
if (result == null) { // first check without locking
- synchronized (ResourcesProvider.class) {
+ RESOURCE_PROVIDER_LOCK.lock();
+ try {
result = provider;
if (result == null) { // second check with locking
provider = result = new ResourcesProvider() {
@@ -214,6 +218,8 @@
};
}
+ } finally {
+ RESOURCE_PROVIDER_LOCK.unlock();
}
}
@@ -226,9 +232,9 @@
final ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
security.checkPermission(rp);
}
- synchronized (ResourcesProvider.class) {
- ResourcesProvider.provider = provider;
- }
+ RESOURCE_PROVIDER_LOCK.lock();
+ ResourcesProvider.provider = provider;
+ RESOURCE_PROVIDER_LOCK.unlock();
}
/**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
index 5a7188a..982a44b 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@@ -56,6 +58,7 @@
private byte[] wadlXmlRepresentation;
private String lastModified;
+ private final Lock lock = new ReentrantLock();
@Context
private WadlApplicationContext wadlContext;
@@ -71,7 +74,8 @@
@Produces({"application/vnd.sun.wadl+xml", "application/xml"})
@GET
- public synchronized Response getWadl(@Context UriInfo uriInfo) {
+ public Response getWadl(@Context UriInfo uriInfo) {
+ lock.lock();
try {
if (!wadlContext.isWadlGenerationEnabled()) {
return Response.status(Response.Status.NOT_FOUND).build();
@@ -103,6 +107,8 @@
return Response.ok(new ByteArrayInputStream(wadlXmlRepresentation)).header("Last-modified", lastModified).build();
} catch (Exception e) {
throw new ProcessingException("Error generating /application.wadl.", e);
+ } finally {
+ lock.unlock();
}
}
@@ -110,9 +116,10 @@
@Produces({"application/xml"})
@GET
@Path("{path}")
- public synchronized Response getExternalGrammar(
+ public Response getExternalGrammar(
@Context UriInfo uriInfo,
@PathParam("path") String path) {
+ lock.lock();
try {
// Fail if wadl generation is disabled
if (!wadlContext.isWadlGenerationEnabled()) {
@@ -135,6 +142,8 @@
.build();
} catch (Exception e) {
throw new ProcessingException(LocalizationMessages.ERROR_WADL_RESOURCE_EXTERNAL_GRAMMAR(), e);
+ } finally {
+ lock.unlock();
}
}
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
index 1d3c5c5..0b95efd 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -57,6 +59,7 @@
private static final Map<Class<?>, WeakReference<JAXBContext>> jaxbContexts =
new WeakHashMap<Class<?>, WeakReference<JAXBContext>>();
+ private static final Lock jaxbContextsLock = new ReentrantLock();
private final Providers jaxrsProviders;
private final boolean fixedResolverMediaType;
private final Value<ContextResolver<JAXBContext>> mtContext;
@@ -149,7 +152,7 @@
/**
* Get the JAXB unmarshaller for the given class and media type.
* <p>
- * In case this provider instance has been {@link #AbstractJaxbProvider(Providers, MediaType)
+ * In case this provider instance has been {@link AbstractJaxbProvider(Providers, MediaType)
* created with a fixed resolver media type}, the supplied media type argument will be ignored.
* </p>
*
@@ -192,7 +195,7 @@
/**
* Get the JAXB marshaller for the given class and media type.
* <p>
- * In case this provider instance has been {@link #AbstractJaxbProvider(Providers, MediaType)
+ * In case this provider instance has been {@link AbstractJaxbProvider(Providers, MediaType)
* created with a fixed resolver media type}, the supplied media type argument will be ignored.
* </p>
*
@@ -280,7 +283,8 @@
* @throws JAXBException in case the JAXB context retrieval fails.
*/
protected JAXBContext getStoredJaxbContext(Class type) throws JAXBException {
- synchronized (jaxbContexts) {
+ jaxbContextsLock.lock();
+ try {
final WeakReference<JAXBContext> ref = jaxbContexts.get(type);
JAXBContext c = (ref != null) ? ref.get() : null;
if (c == null) {
@@ -288,7 +292,10 @@
jaxbContexts.put(type, new WeakReference<JAXBContext>(c));
}
return c;
+ } finally {
+ jaxbContextsLock.unlock();
}
+
}
/**
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
index e7e399d..7065c43 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -20,6 +20,8 @@
import java.lang.reflect.Type;
import java.util.Map;
import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.core.Context;
@@ -54,6 +56,7 @@
public class JaxbStringReaderProvider {
private static final Map<Class, JAXBContext> jaxbContexts = new WeakHashMap<Class, JAXBContext>();
+ private static final Lock jaxbContextsLock = new ReentrantLock();
private final Value<ContextResolver<JAXBContext>> mtContext;
private final Value<ContextResolver<Unmarshaller>> mtUnmarshaller;
@@ -116,13 +119,16 @@
* @throws JAXBException in case JAXB context retrieval fails.
*/
protected JAXBContext getStoredJAXBContext(Class type) throws JAXBException {
- synchronized (jaxbContexts) {
+ jaxbContextsLock.lock();
+ try {
JAXBContext c = jaxbContexts.get(type);
if (c == null) {
c = JAXBContext.newInstance(type);
jaxbContexts.put(type, c);
}
return c;
+ } finally {
+ jaxbContextsLock.unlock();
}
}