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 047bba811b..3d07d3f979 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 class TypedInstances { private final MultivaluedMap> singletonInstances = new MultivaluedHashMap<>(); private final ThreadLocal>> threadInstances = new ThreadLocal<>(); private final List threadPredestroyables = Collections.synchronizedList(new LinkedList<>()); + private final ReentrantLock singletonInstancesLock = new ReentrantLock(); private List> _getSingletons(TYPE clazz) { List> si; - synchronized (singletonInstances) { + singletonInstancesLock.lock(); + try { si = singletonInstances.get(clazz); + } finally { + singletonInstancesLock.unlock(); } return si; } @SuppressWarnings("unchecked") 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> values = singletonInstances.get(clazz); if (values != null) { @@ -118,6 +124,8 @@ T _addSingleton(TYPE clazz, T instance, Binding binding, Annotation[] 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 cb7545f8ff..77ab1e1c21 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 @@ public final class OsgiRegistry implements SynchronousBundleListener { private final Map>>>> factories = new HashMap>>>>(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private static final Lock INSTANCE_LOCK = new ReentrantLock(); private static OsgiRegistry instance; @@ -90,16 +93,21 @@ public final class OsgiRegistry implements SynchronousBundleListener { * * @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 46184d8d09..03d15d31ec 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 @@ private void handleClassNotFoundException() throws ServiceConfigurationError { 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 @@ private static void setInstance(final ServiceIteratorProvider sip) throws Securi 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 47616447a8..5c3a7470e6 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 @@ public T get() { private static class LazyValueImpl implements LazyValue { - private final Object lock; + private final Lock lock; private final Value delegate; private volatile Value value; public LazyValueImpl(final Value delegate) { this.delegate = delegate; - this.lock = new Object(); + this.lock = new ReentrantLock(); } @Override public T get() { Value 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 @@ public static LazyUnsafeValue lazy(final UnsafeVa private static class LazyUnsafeValueImpl implements LazyUnsafeValue { - private final Object lock; + private final Lock lock; private final UnsafeValue delegate; private volatile UnsafeValue value; public LazyUnsafeValueImpl(final UnsafeValue delegate) { this.delegate = delegate; - this.lock = new Object(); + this.lock = new ReentrantLock(); } @Override public T get() throws E { UnsafeValue result = value; if (result == null) { - synchronized (lock) { + lock.lock(); + try { result = value; //noinspection ConstantConditions if (result == null) { @@ -406,6 +413,8 @@ public T get() throws E { } 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 75479e6846..0f76eeddae 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 HttpDateFormat() { private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT"); - private static final ThreadLocal> dateFormats = new ThreadLocal>() { - - @Override - protected synchronized List initialValue() { - return createDateFormats(); - } - }; + private static final ThreadLocal> dateFormats = ThreadLocal.withInitial(() -> createDateFormats()); private static List 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 fe5a2497f1..62df83fdc1 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 StringListReader() { private abstract static class ListReader { private final LRU> LIST_CACHE = LRU.create(); + private final Lock lock = new ReentrantLock(); protected final ListElementCreator creator; protected ListReader(ListElementCreator creator) { @@ -639,7 +642,8 @@ private List readList(final List l, final String header) List 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 @@ private List readList(final List l, final String header) } 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 91738c15fb..7a1a4a5c1c 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 static class UnCloseableInputStream extends InputStream { 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 @@ public int available() throws IOException { } @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 ced41cdbb1..4d92447905 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 resume(RequestContext context) { */ 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 2ce0542b22..b671e68665 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 @@ public class ChunkedOutput extends GenericType implements Closeable { private final BlockingDeque 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 @@ public Void call() throws IOException { 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 @@ public Void call() throws IOException { // 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 @@ public Void call() throws IOException { } 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 @@ public Void call() throws IOException { flushing = shouldClose; break; } + } finally { + lock.unlock(); } } } @@ -303,18 +311,20 @@ public Void call() throws IOException { 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 6f744275b1..d6eb5a6ba7 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 @@ public void handleTimeout(final AsyncResponse asyncResponse) { } }; - private final Object stateLock = new Object(); + private final ReadWriteLock stateLock = new ReentrantReadWriteLock(); private State state = RUNNING; private boolean cancelled = false; @@ -832,11 +834,13 @@ public AsyncResponder(final Responder responder, @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 @@ public void onTimeout(final ContainerResponseWriter responseWriter) { @Override public void onComplete(final Throwable throwable) { - synchronized (stateLock) { - state = COMPLETED; - } + stateLock.writeLock().lock(); + state = COMPLETED; + stateLock.writeLock().unlock(); } @Override @@ -875,14 +879,19 @@ public void run() { @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 @@ public void run() { } 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 @@ public Response get() { } private boolean cancel(final Value responseValue) { - synchronized (stateLock) { + stateLock.readLock().lock(); + try { if (cancelled) { return true; } @@ -986,10 +1001,15 @@ private boolean cancel(final Value responseValue) { 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 void run() { } 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 761065e2dd..91ec5659f7 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 @@ public class JerseyRequestTimeoutHandler { 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 @@ public JerseyRequestTimeoutHandler(final ContainerResponseWriter containerRespon * @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 @@ public boolean suspend(final long timeOut, final TimeUnit unit, final TimeoutHan containerResponseWriter.setSuspendTimeout(timeOut, unit); return true; + } finally { + runtimeLock.unlock(); } } @@ -94,7 +99,8 @@ public boolean suspend(final long timeOut, final TimeUnit unit, final TimeoutHan * @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 @@ public void setSuspendTimeout(final long timeOut, final TimeUnit unit) throws Il @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 @@ public void close() { 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 f8abbed06a..4e95a47624 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 @@ public class JerseyResourceContext implements ExtendedResourceContext { private final Consumer registerBinding; private final Set> bindingCache; - private final Object bindingCacheLock; + private final Lock bindingCacheLock; private volatile ResourceModel resourceModel; @@ -73,7 +75,7 @@ public JerseyResourceContext( 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 @@ public void bindResource(Class resourceClass) { return; } - synchronized (bindingCacheLock) { + bindingCacheLock.lock(); + try { if (bindingCache.contains(resourceClass)) { return; } unsafeBindResource(resourceClass, null); + } finally { + bindingCacheLock.unlock(); } } @@ -135,7 +140,8 @@ public void bindResourceIfSingleton(T resource) { return; } - synchronized (bindingCacheLock) { + bindingCacheLock.lock(); + try { if (bindingCache.contains(resourceClass)) { return; } @@ -144,6 +150,8 @@ public void bindResourceIfSingleton(T resource) { } 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 a7d58297a2..dc99a5d2f0 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 @@ public class MBeanExposer extends AbstractContainerLifecycleListener implements 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 @@ static String convertToObjectName(String name, boolean isUri) { 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 (domain == null) { - // No bean has been registered yet. - return; - } - final Set names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null); - for (ObjectName name : names) { - mBeanServer.unregisterMBean(name); - } + if (destroy) { + destroyed.set(true); // do not register new beans since now + } + + if (domain == null) { + // No bean has been registered yet. + return; + } + final Set 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 14e86e3eb5..818bdbebb9 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 void reset() { public InputStream open() { //noinspection NullableProblems return new InputStream() { + private final Lock markLock = new ReentrantLock(); @Override public int read() throws IOException { @@ -142,13 +145,23 @@ public void close() throws IOException { } @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 06a9432a30..d482dde6ef 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. *

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

* If a URI scheme is not supported a {@link ResourceFinderException} will be thrown @@ -195,13 +197,15 @@ private void init() { 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 @@ public Enumeration getResources(final String name, final ClassLoader cl) }; } + } finally { + RESOURCE_PROVIDER_LOCK.unlock(); } } @@ -226,9 +232,9 @@ private static void setInstance(final ResourcesProvider provider) throws Securit 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 5a7188a2e0..982a44b69d 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 @@ public final class WadlResource { private byte[] wadlXmlRepresentation; private String lastModified; + private final Lock lock = new ReentrantLock(); @Context private WadlApplicationContext wadlContext; @@ -71,7 +74,8 @@ private boolean isCached(UriInfo uriInfo, boolean detailedWadl) { @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 @@ public synchronized Response getWadl(@Context UriInfo uriInfo) { 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 @@ public synchronized Response getWadl(@Context UriInfo uriInfo) { @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 @@ public synchronized Response getExternalGrammar( .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 1d3c5c5425..0b95efd483 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 @@ public abstract class AbstractJaxbProvider extends AbstractMessageReaderWrite private static final Map, WeakReference> jaxbContexts = new WeakHashMap, WeakReference>(); + private static final Lock jaxbContextsLock = new ReentrantLock(); private final Providers jaxrsProviders; private final boolean fixedResolverMediaType; private final Value> mtContext; @@ -149,7 +152,7 @@ protected boolean isSupported(MediaType mediaType) { /** * Get the JAXB unmarshaller for the given class and media type. *

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

* @@ -192,7 +195,7 @@ private Unmarshaller getUnmarshaller(Class type) throws JAXBException { /** * Get the JAXB marshaller for the given class and media type. *

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

* @@ -280,7 +283,8 @@ private JAXBContext getJAXBContext(Class type) throws JAXBException { * @throws JAXBException in case the JAXB context retrieval fails. */ protected JAXBContext getStoredJaxbContext(Class type) throws JAXBException { - synchronized (jaxbContexts) { + jaxbContextsLock.lock(); + try { final WeakReference ref = jaxbContexts.get(type); JAXBContext c = (ref != null) ? ref.get() : null; if (c == null) { @@ -288,7 +292,10 @@ protected JAXBContext getStoredJaxbContext(Class type) throws JAXBException { jaxbContexts.put(type, new WeakReference(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 e7e399d961..7065c43ea8 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 jaxbContexts = new WeakHashMap(); + private static final Lock jaxbContextsLock = new ReentrantLock(); private final Value> mtContext; private final Value> mtUnmarshaller; @@ -116,13 +119,16 @@ private JAXBContext getJAXBContext(Class type) throws JAXBException { * @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(); } }