GH-8643: Replace synchronized with Lock

Fixes https://github.com/spring-projects/spring-integration/issues/8643

* First pass - trivial synchronized blocks
  - Convert the "trivial" `synchronized` block into `ReentrantLock`.

* fix checkstyle

* use blocking lock

* Secon pass - handle multi-lock cases

* javadoc + year

* addres first batch of review suggestions

* fix checkstyle issues

* fix the mqtt parent/child lock monitor sharing

* fix the mqtt parent/child lock monitor sharing, v2

* patch the stomp test
This commit is contained in:
Christian Tzolov
2023-06-21 19:25:45 +02:00
committed by GitHub
parent 9e9bfd0c5c
commit c38ed96ee9
101 changed files with 2658 additions and 1264 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.ReturnedMessage;
@@ -58,6 +60,7 @@ import org.springframework.util.StringUtils;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 4.3
*
@@ -115,6 +118,8 @@ public abstract class AbstractAmqpOutboundEndpoint extends AbstractReplyProducin
private volatile ScheduledFuture<?> confirmChecker;
private final Lock lock = new ReentrantLock();
/**
* Set a custom {@link AmqpHeaderMapper} for mapping request and reply headers.
* Defaults to {@link DefaultAmqpHeaderMapper#outboundMapper()}.
@@ -336,8 +341,14 @@ public abstract class AbstractAmqpOutboundEndpoint extends AbstractReplyProducin
this.confirmTimeout = Duration.ofMillis(confirmTimeout); // NOSONAR sync inconsistency
}
protected final synchronized void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
protected final void setConnectionFactory(ConnectionFactory connectionFactory) {
this.lock.lock();
try {
this.connectionFactory = connectionFactory;
}
finally {
this.lock.unlock();
}
}
protected String getExchangeName() {
@@ -487,26 +498,33 @@ public abstract class AbstractAmqpOutboundEndpoint extends AbstractReplyProducin
}
@Override
public synchronized void start() {
if (!this.running) {
if (!this.lazyConnect && this.connectionFactory != null) {
try {
Connection connection = this.connectionFactory.createConnection(); // NOSONAR (close)
if (connection != null) {
connection.close();
public void start() {
this.lock.lock();
try {
if (!this.running) {
if (!this.lazyConnect && this.connectionFactory != null) {
try {
Connection connection = this.connectionFactory.createConnection(); // NOSONAR (close)
if (connection != null) {
connection.close();
}
}
catch (RuntimeException ex) {
logger.error(ex, "Failed to eagerly establish the connection.");
}
}
catch (RuntimeException ex) {
logger.error(ex, "Failed to eagerly establish the connection.");
doStart();
if (this.confirmTimeout != null && getConfirmNackChannel() != null && getRabbitTemplate() != null) {
this.confirmChecker = getTaskScheduler()
.scheduleAtFixedRate(checkUnconfirmed(), this.confirmTimeout.dividedBy(2L));
}
this.running = true;
}
doStart();
if (this.confirmTimeout != null && getConfirmNackChannel() != null && getRabbitTemplate() != null) {
this.confirmChecker = getTaskScheduler()
.scheduleAtFixedRate(checkUnconfirmed(), this.confirmTimeout.dividedBy(2L));
}
this.running = true;
}
finally {
this.lock.unlock();
}
}
private Runnable checkUnconfirmed() {
@@ -526,14 +544,20 @@ public abstract class AbstractAmqpOutboundEndpoint extends AbstractReplyProducin
protected abstract RabbitTemplate getRabbitTemplate();
@Override
public synchronized void stop() {
if (this.running) {
doStop();
public void stop() {
this.lock.lock();
try {
if (this.running) {
doStop();
}
this.running = false;
if (this.confirmChecker != null) {
this.confirmChecker.cancel(false);
this.confirmChecker = null;
}
}
this.running = false;
if (this.confirmChecker != null) {
this.confirmChecker.cancel(false);
this.confirmChecker = null;
finally {
this.lock.unlock();
}
}

View File

@@ -26,6 +26,8 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import io.micrometer.observation.ObservationRegistry;
@@ -70,6 +72,7 @@ import org.springframework.util.StringUtils;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
@IntegrationManagedResource
public abstract class AbstractMessageChannel extends IntegrationObjectSupport
@@ -475,6 +478,8 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
*/
protected static class ChannelInterceptorList {
private final Lock lock = new ReentrantLock();
protected final List<ChannelInterceptor> interceptors = new CopyOnWriteArrayList<>(); // NOSONAR
private final LogAccessor logger;
@@ -486,11 +491,15 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
}
public boolean set(List<ChannelInterceptor> interceptors) {
synchronized (this.interceptors) {
this.lock.lock();
try {
this.interceptors.clear();
this.size = interceptors.size();
return this.interceptors.addAll(interceptors);
}
finally {
this.lock.unlock();
}
}
public int getSize() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.support.channel.HeaderChannelRegistry;
@@ -44,6 +46,7 @@ import org.springframework.util.Assert;
* @author Gary Russell
* @author Artem Bilan
* @author Trung Pham
* @author Christian Tzolov
*
* @since 3.0
*
@@ -69,6 +72,8 @@ public class DefaultHeaderChannelRegistry extends IntegrationObjectSupport
private volatile boolean explicitlyStopped;
private final Lock lock = new ReentrantLock();
/**
* Construct a registry with the default delay for channel expiry.
*/
@@ -120,25 +125,37 @@ public class DefaultHeaderChannelRegistry extends IntegrationObjectSupport
}
@Override
public synchronized void start() {
if (!this.running) {
Assert.notNull(getTaskScheduler(), "a task scheduler is required");
this.reaperScheduledFuture =
getTaskScheduler()
.schedule(this, Instant.now().plusMillis(this.reaperDelay));
public void start() {
this.lock.lock();
try {
if (!this.running) {
Assert.notNull(getTaskScheduler(), "a task scheduler is required");
this.reaperScheduledFuture = getTaskScheduler()
.schedule(this, Instant.now().plusMillis(this.reaperDelay));
this.running = true;
this.running = true;
}
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void stop() {
this.running = false;
if (this.reaperScheduledFuture != null) {
this.reaperScheduledFuture.cancel(true);
this.reaperScheduledFuture = null;
public void stop() {
this.lock.lock();
try {
this.running = false;
if (this.reaperScheduledFuture != null) {
this.reaperScheduledFuture.cancel(true);
this.reaperScheduledFuture = null;
}
this.explicitlyStopped = true;
}
this.explicitlyStopped = true;
finally {
this.lock.unlock();
}
}
public void stop(Runnable callback) {
@@ -200,35 +217,45 @@ public class DefaultHeaderChannelRegistry extends IntegrationObjectSupport
* Cancel the scheduled reap task and run immediately; then reschedule.
*/
@Override
public synchronized void runReaper() {
if (this.reaperScheduledFuture != null) {
this.reaperScheduledFuture.cancel(true);
this.reaperScheduledFuture = null;
}
public void runReaper() {
this.lock.lock();
try {
if (this.reaperScheduledFuture != null) {
this.reaperScheduledFuture.cancel(true);
this.reaperScheduledFuture = null;
}
run();
run();
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void run() {
logger.trace(() -> "Reaper started; channels size=" + this.channels.size());
Iterator<Entry<String, MessageChannelWrapper>> iterator = this.channels.entrySet().iterator();
long now = System.currentTimeMillis();
while (iterator.hasNext()) {
Entry<String, MessageChannelWrapper> entry = iterator.next();
if (entry.getValue().expireAt() < now) {
logger.debug(() -> "Expiring " + entry.getKey() + " (" + entry.getValue().channel() + ")");
iterator.remove();
public void run() {
this.lock.lock();
try {
logger.trace(() -> "Reaper started; channels size=" + this.channels.size());
Iterator<Entry<String, MessageChannelWrapper>> iterator = this.channels.entrySet().iterator();
long now = System.currentTimeMillis();
while (iterator.hasNext()) {
Entry<String, MessageChannelWrapper> entry = iterator.next();
if (entry.getValue().expireAt() < now) {
logger.debug(() -> "Expiring " + entry.getKey() + " (" + entry.getValue().channel() + ")");
iterator.remove();
}
}
this.reaperScheduledFuture = getTaskScheduler()
.schedule(this, Instant.now().plusMillis(this.reaperDelay));
logger.trace(() -> "Reaper completed; channels size=" + this.channels.size());
}
finally {
this.lock.unlock();
}
this.reaperScheduledFuture =
getTaskScheduler()
.schedule(this, Instant.now().plusMillis(this.reaperDelay));
logger.trace(() -> "Reaper completed; channels size=" + this.channels.size());
}
protected record MessageChannelWrapper(MessageChannel channel, long expireAt) {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.config;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.aopalliance.aop.Advice;
import org.apache.commons.logging.Log;
@@ -58,6 +60,7 @@ import org.springframework.util.CollectionUtils;
* @author Gary Russell
* @author Artem Bilan
* @author David Liu
* @author Christian Tzolov
*/
public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageHandler>
implements FactoryBean<MessageHandler>, ApplicationContextAware, BeanFactoryAware, BeanNameAware,
@@ -65,7 +68,7 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
protected final Log logger = LogFactory.getLog(getClass()); //NOSONAR protected with final
private final Object initializationMonitor = new Object();
private final Lock initializationMonitor = new ReentrantLock();
private BeanFactory beanFactory;
@@ -192,7 +195,8 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
}
protected final H createHandlerInternal() {
synchronized (this.initializationMonitor) {
this.initializationMonitor.lock();
try {
if (this.initialized) {
// There was a problem when this method was called already
return null;
@@ -228,6 +232,9 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
this.order, theOrder -> ((Orderable) this.handler).setOrder(theOrder));
this.initialized = true;
}
finally {
this.initializationMonitor.unlock();
}
initializingBean();
return this.handler;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.config;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import org.aopalliance.aop.Advice;
@@ -77,6 +79,7 @@ import org.springframework.util.StringUtils;
* @author Josh Long
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class ConsumerEndpointFactoryBean
implements FactoryBean<AbstractEndpoint>, BeanFactoryAware, BeanNameAware, BeanClassLoaderAware,
@@ -84,9 +87,9 @@ public class ConsumerEndpointFactoryBean
private static final LogAccessor LOGGER = new LogAccessor(LogFactory.getLog(ConsumerEndpointFactoryBean.class));
private final Object initializationMonitor = new Object();
private final Lock initializationMonitor = new ReentrantLock();
private final Object handlerMonitor = new Object();
private final Lock handlerMonitor = new ReentrantLock();
private MessageHandler handler;
@@ -127,7 +130,8 @@ public class ConsumerEndpointFactoryBean
public void setHandler(Object handler) {
Assert.isTrue(handler instanceof MessageHandler || handler instanceof ReactiveMessageHandler,
"'handler' must be an instance of 'MessageHandler' or 'ReactiveMessageHandler'");
synchronized (this.handlerMonitor) {
this.handlerMonitor.lock();
try {
Assert.isNull(this.handler, "handler cannot be overridden");
if (handler instanceof ReactiveMessageHandler) {
this.handler = new ReactiveMessageHandlerAdapter((ReactiveMessageHandler) handler);
@@ -136,6 +140,9 @@ public class ConsumerEndpointFactoryBean
this.handler = (MessageHandler) handler;
}
}
finally {
this.handlerMonitor.unlock();
}
}
public MessageHandler getHandler() {
@@ -303,7 +310,8 @@ public class ConsumerEndpointFactoryBean
}
private void initializeEndpoint() {
synchronized (this.initializationMonitor) {
this.initializationMonitor.lock();
try {
if (this.initialized) {
return;
}
@@ -340,6 +348,9 @@ public class ConsumerEndpointFactoryBean
this.endpoint.afterPropertiesSet();
this.initialized = true;
}
finally {
this.initializationMonitor.unlock();
}
}
private MessageChannel resolveInputChannel() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.integration.config;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -39,11 +41,14 @@ import org.springframework.util.ReflectionUtils;
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0.4
*/
public final class IdGeneratorConfigurer implements ApplicationListener<ApplicationContextEvent> {
private final Lock lock = new ReentrantLock();
private static final Set<String> GENERATOR_CONTEXT_ID = new HashSet<>();
private static volatile IdGenerator theIdGenerator;
@@ -51,21 +56,28 @@ public final class IdGeneratorConfigurer implements ApplicationListener<Applicat
private final Log logger = LogFactory.getLog(getClass());
@Override
public synchronized void onApplicationEvent(ApplicationContextEvent event) {
ApplicationContext context = event.getApplicationContext();
if (event instanceof ContextRefreshedEvent) {
boolean contextHasIdGenerator = context.getBeanNamesForType(IdGenerator.class).length > 0;
if (contextHasIdGenerator && setIdGenerator(context)) {
IdGeneratorConfigurer.GENERATOR_CONTEXT_ID.add(context.getId());
public void onApplicationEvent(ApplicationContextEvent event) {
this.lock.lock();
try {
ApplicationContext context = event.getApplicationContext();
if (event instanceof ContextRefreshedEvent) {
boolean contextHasIdGenerator = context.getBeanNamesForType(IdGenerator.class).length > 0;
if (contextHasIdGenerator && setIdGenerator(context)) {
IdGeneratorConfigurer.GENERATOR_CONTEXT_ID.add(context.getId());
}
}
else if (event instanceof ContextClosedEvent
&& IdGeneratorConfigurer.GENERATOR_CONTEXT_ID.contains(context.getId())) {
if (IdGeneratorConfigurer.GENERATOR_CONTEXT_ID.size() == 1) {
unsetIdGenerator();
}
IdGeneratorConfigurer.GENERATOR_CONTEXT_ID.remove(context.getId());
}
}
else if (event instanceof ContextClosedEvent
&& IdGeneratorConfigurer.GENERATOR_CONTEXT_ID.contains(context.getId())) {
if (IdGeneratorConfigurer.GENERATOR_CONTEXT_ID.size() == 1) {
unsetIdGenerator();
}
IdGeneratorConfigurer.GENERATOR_CONTEXT_ID.remove(context.getId());
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.aop.Advisor;
import org.springframework.aop.TargetSource;
@@ -38,6 +40,8 @@ import org.springframework.util.PatternMatchUtils;
*
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 4.1
*/
@SuppressWarnings("serial")
@@ -47,6 +51,8 @@ class IdempotentReceiverAutoProxyCreator extends AbstractAutoProxyCreator {
private volatile Map<String, List<String>> idempotentEndpoints; // double check locking requires volatile
private final Lock lock = new ReentrantLock();
public void setIdempotentEndpointsMapping(List<Map<String, String>> idempotentEndpointsMapping) {
Assert.notEmpty(idempotentEndpointsMapping, "'idempotentEndpointsMapping' must not be empty");
this.idempotentEndpointsMapping = idempotentEndpointsMapping; //NOSONAR (inconsistent sync)
@@ -85,8 +91,9 @@ class IdempotentReceiverAutoProxyCreator extends AbstractAutoProxyCreator {
}
private void initIdempotentEndpointsIfNecessary() {
if (this.idempotentEndpoints == null) { //NOSONAR (inconsistent sync)
synchronized (this) {
if (this.idempotentEndpoints == null) { // NOSONAR (inconsistent sync)
this.lock.lock();
try {
if (this.idempotentEndpoints == null) {
this.idempotentEndpoints = new LinkedHashMap<String, List<String>>();
for (Map<String, String> mapping : this.idempotentEndpointsMapping) {
@@ -104,6 +111,9 @@ class IdempotentReceiverAutoProxyCreator extends AbstractAutoProxyCreator {
}
}
}
finally {
this.lock.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.integration.config;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
@@ -41,11 +44,12 @@ import org.springframework.util.StringUtils;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class SourcePollingChannelAdapterFactoryBean implements FactoryBean<SourcePollingChannelAdapter>,
BeanFactoryAware, BeanNameAware, BeanClassLoaderAware, InitializingBean, SmartLifecycle, DisposableBean {
private final Object initializationMonitor = new Object();
private final Lock initializationMonitor = new ReentrantLock();
private MessageSource<?> source;
@@ -158,7 +162,8 @@ public class SourcePollingChannelAdapterFactoryBean implements FactoryBean<Sourc
}
private void initializeAdapter() {
synchronized (this.initializationMonitor) {
this.initializationMonitor.lock();
try {
if (this.initialized) {
return;
}
@@ -207,6 +212,9 @@ public class SourcePollingChannelAdapterFactoryBean implements FactoryBean<Sourc
this.adapter = spca;
this.initialized = true;
}
finally {
this.initializationMonitor.unlock();
}
}
/*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.config.xml;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -38,10 +40,14 @@ import org.springframework.util.StringUtils;
*
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 3.0
*/
public class SpelPropertyAccessorsParser implements BeanDefinitionParser {
private final Lock lock = new ReentrantLock();
private final Map<String, Object> propertyAccessors = new ManagedMap<String, Object>();
@Override
@@ -86,17 +92,23 @@ public class SpelPropertyAccessorsParser implements BeanDefinitionParser {
return null;
}
private synchronized void initializeSpelPropertyAccessorRegistrarIfNecessary(ParserContext parserContext) {
if (!parserContext.getRegistry()
.containsBeanDefinition(IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME)) {
private void initializeSpelPropertyAccessorRegistrarIfNecessary(ParserContext parserContext) {
this.lock.lock();
try {
if (!parserContext.getRegistry()
.containsBeanDefinition(IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME)) {
BeanDefinitionBuilder registrarBuilder = BeanDefinitionBuilder
.genericBeanDefinition(SpelPropertyAccessorRegistrar.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.addConstructorArgValue(this.propertyAccessors);
parserContext.getRegistry()
.registerBeanDefinition(IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME,
registrarBuilder.getBeanDefinition());
BeanDefinitionBuilder registrarBuilder = BeanDefinitionBuilder
.genericBeanDefinition(SpelPropertyAccessorRegistrar.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.addConstructorArgValue(this.propertyAccessors);
parserContext.getRegistry()
.registerBeanDefinition(IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME,
registrarBuilder.getBeanDefinition());
}
}
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.integration.core;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.integration.context.IntegrationContextUtils;
@@ -31,12 +34,15 @@ import org.springframework.messaging.core.GenericMessagingTemplate;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 1.0
*
*/
public class MessagingTemplate extends GenericMessagingTemplate {
private final Lock lock = new ReentrantLock();
private BeanFactory beanFactory;
private volatile boolean throwExceptionOnLateReplySet;
@@ -84,15 +90,19 @@ public class MessagingTemplate extends GenericMessagingTemplate {
@Nullable
public Message<?> sendAndReceive(MessageChannel destination, Message<?> requestMessage) {
if (!this.throwExceptionOnLateReplySet) {
synchronized (this) {
this.lock.lock();
try {
if (!this.throwExceptionOnLateReplySet) {
IntegrationProperties integrationProperties =
IntegrationContextUtils.getIntegrationProperties(this.beanFactory);
IntegrationProperties integrationProperties = IntegrationContextUtils
.getIntegrationProperties(this.beanFactory);
super.setThrowExceptionOnLateReply(
integrationProperties.isMessagingTemplateThrowExceptionOnLateReply());
this.throwExceptionOnLateReplySet = true;
}
}
finally {
this.lock.unlock();
}
}
return super.sendAndReceive(destination, requestMessage);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.dispatcher;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -41,6 +43,7 @@ import org.springframework.util.Assert;
* @author Gary Russell
* @author Diego Belfer
* @author Artem Bilan
* @author Christian Tzolov
*/
public abstract class AbstractDispatcher implements MessageDispatcher {
@@ -52,6 +55,8 @@ public abstract class AbstractDispatcher implements MessageDispatcher {
private volatile MessageHandler theOneHandler;
private final Lock lock = new ReentrantLock();
/**
* Set the maximum subscribers allowed by this dispatcher.
* @param maxSubscribers The maximum number of subscribers allowed.
@@ -77,17 +82,23 @@ public abstract class AbstractDispatcher implements MessageDispatcher {
* @return the result of {@link Set#add(Object)}
*/
@Override
public synchronized boolean addHandler(MessageHandler handler) {
Assert.notNull(handler, "handler must not be null");
Assert.isTrue(this.handlers.size() < this.maxSubscribers, "Maximum subscribers exceeded");
boolean added = this.handlers.add(handler);
if (this.handlers.size() == 1) {
this.theOneHandler = handler;
public boolean addHandler(MessageHandler handler) {
this.lock.lock();
try {
Assert.notNull(handler, "handler must not be null");
Assert.isTrue(this.handlers.size() < this.maxSubscribers, "Maximum subscribers exceeded");
boolean added = this.handlers.add(handler);
if (this.handlers.size() == 1) {
this.theOneHandler = handler;
}
else {
this.theOneHandler = null;
}
return added;
}
else {
this.theOneHandler = null;
finally {
this.lock.unlock();
}
return added;
}
/**
@@ -96,16 +107,22 @@ public abstract class AbstractDispatcher implements MessageDispatcher {
* @return the result of {@link Set#remove(Object)}
*/
@Override
public synchronized boolean removeHandler(MessageHandler handler) {
Assert.notNull(handler, "handler must not be null");
boolean removed = this.handlers.remove(handler);
if (this.handlers.size() == 1) {
this.theOneHandler = this.handlers.iterator().next();
public boolean removeHandler(MessageHandler handler) {
this.lock.lock();
try {
Assert.notNull(handler, "handler must not be null");
boolean removed = this.handlers.remove(handler);
if (this.handlers.size() == 1) {
this.theOneHandler = this.handlers.iterator().next();
}
else {
this.theOneHandler = null;
}
return removed;
}
else {
this.theOneHandler = null;
finally {
this.lock.unlock();
}
return removed;
}
protected boolean tryOptimizedDispatch(Message<?> message) {

View File

@@ -25,6 +25,8 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import org.springframework.integration.util.ErrorHandlingTaskExecutor;
@@ -49,6 +51,7 @@ import org.springframework.util.ErrorHandler;
* The rest of the logic is similar to {@link UnicastingDispatcher} behavior.
*
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 6.1
*/
@@ -73,6 +76,8 @@ public class PartitionedDispatcher extends AbstractDispatcher {
private MessageHandlingTaskDecorator messageHandlingTaskDecorator = task -> task;
private final Lock lock = new ReentrantLock();
/**
* Instantiate based on a provided number of partitions and function for partition key against
* the message to dispatch.
@@ -153,7 +158,8 @@ public class PartitionedDispatcher extends AbstractDispatcher {
private void populatedPartitions() {
if (this.partitions.isEmpty()) {
synchronized (this.partitions) {
this.lock.lock();
try {
if (this.partitions.isEmpty()) {
Map<Integer, UnicastingDispatcher> partitionsToUse = new HashMap<>();
for (int i = 0; i < this.partitionCount; i++) {
@@ -162,6 +168,9 @@ public class PartitionedDispatcher extends AbstractDispatcher {
this.partitions.putAll(partitionsToUse);
}
}
finally {
this.lock.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.aopalliance.aop.Advice;
@@ -78,6 +80,7 @@ import org.springframework.util.ReflectionUtils;
* @author Gary Russell
* @author Artem Bilan
* @author Andreas Baer
* @author Christian Tzolov
*/
public abstract class AbstractPollingEndpoint extends AbstractEndpoint implements BeanClassLoaderAware {
@@ -88,7 +91,7 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
private final Collection<Advice> appliedAdvices = new HashSet<>();
private final Object initializationMonitor = new Object();
private final Lock initializationMonitor = new ReentrantLock();
private Executor taskExecutor = new SyncTaskExecutor();
@@ -262,7 +265,8 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
@Override
protected void onInit() {
synchronized (this.initializationMonitor) {
this.initializationMonitor.lock();
try {
if (this.initialized) {
return;
}
@@ -280,6 +284,9 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
}
this.initialized = true;
}
finally {
this.initializationMonitor.unlock();
}
try {
super.onInit();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.endpoint;
import java.lang.reflect.Method;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.Lifecycle;
import org.springframework.integration.support.management.ManageableLifecycle;
@@ -31,6 +33,7 @@ import org.springframework.util.ReflectionUtils;
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class MethodInvokingMessageSource extends AbstractMessageSource<Object> implements ManageableLifecycle {
@@ -42,7 +45,7 @@ public class MethodInvokingMessageSource extends AbstractMessageSource<Object> i
private volatile boolean initialized;
private final Object initializationMonitor = new Object();
private final Lock initializationMonitor = new ReentrantLock();
public void setObject(Object object) {
@@ -67,7 +70,8 @@ public class MethodInvokingMessageSource extends AbstractMessageSource<Object> i
@Override
protected void onInit() {
synchronized (this.initializationMonitor) {
this.initializationMonitor.lock();
try {
if (this.initialized) {
return;
}
@@ -83,6 +87,9 @@ public class MethodInvokingMessageSource extends AbstractMessageSource<Object> i
ReflectionUtils.makeAccessible(this.method);
this.initialized = true;
}
finally {
this.initializationMonitor.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.integration.endpoint;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.Lifecycle;
@@ -43,6 +46,7 @@ import org.springframework.util.Assert;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class SourcePollingChannelAdapter extends AbstractPollingEndpoint
implements TrackableComponent {
@@ -59,6 +63,8 @@ public class SourcePollingChannelAdapter extends AbstractPollingEndpoint
private volatile boolean shouldTrack;
private final Lock lock = new ReentrantLock();
/**
* Specify the source to be polled for Messages.
*
@@ -175,12 +181,16 @@ public class SourcePollingChannelAdapter extends AbstractPollingEndpoint
public MessageChannel getOutputChannel() {
if (this.outputChannelName != null) {
synchronized (this) {
this.lock.lock();
try {
if (this.outputChannelName != null) {
this.outputChannel = getChannelResolver().resolveDestination(this.outputChannelName);
this.outputChannelName = null;
}
}
finally {
this.lock.unlock();
}
}
return this.outputChannel;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -53,6 +55,7 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*
@@ -80,16 +83,22 @@ public class ReloadableResourceBundleExpressionSource implements ExpressionSourc
*/
private final Map<String, Map<Locale, List<String>>> cachedFilenames = new HashMap<>();
private final Lock cachedFilenamesMonitor = new ReentrantLock();
/**
* Cache to hold already loaded properties per filename.
*/
private final Map<String, PropertiesHolder> cachedProperties = new HashMap<>();
private final Lock cachedPropertiesMonitor = new ReentrantLock();
/**
* Cache to hold merged loaded properties per locale.
*/
private final Map<Locale, PropertiesHolder> cachedMergedProperties = new HashMap<>();
private final Lock cachedMergedPropertiesMonitor = new ReentrantLock();
private final ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
private String[] basenames = {};
@@ -282,7 +291,8 @@ public class ReloadableResourceBundleExpressionSource implements ExpressionSourc
* cached forever.
*/
private PropertiesHolder getMergedProperties(Locale locale) {
synchronized (this.cachedMergedProperties) {
this.cachedMergedPropertiesMonitor.lock();
try {
PropertiesHolder mergedHolder = this.cachedMergedProperties.get(locale);
if (mergedHolder != null) {
return mergedHolder;
@@ -303,6 +313,9 @@ public class ReloadableResourceBundleExpressionSource implements ExpressionSourc
this.cachedMergedProperties.put(locale, mergedHolder);
return mergedHolder;
}
finally {
this.cachedMergedPropertiesMonitor.unlock();
}
}
/**
@@ -316,7 +329,8 @@ public class ReloadableResourceBundleExpressionSource implements ExpressionSourc
* @see #calculateFilenamesForLocale
*/
private List<String> calculateAllFilenames(String basename, Locale locale) {
synchronized (this.cachedFilenames) {
this.cachedFilenamesMonitor.lock();
try {
Map<Locale, List<String>> localeMap = this.cachedFilenames.get(basename);
if (localeMap != null) {
List<String> filenames = localeMap.get(locale);
@@ -345,6 +359,9 @@ public class ReloadableResourceBundleExpressionSource implements ExpressionSourc
}
return filenames;
}
finally {
this.cachedFilenamesMonitor.unlock();
}
}
/**
@@ -392,7 +409,8 @@ public class ReloadableResourceBundleExpressionSource implements ExpressionSourc
* @return the current PropertiesHolder for the bundle
*/
private PropertiesHolder getProperties(String filename) {
synchronized (this.cachedProperties) {
this.cachedPropertiesMonitor.lock();
try {
PropertiesHolder propHolder = this.cachedProperties.get(filename);
if (propHolder != null &&
(propHolder.getRefreshTimestamp() < 0 ||
@@ -401,6 +419,9 @@ public class ReloadableResourceBundleExpressionSource implements ExpressionSourc
}
return refreshProperties(filename, propHolder);
}
finally {
this.cachedPropertiesMonitor.unlock();
}
}
/**
@@ -539,12 +560,21 @@ public class ReloadableResourceBundleExpressionSource implements ExpressionSourc
*/
public void clearCache() {
LOGGER.debug("Clearing entire resource bundle cache");
synchronized (this.cachedProperties) {
this.cachedPropertiesMonitor.lock();
try {
this.cachedProperties.clear();
}
synchronized (this.cachedMergedProperties) {
finally {
this.cachedPropertiesMonitor.unlock();
}
this.cachedMergedPropertiesMonitor.lock();
try {
this.cachedMergedProperties.clear();
}
finally {
this.cachedMergedPropertiesMonitor.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.integration.gateway;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -28,6 +31,7 @@ import org.springframework.messaging.MessageChannel;
* The {@link AbstractReplyProducingMessageHandler} implementation for mid-flow Gateway.
*
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 5.0
*/
@@ -39,6 +43,8 @@ public class GatewayMessageHandler extends AbstractReplyProducingMessageHandler
private volatile boolean running;
private final Lock lock = new ReentrantLock();
public GatewayMessageHandler() {
this.gatewayProxyFactoryBean = new GatewayProxyFactoryBean<>();
}
@@ -78,11 +84,15 @@ public class GatewayMessageHandler extends AbstractReplyProducingMessageHandler
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
if (this.exchanger == null) {
synchronized (this) {
this.lock.lock();
try {
if (this.exchanger == null) {
initialize();
}
}
finally {
this.lock.unlock();
}
}
return this.exchanger.exchange(requestMessage);
}

View File

@@ -29,6 +29,8 @@ import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
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.function.Supplier;
@@ -106,12 +108,13 @@ import org.springframework.util.StringUtils;
* @author Gary Russell
* @author Artem Bilan
* @author JingPeng Xie
* @author Christian Tzolov
*/
public class GatewayProxyFactoryBean<T> extends AbstractEndpoint
implements TrackableComponent, FactoryBean<T>, MethodInterceptor, BeanClassLoaderAware,
IntegrationManagement {
private final Object initializationMonitor = new Object();
private final Lock initializationMonitor = new ReentrantLock();
private final Map<Method, MethodInvocationGateway> gatewayMap = new HashMap<>();
@@ -455,7 +458,8 @@ public class GatewayProxyFactoryBean<T> extends AbstractEndpoint
@Override
@SuppressWarnings("unchecked")
protected void onInit() {
synchronized (this.initializationMonitor) {
this.initializationMonitor.lock();
try {
if (this.initialized) {
return;
}
@@ -466,13 +470,15 @@ public class GatewayProxyFactoryBean<T> extends AbstractEndpoint
populateMethodInvocationGateways();
ProxyFactory gatewayProxyFactory =
new ProxyFactory(this.serviceInterface, this);
ProxyFactory gatewayProxyFactory = new ProxyFactory(this.serviceInterface, this);
gatewayProxyFactory.addAdvice(new DefaultMethodInvokingMethodInterceptor());
this.serviceProxy = (T) gatewayProxyFactory.getProxy(this.beanClassLoader);
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory);
this.initialized = true;
}
finally {
this.initializationMonitor.unlock();
}
}
private void populateMethodInvocationGateways() {

View File

@@ -19,6 +19,8 @@ package org.springframework.integration.gateway;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import io.micrometer.observation.ObservationRegistry;
import org.reactivestreams.Publisher;
@@ -86,6 +88,7 @@ import org.springframework.util.Assert;
* @author Gary Russell
* @author Artem Bilan
* @author Trung Pham
* @author Christian Tzolov
*/
@IntegrationManagedResource
public abstract class MessagingGatewaySupport extends AbstractEndpoint
@@ -99,7 +102,7 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
private final HistoryWritingMessagePostProcessor historyWritingPostProcessor =
new HistoryWritingMessagePostProcessor();
private final Object replyMessageCorrelatorMonitor = new Object();
private final Lock replyMessageCorrelatorMonitor = new ReentrantLock();
private final ManagementOverrides managementOverrides = new ManagementOverrides();
@@ -892,7 +895,8 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
protected void registerReplyMessageCorrelatorIfNecessary() {
MessageChannel replyChan = getReplyChannel();
if (replyChan != null && this.replyMessageCorrelator == null) {
synchronized (this.replyMessageCorrelatorMonitor) {
this.replyMessageCorrelatorMonitor.lock();
try {
if (this.replyMessageCorrelator != null) {
return;
}
@@ -923,6 +927,9 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
correlator.afterPropertiesSet();
this.replyMessageCorrelator = correlator;
}
finally {
this.replyMessageCorrelatorMonitor.unlock();
}
if (isRunning()) {
this.replyMessageCorrelator.start();
}

View File

@@ -26,6 +26,8 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -61,6 +63,7 @@ import org.springframework.messaging.PollableChannel;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 4.3
*
@@ -70,6 +73,8 @@ public class IntegrationGraphServer implements ApplicationContextAware, Applicat
private static final float GRAPH_VERSION = 1.2f;
private final Lock lock = new ReentrantLock();
private final NodeFactory nodeFactory = new NodeFactory(this::enhance);
private MicrometerNodeEnhancer micrometerEnhancer;
@@ -127,12 +132,16 @@ public class IntegrationGraphServer implements ApplicationContextAware, Applicat
* @see #rebuild()
*/
public Graph getGraph() {
if (this.graph == null) { //NOSONAR (sync)
synchronized (this) {
if (this.graph == null) { // NOSONAR (sync)
this.lock.lock();
try {
if (this.graph == null) {
buildGraph();
}
}
finally {
this.lock.unlock();
}
}
return this.graph;
}
@@ -169,35 +178,41 @@ public class IntegrationGraphServer implements ApplicationContextAware, Applicat
}
}
private synchronized Graph buildGraph() {
if (this.micrometerEnhancer == null && MicrometerMetricsCaptorConfiguration.METER_REGISTRY_PRESENT) {
this.micrometerEnhancer = new MicrometerNodeEnhancer(this.applicationContext);
private Graph buildGraph() {
this.lock.lock();
try {
if (this.micrometerEnhancer == null && MicrometerMetricsCaptorConfiguration.METER_REGISTRY_PRESENT) {
this.micrometerEnhancer = new MicrometerNodeEnhancer(this.applicationContext);
}
String implementationVersion = IntegrationGraphServer.class.getPackage().getImplementationVersion();
if (implementationVersion == null) {
implementationVersion = "unknown - is Spring Integration running from the distribution jar?";
}
Map<String, Object> descriptor = new HashMap<>();
descriptor.put("provider", "spring-integration");
descriptor.put("providerVersion", implementationVersion);
descriptor.put("providerFormatVersion", GRAPH_VERSION);
String name = this.applicationName;
if (name == null) {
name = this.applicationContext.getEnvironment().getProperty("spring.application.name");
}
if (name != null) {
descriptor.put("name", name);
}
this.nodeFactory.reset();
Collection<IntegrationNode> nodes = new ArrayList<>();
Collection<LinkNode> links = new ArrayList<>();
Map<String, MessageChannelNode> channelNodes = channels(nodes);
pollingAdapters(nodes, links, channelNodes);
gateways(nodes, links, channelNodes);
producers(nodes, links, channelNodes);
consumers(nodes, links, channelNodes);
this.graph = new Graph(descriptor, nodes, links);
return this.graph;
}
String implementationVersion = IntegrationGraphServer.class.getPackage().getImplementationVersion();
if (implementationVersion == null) {
implementationVersion = "unknown - is Spring Integration running from the distribution jar?";
finally {
this.lock.unlock();
}
Map<String, Object> descriptor = new HashMap<>();
descriptor.put("provider", "spring-integration");
descriptor.put("providerVersion", implementationVersion);
descriptor.put("providerFormatVersion", GRAPH_VERSION);
String name = this.applicationName;
if (name == null) {
name = this.applicationContext.getEnvironment().getProperty("spring.application.name");
}
if (name != null) {
descriptor.put("name", name);
}
this.nodeFactory.reset();
Collection<IntegrationNode> nodes = new ArrayList<>();
Collection<LinkNode> links = new ArrayList<>();
Map<String, MessageChannelNode> channelNodes = channels(nodes);
pollingAdapters(nodes, links, channelNodes);
gateways(nodes, links, channelNodes);
producers(nodes, links, channelNodes);
consumers(nodes, links, channelNodes);
this.graph = new Graph(descriptor, nodes, links);
return this.graph;
}
private Map<String, MessageChannelNode> channels(Collection<IntegrationNode> nodes) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.integration.handler;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.aopalliance.aop.Advice;
@@ -41,10 +43,13 @@ import org.springframework.util.ClassUtils;
* @author Artem Bilan
* @author David Liu
* @author Trung Pham
* @author Christian Tzolov
*/
public abstract class AbstractReplyProducingMessageHandler extends AbstractMessageProducingHandler
implements BeanClassLoaderAware {
private final Lock lock = new ReentrantLock();
private final List<Advice> adviceChain = new LinkedList<>();
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
@@ -72,13 +77,17 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
*/
public void setAdviceChain(List<Advice> adviceChain) {
Assert.notEmpty(adviceChain, "adviceChain cannot be empty");
synchronized (this.adviceChain) {
this.lock.lock();
try {
this.adviceChain.clear();
this.adviceChain.addAll(adviceChain);
if (isInitialized()) {
initAdvisedRequestHandlerIfAny();
}
}
finally {
this.lock.unlock();
}
}
protected boolean hasAdviceChain() {

View File

@@ -98,6 +98,7 @@ import org.springframework.util.ObjectUtils;
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 1.0.3
*/
@@ -110,6 +111,8 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
public static final long DEFAULT_RETRY_DELAY = 1_000;
private final Lock lock = new ReentrantLock();
private final ConcurrentMap<String, AtomicInteger> deliveries = new ConcurrentHashMap<>();
private final Lock removeReleasedMessageLock = new ReentrantLock();
@@ -618,22 +621,28 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
* behavior is dictated by the avoidance of invocation thread overload.
*/
@Override
public synchronized void reschedulePersistedMessages() {
MessageGroup messageGroup = this.messageStore.getMessageGroup(this.messageGroupId);
try (Stream<Message<?>> messageStream = messageGroup.streamMessages()) {
TaskScheduler taskScheduler = getTaskScheduler();
messageStream.forEach((message) -> // NOSONAR
taskScheduler.schedule(() -> {
// This is fine to keep the reference to the message,
// because the scheduled task is performed immediately.
long delay = determineDelayForMessage(message);
if (delay > 0) {
releaseMessageAfterDelay(message, delay);
}
else {
releaseMessage(message);
}
}, Instant.now()));
public void reschedulePersistedMessages() {
this.lock.lock();
try {
MessageGroup messageGroup = this.messageStore.getMessageGroup(this.messageGroupId);
try (Stream<Message<?>> messageStream = messageGroup.streamMessages()) {
TaskScheduler taskScheduler = getTaskScheduler();
messageStream.forEach((message) -> // NOSONAR
taskScheduler.schedule(() -> {
// This is fine to keep the reference to the message,
// because the scheduled task is performed immediately.
long delay = determineDelayForMessage(message);
if (delay > 0) {
releaseMessageAfterDelay(message, delay);
}
else {
releaseMessage(message);
}
}, Instant.now()));
}
}
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.Lifecycle;
@@ -66,11 +67,12 @@ import org.springframework.util.Assert;
* @author Gary Russell
* @author Artem Bilan
* @author Trung Pham
* @author Christian Tzolov
*/
public class MessageHandlerChain extends AbstractMessageProducingHandler
implements CompositeMessageHandler, ManageableLifecycle {
private final Object initializationMonitor = new Object();
private final Lock initializationMonitor = new ReentrantLock();
private final ReentrantLock lifecycleLock = new ReentrantLock();
@@ -102,13 +104,17 @@ public class MessageHandlerChain extends AbstractMessageProducingHandler
@Override
protected void onInit() {
super.onInit();
synchronized (this.initializationMonitor) {
this.initializationMonitor.lock();
try {
if (!this.initialized) {
Assert.notEmpty(this.handlers, "handler list must not be empty");
configureChain();
this.initialized = true;
}
}
finally {
this.initializationMonitor.unlock();
}
}
private void configureChain() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,8 @@ import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -119,6 +121,8 @@ import org.springframework.util.StringUtils;
* @author Gary Russell
* @author Artem Bilan
* @author Trung Pham
* @author Christian Tzolov
*
* @since 2.0
*/
public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator implements ManageableLifecycle {
@@ -160,6 +164,8 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator im
SPEL_COMPILERS.put(SpelCompilerMode.MIXED, EXPRESSION_PARSER_MIXED);
}
private final Lock lock = new ReentrantLock();
private final Object targetObject;
private final JsonObjectMapper<?, ?> jsonObjectMapper;
@@ -486,23 +492,28 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator im
}
}
private synchronized void initialize() {
if (isProvidedMessageHandlerFactoryBean()) {
LOGGER.trace("Overriding default instance of MessageHandlerMethodFactory with the one provided.");
this.messageHandlerMethodFactory =
getBeanFactory()
.getBean(
this.canProcessMessageList
? IntegrationContextUtils.LIST_MESSAGE_HANDLER_FACTORY_BEAN_NAME
: IntegrationContextUtils.MESSAGE_HANDLER_FACTORY_BEAN_NAME,
MessageHandlerMethodFactory.class);
}
else {
configureLocalMessageHandlerFactory();
}
private void initialize() {
this.lock.lock();
try {
if (isProvidedMessageHandlerFactoryBean()) {
LOGGER.trace("Overriding default instance of MessageHandlerMethodFactory with the one provided.");
this.messageHandlerMethodFactory = getBeanFactory()
.getBean(
this.canProcessMessageList
? IntegrationContextUtils.LIST_MESSAGE_HANDLER_FACTORY_BEAN_NAME
: IntegrationContextUtils.MESSAGE_HANDLER_FACTORY_BEAN_NAME,
MessageHandlerMethodFactory.class);
}
else {
configureLocalMessageHandlerFactory();
}
prepareEvaluationContext();
this.initialized = true;
prepareEvaluationContext();
this.initialized = true;
}
finally {
this.lock.unlock();
}
}
private boolean isProvidedMessageHandlerFactoryBean() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -44,6 +46,7 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -54,6 +57,8 @@ public class MessageHistoryConfigurer implements ManageableSmartLifecycle, BeanF
private static final Log LOGGER = LogFactory.getLog(MessageHistoryConfigurer.class);
private final Lock lock = new ReentrantLock();
private final Set<TrackableComponent> currentlyTrackedComponents = ConcurrentHashMap.newKeySet();
private String[] componentNamePatterns = {"*"};
@@ -180,7 +185,8 @@ public class MessageHistoryConfigurer implements ManageableSmartLifecycle, BeanF
@ManagedOperation
@Override
public void start() {
synchronized (this.currentlyTrackedComponents) {
this.lock.lock();
try {
if (!this.running) {
for (TrackableComponent component : getTrackableComponents(this.beanFactory)) {
trackComponentIfAny(component);
@@ -188,12 +194,16 @@ public class MessageHistoryConfigurer implements ManageableSmartLifecycle, BeanF
}
}
}
finally {
this.lock.unlock();
}
}
@ManagedOperation
@Override
public void stop() {
synchronized (this.currentlyTrackedComponents) {
this.lock.lock();
try {
if (this.running) {
this.currentlyTrackedComponents.forEach(component -> {
component.setShouldTrack(false);
@@ -207,6 +217,9 @@ public class MessageHistoryConfigurer implements ManageableSmartLifecycle, BeanF
this.running = false;
}
}
finally {
this.lock.unlock();
}
}
private static Collection<TrackableComponent> getTrackableComponents(ListableBeanFactory beanFactory) {

View File

@@ -18,6 +18,8 @@ package org.springframework.integration.router;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.ConversionService;
@@ -43,11 +45,14 @@ import org.springframework.util.Assert;
* @author Soby Chacko
* @author Stefan Ferstl
* @author Artem Bilan
* @author Christian Tzolov
*/
@ManagedResource
@IntegrationManagedResource
public abstract class AbstractMessageRouter extends AbstractMessageHandler implements MessageRouter {
private final Lock lock = new ReentrantLock();
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
private volatile MessageChannel defaultOutputChannel;
@@ -83,12 +88,16 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler imple
@Override
public MessageChannel getDefaultOutputChannel() {
if (this.defaultOutputChannelName != null) {
synchronized (this) {
this.lock.lock();
try {
if (this.defaultOutputChannelName != null) {
this.defaultOutputChannel = getChannelResolver().resolveDestination(this.defaultOutputChannelName);
this.defaultOutputChannelName = null;
}
}
finally {
this.lock.unlock();
}
}
return this.defaultOutputChannel;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.integration.selector;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.integration.core.MessageSelector;
import org.springframework.messaging.Message;
@@ -32,9 +34,12 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class MessageSelectorChain implements MessageSelector {
private final Lock lock = new ReentrantLock();
private volatile VotingStrategy votingStrategy = VotingStrategy.ALL;
private final List<MessageSelector> selectors = new CopyOnWriteArrayList<>();
@@ -72,10 +77,14 @@ public class MessageSelectorChain implements MessageSelector {
*/
public void setSelectors(List<MessageSelector> selectors) {
Assert.notEmpty(selectors, "selectors must not be empty");
synchronized (this.selectors) {
this.lock.lock();
try {
this.selectors.clear();
this.selectors.addAll(selectors);
}
finally {
this.lock.unlock();
}
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2020 the original author or authors.
* Copyright 2014-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.integration.selector;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiPredicate;
import org.springframework.integration.core.MessageSelector;
@@ -50,11 +52,14 @@ import org.springframework.util.Assert;
*
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 4.1
*/
public class MetadataStoreSelector implements MessageSelector {
private final Lock lock = new ReentrantLock();
private final ConcurrentMetadataStore metadataStore;
private final MessageProcessor<String> keyStrategy;
@@ -119,7 +124,8 @@ public class MetadataStoreSelector implements MessageSelector {
return this.metadataStore.putIfAbsent(key, value) == null;
}
else {
synchronized (this) {
this.lock.lock();
try {
String oldValue = this.metadataStore.get(key);
if (oldValue == null) {
return this.metadataStore.putIfAbsent(key, value) == null;
@@ -129,6 +135,9 @@ public class MetadataStoreSelector implements MessageSelector {
}
return false;
}
finally {
this.lock.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.integration.store;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -33,6 +35,7 @@ import org.springframework.messaging.Message;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -42,6 +45,8 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
protected final Log logger = LogFactory.getLog(getClass()); // NOSONAR final
private final Lock lock = new ReentrantLock();
private final Collection<MessageGroupCallback> expiryCallbacks = new LinkedHashSet<>();
private final MessageGroupFactory persistentMessageGroupFactory =
@@ -122,22 +127,28 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
@Override
@ManagedOperation
public synchronized int expireMessageGroups(long timeout) {
int count = 0;
long threshold = System.currentTimeMillis() - timeout;
for (MessageGroup group : this) {
public int expireMessageGroups(long timeout) {
this.lock.lock();
try {
int count = 0;
long threshold = System.currentTimeMillis() - timeout;
for (MessageGroup group : this) {
long timestamp = group.getTimestamp();
if (this.isTimeoutOnIdle() && group.getLastModified() > 0) {
timestamp = group.getLastModified();
}
long timestamp = group.getTimestamp();
if (this.isTimeoutOnIdle() && group.getLastModified() > 0) {
timestamp = group.getLastModified();
}
if (timestamp <= threshold) {
count++;
expire(copy(group));
if (timestamp <= threshold) {
count++;
expire(copy(group));
}
}
return count;
}
finally {
this.lock.unlock();
}
return count;
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2021 the original author or authors.
* Copyright 2016-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
import org.apache.commons.logging.Log;
@@ -32,6 +34,7 @@ import org.springframework.messaging.Message;
/**
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 4.3
*/
@@ -39,6 +42,8 @@ class PersistentMessageGroup implements MessageGroup {
private static final Log LOGGER = LogFactory.getLog(PersistentMessageGroup.class);
private final Lock lock = new ReentrantLock();
private final MessageGroupStore messageGroupStore;
private final Collection<Message<?>> messages = new PersistentCollection();
@@ -76,7 +81,8 @@ class PersistentMessageGroup implements MessageGroup {
@Override
public Message<?> getOne() {
if (this.oneMessage == null) {
synchronized (this) {
this.lock.lock();
try {
if (this.oneMessage == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Lazy loading of one message for messageGroup: " + this.original.getGroupId());
@@ -84,6 +90,9 @@ class PersistentMessageGroup implements MessageGroup {
this.oneMessage = this.messageGroupStore.getOneMessageFromGroup(this.original.getGroupId());
}
}
finally {
this.lock.unlock();
}
}
return this.oneMessage;
}
@@ -109,7 +118,8 @@ class PersistentMessageGroup implements MessageGroup {
@Override
public int size() {
if (this.size == 0) {
synchronized (this) {
this.lock.lock();
try {
if (this.size == 0) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Lazy loading of group size for messageGroup: " + this.original.getGroupId());
@@ -117,6 +127,9 @@ class PersistentMessageGroup implements MessageGroup {
this.size = this.messageGroupStore.messageGroupSize(this.original.getGroupId());
}
}
finally {
this.lock.unlock();
}
}
return this.size;
}
@@ -195,6 +208,8 @@ class PersistentMessageGroup implements MessageGroup {
private final class PersistentCollection extends AbstractCollection<Message<?>> {
private final Lock innerLock = new ReentrantLock();
private volatile Collection<Message<?>> collection;
PersistentCollection() {
@@ -202,7 +217,8 @@ class PersistentMessageGroup implements MessageGroup {
private void load() {
if (this.collection == null) {
synchronized (this) {
this.innerLock.lock();
try {
if (this.collection == null) {
Object groupId = PersistentMessageGroup.this.original.getGroupId();
if (LOGGER.isDebugEnabled()) {
@@ -211,6 +227,9 @@ class PersistentMessageGroup implements MessageGroup {
this.collection = PersistentMessageGroup.this.messageGroupStore.getMessagesForGroup(groupId);
}
}
finally {
this.innerLock.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.lang.Nullable;
@@ -38,11 +40,14 @@ import org.springframework.util.Assert;
* @author Dave Syer
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
public class SimpleMessageGroup implements MessageGroup {
private final Lock lock = new ReentrantLock();
private final Object groupId;
private final Collection<Message<?>> messages;
@@ -189,10 +194,14 @@ public class SimpleMessageGroup implements MessageGroup {
@Override
public Message<?> getOne() {
synchronized (this.messages) {
this.lock.lock();
try {
Iterator<Message<?>> iterator = this.messages.iterator();
return iterator.hasNext() ? iterator.next() : null;
}
finally {
this.lock.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2021 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -52,6 +54,7 @@ import org.springframework.util.MultiValueMap;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 4.2
*
@@ -63,6 +66,8 @@ public class SmartLifecycleRoleController implements ApplicationListener<Abstrac
private static final String IN_ROLE = " in role ";
private final Lock lock = new ReentrantLock();
private final MultiValueMap<String, SmartLifecycle> lifecycles = new LinkedMultiValueMap<>();
private final MultiValueMap<String, String> lazyLifecycles = new LinkedMultiValueMap<>();
@@ -283,9 +288,15 @@ public class SmartLifecycleRoleController implements ApplicationListener<Abstrac
Lifecycle::isRunning));
}
private synchronized void addLazyLifecycles() {
this.lazyLifecycles.forEach(this::doAddLifecyclesToRole);
this.lazyLifecycles.clear();
private void addLazyLifecycles() {
this.lock.lock();
try {
this.lazyLifecycles.forEach(this::doAddLifecyclesToRole);
this.lazyLifecycles.clear();
}
finally {
this.lock.unlock();
}
}
private void doAddLifecyclesToRole(String role, List<String> lifecycleBeanNames) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.integration.support.channel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -39,6 +42,7 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @see BeanFactory
*/
@@ -46,6 +50,8 @@ public class BeanFactoryChannelResolver implements DestinationResolver<MessageCh
private static final Log LOGGER = LogFactory.getLog(BeanFactoryChannelResolver.class);
private final Lock lock = new ReentrantLock();
private BeanFactory beanFactory;
private HeaderChannelRegistry replyChannelRegistry;
@@ -93,12 +99,12 @@ public class BeanFactoryChannelResolver implements DestinationResolver<MessageCh
+ name + "' exists, but failed to be created", e);
}
if (!this.initialized) {
synchronized (this) {
this.lock.lock();
try {
if (!this.initialized) {
try {
this.replyChannelRegistry =
this.beanFactory.getBean("integrationHeaderChannelRegistry",
HeaderChannelRegistry.class);
this.replyChannelRegistry = this.beanFactory.getBean("integrationHeaderChannelRegistry",
HeaderChannelRegistry.class);
}
catch (Exception ex) {
LOGGER.debug("No HeaderChannelRegistry found");
@@ -106,6 +112,9 @@ public class BeanFactoryChannelResolver implements DestinationResolver<MessageCh
this.initialized = true;
}
}
finally {
this.lock.unlock();
}
}
if (this.replyChannelRegistry != null) {
MessageChannel channel = this.replyChannelRegistry.channelNameToChannel(name);

View File

@@ -21,6 +21,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationEventPublisher;
@@ -56,6 +57,7 @@ import org.springframework.util.Assert;
* @author Glenn Renfro
* @author Kiel Boatman
* @author Gary Russell
* @author Christian Tzolov
*
* @since 4.3.1
*/
@@ -68,6 +70,8 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe
private static final LogAccessor LOGGER = new LogAccessor(LockRegistryLeaderInitiator.class);
private final Lock lock = new ReentrantLock();
/**
* A lock registry. The locks it manages should be global (whatever that means for the
* system) and expiring, in case the holder dies without notifying anyone.
@@ -286,15 +290,21 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe
* Start the registration of the {@link #candidate} for leader election.
*/
@Override
public synchronized void start() {
if (this.leaderEventPublisher == null && this.applicationEventPublisher != null) {
this.leaderEventPublisher = new DefaultLeaderEventPublisher(this.applicationEventPublisher);
public void start() {
this.lock.lock();
try {
if (this.leaderEventPublisher == null && this.applicationEventPublisher != null) {
this.leaderEventPublisher = new DefaultLeaderEventPublisher(this.applicationEventPublisher);
}
if (!this.running) {
this.leaderSelector = new LeaderSelector(buildLeaderPath());
this.running = true;
this.future = this.taskExecutor.submit(this.leaderSelector);
LOGGER.debug("Started LeaderInitiator");
}
}
if (!this.running) {
this.leaderSelector = new LeaderSelector(buildLeaderPath());
this.running = true;
this.future = this.taskExecutor.submit(this.leaderSelector);
LOGGER.debug("Started LeaderInitiator");
finally {
this.lock.unlock();
}
}
@@ -308,14 +318,20 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe
* candidate is currently leader, its leadership will be revoked.
*/
@Override
public synchronized void stop() {
if (this.running) {
this.running = false;
if (this.future != null) {
this.future.cancel(true);
public void stop() {
this.lock.lock();
try {
if (this.running) {
this.running = false;
if (this.future != null) {
this.future.cancel(true);
}
this.future = null;
LOGGER.debug(() -> "Stopped LeaderInitiator for " + getContext());
}
this.future = null;
LOGGER.debug(() -> "Stopped LeaderInitiator for " + getContext());
}
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
@@ -41,12 +43,16 @@ import org.springframework.util.Assert;
*
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 4.1
*/
public class RoutingSlipHeaderValueMessageProcessor
extends AbstractHeaderValueMessageProcessor<Map<List<Object>, Integer>>
implements BeanFactoryAware {
private final Lock lock = new ReentrantLock();
private final List<Object> routingSlipPath;
private volatile Map<List<Object>, Integer> routingSlip;
@@ -79,7 +85,8 @@ public class RoutingSlipHeaderValueMessageProcessor
// use a local variable to avoid the second access to volatile field on the happy path
Map<List<Object>, Integer> slip = this.routingSlip;
if (slip == null) {
synchronized (this) {
this.lock.lock();
try {
slip = this.routingSlip;
if (slip == null) {
List<Object> slipPath = this.routingSlipPath;
@@ -118,6 +125,9 @@ public class RoutingSlipHeaderValueMessageProcessor
this.routingSlip = slip;
}
}
finally {
this.lock.unlock();
}
}
return slip;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* An implementation of {@link CollectionFilter} that remembers the elements passed in
@@ -28,6 +30,7 @@ import java.util.List;
* @param <T> the collection element type.
*
* @author Mark Fisher
* @author Christian Tzolov
*
* @since 2.1
*/
@@ -35,15 +38,23 @@ public class AcceptOnceCollectionFilter<T> implements CollectionFilter<T> {
private volatile Collection<T> lastSeenElements = Collections.emptyList();
public synchronized Collection<T> filter(Collection<T> unfilteredElements) {
List<T> filteredElements = new ArrayList<>();
for (T element : unfilteredElements) {
if (!this.lastSeenElements.contains(element)) {
filteredElements.add(element);
private final Lock lock = new ReentrantLock();
public Collection<T> filter(Collection<T> unfilteredElements) {
this.lock.lock();
try {
List<T> filteredElements = new ArrayList<>();
for (T element : unfilteredElements) {
if (!this.lastSeenElements.contains(element)) {
filteredElements.add(element);
}
}
this.lastSeenElements = unfilteredElements;
return filteredElements;
}
finally {
this.lock.unlock();
}
this.lastSeenElements = unfilteredElements;
return filteredElements;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -40,6 +42,7 @@ import org.springframework.util.Assert;
* @author Gary Russell
* @author Sergey Bogatyrev
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.2
*
@@ -48,6 +51,8 @@ public class SimplePool<T> implements Pool<T> {
protected final Log logger = LogFactory.getLog(getClass()); // NOSONAR final
private final Lock lock = new ReentrantLock();
private final PoolSemaphore permits = new PoolSemaphore(0);
private final AtomicInteger poolSize = new AtomicInteger();
@@ -93,39 +98,45 @@ public class SimplePool<T> implements Pool<T> {
* items are returned.
* @param poolSize The desired target pool size.
*/
public synchronized void setPoolSize(int poolSize) {
int delta = poolSize - this.poolSize.get();
this.targetPoolSize.addAndGet(delta);
if (this.logger.isDebugEnabled()) {
this.logger.debug(String.format("Target pool size changed by %d, now %d", delta,
this.targetPoolSize.get()));
}
if (delta > 0) {
this.poolSize.addAndGet(delta);
this.permits.release(delta);
}
else {
this.permits.reducePermits(-delta);
public void setPoolSize(int poolSize) {
this.lock.lock();
try {
int delta = poolSize - this.poolSize.get();
this.targetPoolSize.addAndGet(delta);
if (this.logger.isDebugEnabled()) {
this.logger.debug(String.format("Target pool size changed by %d, now %d", delta,
this.targetPoolSize.get()));
}
if (delta > 0) {
this.poolSize.addAndGet(delta);
this.permits.release(delta);
}
else {
this.permits.reducePermits(-delta);
int inUseSize = this.inUse.size();
int newPoolSize = Math.max(poolSize, inUseSize);
this.poolSize.set(newPoolSize);
int inUseSize = this.inUse.size();
int newPoolSize = Math.max(poolSize, inUseSize);
this.poolSize.set(newPoolSize);
for (int i = this.available.size(); i > newPoolSize - inUseSize; i--) {
T item = this.available.poll();
if (item != null) {
doRemoveItem(item);
for (int i = this.available.size(); i > newPoolSize - inUseSize; i--) {
T item = this.available.poll();
if (item != null) {
doRemoveItem(item);
}
else {
break;
}
}
else {
break;
int inUseDelta = poolSize - inUseSize;
if (inUseDelta < 0 && this.logger.isDebugEnabled()) {
this.logger.debug(String.format("Pool is overcommitted by %d; items will be removed when returned",
-inUseDelta));
}
}
int inUseDelta = poolSize - inUseSize;
if (inUseDelta < 0 && this.logger.isDebugEnabled()) {
this.logger.debug(String.format("Pool is overcommitted by %d; items will be removed when returned",
-inUseDelta));
}
}
finally {
this.lock.unlock();
}
}
@@ -135,8 +146,14 @@ public class SimplePool<T> implements Pool<T> {
* to be set.
*/
@Override
public synchronized int getPoolSize() {
return this.poolSize.get();
public int getPoolSize() {
this.lock.lock();
try {
return this.poolSize.get();
}
finally {
this.lock.unlock();
}
}
@Override
@@ -224,36 +241,48 @@ public class SimplePool<T> implements Pool<T> {
* Return an item to the pool.
*/
@Override
public synchronized void releaseItem(T item) {
Assert.notNull(item, "Item cannot be null");
Assert.isTrue(this.allocated.contains(item),
"You can only release items that were obtained from the pool");
if (this.inUse.contains(item)) {
if (this.poolSize.get() > this.targetPoolSize.get() || this.closed) {
this.poolSize.decrementAndGet();
doRemoveItem(item);
public void releaseItem(T item) {
this.lock.lock();
try {
Assert.notNull(item, "Item cannot be null");
Assert.isTrue(this.allocated.contains(item),
"You can only release items that were obtained from the pool");
if (this.inUse.contains(item)) {
if (this.poolSize.get() > this.targetPoolSize.get() || this.closed) {
this.poolSize.decrementAndGet();
doRemoveItem(item);
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Releasing " + item + " back to the pool");
}
this.available.add(item);
this.inUse.remove(item);
this.permits.release();
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Releasing " + item + " back to the pool");
this.logger.debug("Ignoring release of " + item + " back to the pool - not in use");
}
this.available.add(item);
this.inUse.remove(item);
this.permits.release();
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Ignoring release of " + item + " back to the pool - not in use");
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void removeAllIdleItems() {
T item;
while ((item = this.available.poll()) != null) {
doRemoveItem(item);
public void removeAllIdleItems() {
this.lock.lock();
try {
T item;
while ((item = this.available.poll()) != null) {
doRemoveItem(item);
}
}
finally {
this.lock.unlock();
}
}
@@ -267,9 +296,15 @@ public class SimplePool<T> implements Pool<T> {
}
@Override
public synchronized void close() {
this.closed = true;
removeAllIdleItems();
public void close() {
this.lock.lock();
try {
this.closed = true;
removeAllIdleItems();
}
finally {
this.lock.unlock();
}
}
@SuppressWarnings("serial")

View File

@@ -28,6 +28,8 @@ import java.util.Date;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndFeed;
@@ -56,6 +58,7 @@ import org.springframework.util.StringUtils;
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Aaron Loes
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -69,13 +72,13 @@ public class FeedEntryMessageSource extends AbstractMessageSource<SyndEntry> {
private final Queue<SyndEntry> entries = new ConcurrentLinkedQueue<>();
private final Object monitor = new Object();
private final Lock monitor = new ReentrantLock();
private final Comparator<SyndEntry> syndEntryComparator =
Comparator.comparing(FeedEntryMessageSource::getLastModifiedDate,
Comparator.nullsFirst(Comparator.naturalOrder()));
private final Object feedMonitor = new Object();
private final Lock feedMonitor = new ReentrantLock();
private SyndFeedInput syndFeedInput = new SyndFeedInput();
@@ -176,7 +179,8 @@ public class FeedEntryMessageSource extends AbstractMessageSource<SyndEntry> {
Assert.isTrue(this.initialized,
"'FeedEntryReaderMessageSource' must be initialized before it can produce Messages.");
SyndEntry nextEntry;
synchronized (this.monitor) {
this.monitor.lock();
try {
nextEntry = getNextEntry();
if (nextEntry == null) {
// read feed and try again
@@ -184,6 +188,9 @@ public class FeedEntryMessageSource extends AbstractMessageSource<SyndEntry> {
nextEntry = getNextEntry();
}
}
finally {
this.monitor.unlock();
}
return nextEntry;
}
@@ -225,7 +232,8 @@ public class FeedEntryMessageSource extends AbstractMessageSource<SyndEntry> {
private SyndFeed getFeed() {
try {
synchronized (this.feedMonitor) {
this.feedMonitor.lock();
try {
SyndFeed feed = buildSyndFeed();
logger.debug(() -> "Retrieved feed for [" + this + "]");
if (feed == null) {
@@ -233,6 +241,9 @@ public class FeedEntryMessageSource extends AbstractMessageSource<SyndEntry> {
}
return feed;
}
finally {
this.feedMonitor.unlock();
}
}
catch (Exception e) {
throw new MessagingException("Failed to retrieve feed for '" + this + "'", e);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -109,6 +110,7 @@ import org.springframework.util.StringUtils;
* @author Tony Falabella
* @author Alen Turkovic
* @author Trung Pham
* @author Christian Tzolov
*/
public class FileWritingMessageHandler extends AbstractReplyProducingMessageHandler
implements ManageableLifecycle, MessageTriggerAction {
@@ -130,6 +132,8 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
PosixFilePermission.OWNER_READ
};
private final Lock lock = new ReentrantLock();
private final Map<String, FileState> fileStates = new HashMap<>();
private final Expression destinationDirectoryExpression;
@@ -459,12 +463,16 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
@Override
public void stop() {
synchronized (this) {
this.lock.lock();
try {
if (this.flushTask != null) {
this.flushTask.cancel(true);
this.flushTask = null;
}
}
finally {
this.lock.unlock();
}
Flusher flusher = new Flusher();
flusher.run();
boolean needInterrupt = this.fileStates.size() > 0;
@@ -873,37 +881,42 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
return destinationDirectory;
}
private synchronized FileState getFileState(File fileToWriteTo, boolean isString)
private FileState getFileState(File fileToWriteTo, boolean isString)
throws FileNotFoundException {
FileState state;
boolean appendNoFlush = FileExistsMode.APPEND_NO_FLUSH.equals(this.fileExistsMode);
if (appendNoFlush) {
String absolutePath = fileToWriteTo.getAbsolutePath();
state = this.fileStates.get(absolutePath);
if (state != null // NOSONAR
&& ((isString && state.stream != null) || (!isString && state.writer != null))) {
state.close();
this.lock.lock();
try {
FileState state;
boolean appendNoFlush = FileExistsMode.APPEND_NO_FLUSH.equals(this.fileExistsMode);
if (appendNoFlush) {
String absolutePath = fileToWriteTo.getAbsolutePath();
state = this.fileStates.get(absolutePath);
if (state != null // NOSONAR
&& ((isString && state.stream != null) || (!isString && state.writer != null))) {
state.close();
state = null;
this.fileStates.remove(absolutePath);
}
if (state == null) {
if (isString) {
state = new FileState(createWriter(fileToWriteTo, true),
this.lockRegistry.obtain(fileToWriteTo.getAbsolutePath()));
}
else {
state = new FileState(createOutputStream(fileToWriteTo, true),
this.lockRegistry.obtain(fileToWriteTo.getAbsolutePath()));
}
this.fileStates.put(absolutePath, state);
}
state.lastWrite = Long.MAX_VALUE; // prevent flush while we write
}
else {
state = null;
this.fileStates.remove(absolutePath);
}
if (state == null) {
if (isString) {
state = new FileState(createWriter(fileToWriteTo, true),
this.lockRegistry.obtain(fileToWriteTo.getAbsolutePath()));
}
else {
state = new FileState(createOutputStream(fileToWriteTo, true),
this.lockRegistry.obtain(fileToWriteTo.getAbsolutePath()));
}
this.fileStates.put(absolutePath, state);
}
state.lastWrite = Long.MAX_VALUE; // prevent flush while we write
return state;
}
else {
state = null;
finally {
this.lock.unlock();
}
return state;
}
/**
@@ -975,7 +988,8 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
private Map<String, FileState> findFilesToFlush(MessageFlushPredicate flushPredicate, Message<?> filterMessage) {
Map<String, FileState> toRemove = new HashMap<>();
synchronized (this) {
this.lock.lock();
try {
Iterator<Entry<String, FileState>> iterator = this.fileStates.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, FileState> entry = iterator.next();
@@ -986,12 +1000,21 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
}
}
}
finally {
this.lock.unlock();
}
return toRemove;
}
private synchronized void clearState(final File fileToWriteTo, final FileState state) {
private void clearState(final File fileToWriteTo, final FileState state) {
if (state != null) {
this.fileStates.remove(fileToWriteTo.getAbsolutePath());
this.lock.lock();
try {
this.fileStates.remove(fileToWriteTo.getAbsolutePath());
}
finally {
this.lock.unlock();
}
}
}
@@ -1014,11 +1037,15 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
FileWritingMessageHandler.this.logger
.debug("Interrupted during flush; not flushed: " + toRestore.keySet());
}
synchronized (this) {
this.lock.lock();
try {
for (Entry<String, FileState> entry : toRestore.entrySet()) {
this.fileStates.putIfAbsent(entry.getKey(), entry.getValue());
}
}
finally {
this.lock.unlock();
}
}
}
@@ -1085,11 +1112,12 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
@Override
public void run() {
Map<String, FileState> toRemove = new HashMap<>();
synchronized (FileWritingMessageHandler.this) {
FileWritingMessageHandler.this.lock.lock();
try {
long expired = FileWritingMessageHandler.this.flushTask == null ? Long.MAX_VALUE
: (System.currentTimeMillis() - FileWritingMessageHandler.this.flushInterval);
Iterator<Entry<String, FileState>> iterator =
FileWritingMessageHandler.this.fileStates.entrySet().iterator();
Iterator<Entry<String, FileState>> iterator = FileWritingMessageHandler.this.fileStates.entrySet()
.iterator();
while (iterator.hasNext()) {
Entry<String, FileState> entry = iterator.next();
FileState state = entry.getValue();
@@ -1100,6 +1128,9 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
}
}
}
finally {
FileWritingMessageHandler.this.lock.unlock();
}
doFlush(toRemove);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.integration.file.config;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.integration.file.filters.AcceptAllFileListFilter;
@@ -34,6 +36,8 @@ import org.springframework.lang.NonNull;
* @author Mark Fisher
* @author Gunnar Hillert
* @author Gary Russell
* @author Christian Tzolov
*
* @since 1.0.3
*/
public class FileListFilterFactoryBean implements FactoryBean<FileListFilter<File>> {
@@ -52,7 +56,7 @@ public class FileListFilterFactoryBean implements FactoryBean<FileListFilter<Fil
private volatile Boolean alwaysAcceptDirectories;
private final Object monitor = new Object();
private final Lock monitor = new ReentrantLock();
public void setFilter(FileListFilter<File> filter) {
this.filter = filter;
@@ -95,9 +99,13 @@ public class FileListFilterFactoryBean implements FactoryBean<FileListFilter<Fil
@NonNull
public FileListFilter<File> getObject() {
if (this.result == null) {
synchronized (this.monitor) {
this.monitor.lock();
try {
this.initializeFileListFilter();
}
finally {
this.monitor.unlock();
}
}
return this.result;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.lang.Nullable;
@@ -37,6 +39,7 @@ import org.springframework.lang.Nullable;
* @author Josh Long
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class AcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> implements ReversibleFileListFilter<F>,
ResettableFileListFilter<F> {
@@ -46,7 +49,7 @@ public class AcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> imple
private final Set<F> seenSet = new HashSet<F>();
private final Object monitor = new Object();
private final Lock monitor = new ReentrantLock();
/**
@@ -69,7 +72,8 @@ public class AcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> imple
@Override
public boolean accept(F file) {
synchronized (this.monitor) {
this.monitor.lock();
try {
if (this.seenSet.contains(file)) {
return false;
}
@@ -81,11 +85,15 @@ public class AcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> imple
this.seenSet.add(file);
return true;
}
finally {
this.monitor.unlock();
}
}
@Override
public void rollback(F file, List<F> files) {
synchronized (this.monitor) {
this.monitor.lock();
try {
boolean rollingBack = false;
for (F fileToRollback : files) {
if (fileToRollback.equals(file)) {
@@ -96,6 +104,9 @@ public class AcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> imple
}
}
}
finally {
this.monitor.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@ import java.util.LinkedHashSet;
import java.util.List;
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.function.Consumer;
import org.springframework.beans.factory.InitializingBean;
@@ -46,6 +48,7 @@ import org.springframework.util.Assert;
* @author Josh Long
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class CompositeFileListFilter<F>
implements ReversibleFileListFilter<F>, ResettableFileListFilter<F>, DiscardAwareFileListFilter<F>, Closeable {
@@ -58,6 +61,7 @@ public class CompositeFileListFilter<F>
private boolean oneIsForRecursion;
private final Lock lock = new ReentrantLock();
public CompositeFileListFilter() {
this.fileFilters = new LinkedHashSet<>();
@@ -104,24 +108,30 @@ public class CompositeFileListFilter<F>
* @param filtersToAdd a list of filters to add
* @return this CompositeFileListFilter instance with the added filters
*/
public synchronized CompositeFileListFilter<F> addFilters(Collection<? extends FileListFilter<F>> filtersToAdd) {
for (FileListFilter<F> elf : filtersToAdd) {
if (elf instanceof DiscardAwareFileListFilter) {
((DiscardAwareFileListFilter<F>) elf).addDiscardCallback(this.discardCallback);
}
if (elf instanceof InitializingBean) {
try {
((InitializingBean) elf).afterPropertiesSet();
public CompositeFileListFilter<F> addFilters(Collection<? extends FileListFilter<F>> filtersToAdd) {
this.lock.lock();
try {
for (FileListFilter<F> elf : filtersToAdd) {
if (elf instanceof DiscardAwareFileListFilter) {
((DiscardAwareFileListFilter<F>) elf).addDiscardCallback(this.discardCallback);
}
catch (Exception e) {
throw new IllegalStateException(e);
if (elf instanceof InitializingBean) {
try {
((InitializingBean) elf).afterPropertiesSet();
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
this.allSupportAccept = this.allSupportAccept && elf.supportsSingleFileFiltering();
this.oneIsForRecursion |= elf.isForRecursion();
}
this.allSupportAccept = this.allSupportAccept && elf.supportsSingleFileFiltering();
this.oneIsForRecursion |= elf.isForRecursion();
this.fileFilters.addAll(filtersToAdd);
return this;
}
finally {
this.lock.unlock();
}
this.fileFilters.addAll(filtersToAdd);
return this;
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.integration.file.remote.session;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -40,6 +42,7 @@ import org.springframework.util.Assert;
* @author Gary Russell
* @author Alen Turkovic
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -47,6 +50,8 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
private static final Log LOGGER = LogFactory.getLog(CachingSessionFactory.class);
private final Lock lock = new ReentrantLock();
private final SessionFactory<F> sessionFactory;
private final SimplePool<Session<F>> pool;
@@ -146,24 +151,29 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
* Clear the cache of sessions; also any in-use sessions will be closed when
* returned to the cache.
*/
public synchronized void resetCache() {
LOGGER.debug("Cache reset; idle sessions will be removed, in-use sessions will be closed when returned");
if (this.isSharedSessionCapable && ((SharedSessionCapable) this.sessionFactory).isSharedSession()) {
((SharedSessionCapable) this.sessionFactory).resetSharedSession();
public void resetCache() {
this.lock.lock();
try {
LOGGER.debug("Cache reset; idle sessions will be removed, in-use sessions will be closed when returned");
if (this.isSharedSessionCapable && ((SharedSessionCapable) this.sessionFactory).isSharedSession()) {
((SharedSessionCapable) this.sessionFactory).resetSharedSession();
}
long epoch = System.nanoTime();
/*
* Spin until we get a new value - nano precision but may be lower resolution. We reset the epoch AFTER
* resetting the shared session so there is no possibility of an "old" session being created in the new
* epoch. There is a slight possibility that a "new" session might appear in the old epoch and thus be
* closed when returned to the cache.
*/
while (epoch == this.sharedSessionEpoch) {
epoch = System.nanoTime();
}
this.sharedSessionEpoch = epoch;
this.pool.removeAllIdleItems();
}
long epoch = System.nanoTime();
/*
* Spin until we get a new value - nano precision but may be lower resolution.
* We reset the epoch AFTER resetting the shared session so there is no possibility
* of an "old" session being created in the new epoch. There is a slight possibility
* that a "new" session might appear in the old epoch and thus be closed when returned to
* the cache.
*/
while (epoch == this.sharedSessionEpoch) {
epoch = System.nanoTime();
finally {
this.lock.unlock();
}
this.sharedSessionEpoch = epoch;
this.pool.removeAllIdleItems();
}
public class CachedSession implements Session<F> { //NOSONAR must be final, but can't for mocking in tests
@@ -174,6 +184,8 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
private boolean dirty;
private final Lock lock = new ReentrantLock();
/**
* The epoch in which this session was created.
*/
@@ -185,35 +197,42 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
}
@Override
public synchronized void close() {
if (this.released) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Session " + this.targetSession + " already released.");
public void close() {
this.lock.lock();
try {
if (this.released) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Session " + this.targetSession + " already released.");
}
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Releasing Session " + this.targetSession + " back to the pool.");
}
if (this.sharedSessionEpoch != CachingSessionFactory.this.sharedSessionEpoch) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Closing session " + this.targetSession + " after reset.");
}
this.targetSession.close();
}
else if (this.dirty) {
this.targetSession.close();
}
if (this.targetSession.isOpen()) {
try {
this.targetSession.finalizeRaw();
}
catch (IOException e) {
// No-op in this context
}
}
CachingSessionFactory.this.pool.releaseItem(this.targetSession);
this.released = true;
}
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Releasing Session " + this.targetSession + " back to the pool.");
}
if (this.sharedSessionEpoch != CachingSessionFactory.this.sharedSessionEpoch) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Closing session " + this.targetSession + " after reset.");
}
this.targetSession.close();
}
else if (this.dirty) {
this.targetSession.close();
}
if (this.targetSession.isOpen()) {
try {
this.targetSession.finalizeRaw();
}
catch (IOException e) {
//No-op in this context
}
}
CachingSessionFactory.this.pool.releaseItem(this.targetSession);
this.released = true;
finally {
this.lock.unlock();
}
}

View File

@@ -21,6 +21,8 @@ import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.cp.CPSubsystem;
@@ -54,6 +56,7 @@ import org.springframework.util.Assert;
* @author Mael Le Guével
* @author Alexey Tsoy
* @author Robert Höglund
* @author Christian Tzolov
*/
public class LeaderInitiator implements SmartLifecycle, DisposableBean, ApplicationEventPublisherAware {
@@ -61,6 +64,8 @@ public class LeaderInitiator implements SmartLifecycle, DisposableBean, Applicat
private static final Context NULL_CONTEXT = new NullContext();
private final Lock lock = new ReentrantLock();
/*** Hazelcast client.
*/
private final HazelcastInstance client;
@@ -208,11 +213,17 @@ public class LeaderInitiator implements SmartLifecycle, DisposableBean, Applicat
* Start the registration of the {@link #candidate} for leader election.
*/
@Override
public synchronized void start() {
if (!this.running) {
this.leaderSelector = new LeaderSelector();
this.running = true;
this.future = this.taskExecutor.submit(this.leaderSelector);
public void start() {
this.lock.lock();
try {
if (!this.running) {
this.leaderSelector = new LeaderSelector();
this.running = true;
this.future = this.taskExecutor.submit(this.leaderSelector);
}
}
finally {
this.lock.unlock();
}
}
@@ -227,13 +238,19 @@ public class LeaderInitiator implements SmartLifecycle, DisposableBean, Applicat
* If the candidate is currently leader, its leadership will be revoked.
*/
@Override
public synchronized void stop() {
if (this.running) {
this.running = false;
if (this.future != null) {
this.future.cancel(true);
public void stop() {
this.lock.lock();
try {
if (this.running) {
this.running = false;
if (this.future != null) {
this.future.cancel(true);
}
this.future = null;
}
this.future = null;
}
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.transform.Source;
@@ -71,6 +73,7 @@ import org.springframework.web.util.DefaultUriBuilderFactory;
* @author Wallace Wadge
* @author Shiliang Li
* @author Florian Schöffl
* @author Christian Tzolov
*
* @since 5.0
*/
@@ -81,6 +84,8 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
protected final DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); // NOSONAR - final
private final Lock lock = new ReentrantLock();
private final Map<String, Expression> uriVariableExpressions = new HashMap<>();
private final Expression uriExpression;
@@ -226,10 +231,14 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
* @param uriVariableExpressions The URI variable expressions.
*/
public void setUriVariableExpressions(Map<String, Expression> uriVariableExpressions) {
synchronized (this.uriVariableExpressions) {
this.lock.lock();
try {
this.uriVariableExpressions.clear();
this.uriVariableExpressions.putAll(uriVariableExpressions);
}
finally {
this.lock.unlock();
}
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.integration.ip;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.support.management.ManageableLifecycle;
@@ -27,11 +29,15 @@ import org.springframework.util.Assert;
* Base class for UDP MessageHandlers.
*
* @author Gary Russell
* @author Christian Tzolov
*
* @since 2.0
*/
public abstract class AbstractInternetProtocolSendingMessageHandler extends AbstractMessageHandler
implements CommonSocketOptions, ManageableLifecycle {
private final Lock lock = new ReentrantLock();
private final SocketAddress destinationAddress;
private final String host;
@@ -119,20 +125,32 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
@Override
public synchronized void start() {
if (!this.running) {
this.doStart();
this.running = true;
public void start() {
this.lock.lock();
try {
if (!this.running) {
this.doStart();
this.running = true;
}
}
finally {
this.lock.unlock();
}
}
protected abstract void doStart();
@Override
public synchronized void stop() {
if (this.running) {
this.doStop();
this.running = false;
public void stop() {
this.lock.lock();
try {
if (this.running) {
this.doStop();
this.running = false;
}
}
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.integration.handler.AbstractMessageHandler;
@@ -48,6 +50,7 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*
@@ -60,7 +63,7 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
*/
public static final long DEFAULT_RETRY_INTERVAL = 60000;
protected final Object lifecycleMonitor = new Object(); // NOSONAR
protected final Lock lifecycleMonitor = new ReentrantLock(); // NOSONAR
private final Map<String, TcpConnection> connections = new ConcurrentHashMap<>();
@@ -251,7 +254,8 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (!this.active) {
this.active = true;
if (this.clientConnectionFactory != null) {
@@ -273,11 +277,15 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
}
}
}
finally {
this.lifecycleMonitor.unlock();
}
}
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (this.active) {
this.active = false;
if (this.scheduledFuture != null) {
@@ -292,6 +300,9 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
}
}
}
finally {
this.lifecycleMonitor.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,6 +42,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
@@ -58,6 +60,7 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*
@@ -73,10 +76,12 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
private static final int DEFAULT_READ_DELAY = 100;
protected final Object lifecycleMonitor = new Object(); // NOSONAR final
protected final Lock lifecycleMonitor = new ReentrantLock(); // NOSONAR final
private final Map<String, TcpConnectionSupport> connections = new ConcurrentHashMap<>();
private final Lock connectionsMonitor = new ReentrantLock();
private final BlockingQueue<PendingIO> delayedReads = new LinkedBlockingQueue<>();
private final List<TcpSender> senders = Collections.synchronizedList(new ArrayList<>());
@@ -546,13 +551,17 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
if (!this.active) {
throw new MessagingException("Connection Factory not started");
}
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (this.taskExecutor == null) {
this.privateExecutor = true;
this.taskExecutor = Executors.newCachedThreadPool();
}
return this.taskExecutor;
}
finally {
this.lifecycleMonitor.unlock();
}
}
/**
@@ -561,7 +570,8 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
@Override
public void stop() {
this.active = false;
synchronized (this.connections) {
this.connectionsMonitor.lock();
try {
Iterator<Entry<String, TcpConnectionSupport>> iterator = this.connections.entrySet().iterator();
while (iterator.hasNext()) {
TcpConnectionSupport connection = iterator.next().getValue();
@@ -575,7 +585,12 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
}
}
}
synchronized (this.lifecycleMonitor) {
finally {
this.connectionsMonitor.unlock();
}
this.lifecycleMonitor.lock();
try {
if (this.privateExecutor) {
ExecutorService executorService = (ExecutorService) this.taskExecutor;
executorService.shutdown();
@@ -598,6 +613,9 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
}
}
}
finally {
this.lifecycleMonitor.unlock();
}
logger.info(() -> "stopped " + this);
}
@@ -849,7 +867,8 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
}
protected void addConnection(TcpConnectionSupport connection) {
synchronized (this.connections) {
this.connectionsMonitor.lock();
try {
if (!this.active) {
connection.close();
return;
@@ -857,6 +876,9 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
this.connections.put(connection.getConnectionId(), connection);
logger.debug(() -> getComponentName() + ": Added new connection: " + connection.getConnectionId());
}
finally {
this.connectionsMonitor.unlock();
}
}
/**
@@ -864,7 +886,8 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
* @return a list of open connection ids.
*/
private List<String> removeClosedConnectionsAndReturnOpenConnectionIds() {
synchronized (this.connections) {
this.connectionsMonitor.lock();
try {
List<String> openConnectionIds = new ArrayList<>();
Iterator<Entry<String, TcpConnectionSupport>> iterator = this.connections.entrySet().iterator();
while (iterator.hasNext()) {
@@ -888,6 +911,9 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
}
return openConnectionIds;
}
finally {
this.connectionsMonitor.unlock();
}
}
/**
@@ -948,7 +974,8 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
public boolean closeConnection(String connectionId) {
Assert.notNull(connectionId, "'connectionId' to close must not be null");
// closed connections are removed from #connections in #harvestClosedConnections()
synchronized (this.connections) {
this.connectionsMonitor.lock();
try {
boolean closed = false;
TcpConnectionSupport connection = this.connections.remove(connectionId);
if (connection != null) {
@@ -964,6 +991,9 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
}
return closed;
}
finally {
this.connectionsMonitor.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2001-2022 the original author or authors.
* Copyright 2001-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -75,13 +76,17 @@ public abstract class AbstractServerConnectionFactory extends AbstractConnection
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (!isActive()) {
this.setActive(true);
this.shuttingDown = false;
getTaskExecutor().execute(this);
}
}
finally {
this.lifecycleMonitor.unlock();
}
super.start();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.serializer.Deserializer;
@@ -39,11 +41,15 @@ import org.springframework.messaging.support.ErrorMessage;
* false, or cache starvation will result.
*
* @author Gary Russell
* @author Christian Tzolov
*
* @since 2.2
*
*/
public class CachingClientConnectionFactory extends AbstractClientConnectionFactory implements DisposableBean {
private final Lock lock = new ReentrantLock();
private final AbstractClientConnectionFactory targetConnectionFactory;
private final SimplePool<TcpConnectionSupport> pool;
@@ -385,9 +391,15 @@ public class CachingClientConnectionFactory extends AbstractClientConnectionFact
}
@Override
public synchronized void stop() {
this.targetConnectionFactory.stop();
this.pool.removeAllIdleItems();
public void stop() {
this.lock.lock();
try {
this.targetConnectionFactory.stop();
this.pool.removeAllIdleItems();
}
finally {
this.lock.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.ip.tcp.connection;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -31,6 +33,7 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.1
*
@@ -39,6 +42,8 @@ public class ClientModeConnectionManager implements Runnable {
private final Log logger = LogFactory.getLog(this.getClass());
private final Lock lock = new ReentrantLock();
private final AbstractConnectionFactory clientConnectionFactory;
private volatile TcpConnection lastConnection;
@@ -54,7 +59,8 @@ public class ClientModeConnectionManager implements Runnable {
@Override
public void run() {
synchronized (this.clientConnectionFactory) {
this.lock.lock();
try {
try {
TcpConnection connection = this.clientConnectionFactory.getConnection();
if (!Objects.equals(connection, this.lastConnection)) {
@@ -73,6 +79,9 @@ public class ClientModeConnectionManager implements Runnable {
this.logger.error("Could not establish connection using " + this.clientConnectionFactory, ex);
}
}
finally {
this.lock.unlock();
}
}
public boolean isConnected() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.SSLSession;
@@ -37,6 +39,8 @@ import org.springframework.util.Assert;
* succeeds or the list is exhausted.
*
* @author Gary Russell
* @author Christian Tzolov
*
* @since 2.2
*
*/
@@ -224,6 +228,8 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
*/
private final class FailoverTcpConnection extends TcpConnectionSupport implements TcpListener {
private final Lock lock = new ReentrantLock();
private final List<AbstractClientConnectionFactory> connectionFactories;
private final String connectionId;
@@ -257,45 +263,50 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
* factories are down.
* @throws InterruptedException if interrupted.
*/
private synchronized void findAConnection() throws InterruptedException {
boolean success = false;
AbstractClientConnectionFactory lastFactoryToTry = this.currentFactory;
AbstractClientConnectionFactory nextFactory = null;
if (!this.factoryIterator.hasNext()) {
this.factoryIterator = this.connectionFactories.iterator();
private void findAConnection() throws InterruptedException {
this.lock.lock();
try {
boolean success = false;
AbstractClientConnectionFactory lastFactoryToTry = this.currentFactory;
AbstractClientConnectionFactory nextFactory = null;
if (!this.factoryIterator.hasNext()) {
this.factoryIterator = this.connectionFactories.iterator();
}
boolean restartedList = false;
while (!success) {
try {
nextFactory = this.factoryIterator.next();
this.delegate = nextFactory.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Got " + this.delegate.getConnectionId() + " from " + nextFactory);
}
this.delegate.registerListener(this);
this.currentFactory = nextFactory;
success = this.delegate.isOpen();
}
catch (RuntimeException e) {
if (logger.isDebugEnabled()) {
logger.debug(nextFactory + " failed with "
+ e.toString()
+ ", trying another");
}
if (restartedList && (lastFactoryToTry == null || lastFactoryToTry.equals(nextFactory))) {
logger.debug("Failover failed to find a connection");
/*
* We've tried every factory including the one the current connection was on.
*/
this.open = false;
throw e;
}
if (!this.factoryIterator.hasNext()) {
this.factoryIterator = this.connectionFactories.iterator();
restartedList = true;
}
}
}
}
boolean restartedList = false;
while (!success) {
try {
nextFactory = this.factoryIterator.next();
this.delegate = nextFactory.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Got " + this.delegate.getConnectionId() + " from " + nextFactory);
}
this.delegate.registerListener(this);
this.currentFactory = nextFactory;
success = this.delegate.isOpen();
}
catch (RuntimeException e) {
if (logger.isDebugEnabled()) {
logger.debug(nextFactory + " failed with "
+ e.toString()
+ ", trying another");
}
if (restartedList && (lastFactoryToTry == null || lastFactoryToTry.equals(nextFactory))) {
logger.debug("Failover failed to find a connection");
/*
* We've tried every factory including the
* one the current connection was on.
*/
this.open = false;
throw e;
}
if (!this.factoryIterator.hasNext()) {
this.factoryIterator = this.connectionFactories.iterator();
restartedList = true;
}
}
finally {
this.lock.unlock();
}
}
@@ -316,39 +327,46 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
* If send fails on a connection from every factory, we give up.
*/
@Override
public synchronized void send(Message<?> message) {
boolean success = false;
AbstractClientConnectionFactory lastFactoryToTry = this.currentFactory;
AbstractClientConnectionFactory lastFactoryTried = null;
boolean retried = false;
while (!success) {
try {
lastFactoryTried = this.currentFactory;
this.delegate.send(message);
success = true;
}
catch (RuntimeException e) {
if (retried && lastFactoryTried.equals(lastFactoryToTry)) {
logger.error("All connection factories exhausted", e);
this.open = false;
throw e;
}
retried = true;
if (logger.isDebugEnabled()) {
logger.debug("Send to " + this.delegate.getConnectionId() + " failed; attempting failover", e);
}
this.delegate.close();
public void send(Message<?> message) {
this.lock.lock();
try {
boolean success = false;
AbstractClientConnectionFactory lastFactoryToTry = this.currentFactory;
AbstractClientConnectionFactory lastFactoryTried = null;
boolean retried = false;
while (!success) {
try {
findAConnection();
lastFactoryTried = this.currentFactory;
this.delegate.send(message);
success = true;
}
catch (@SuppressWarnings("unused") InterruptedException e1) {
Thread.currentThread().interrupt();
}
if (logger.isDebugEnabled()) {
logger.debug("Failing over to " + this.delegate.getConnectionId());
catch (RuntimeException e) {
if (retried && lastFactoryTried.equals(lastFactoryToTry)) {
logger.error("All connection factories exhausted", e);
this.open = false;
throw e;
}
retried = true;
if (logger.isDebugEnabled()) {
logger.debug("Send to " + this.delegate.getConnectionId() + " failed; attempting failover",
e);
}
this.delegate.close();
try {
findAConnection();
}
catch (@SuppressWarnings("unused") InterruptedException e1) {
Thread.currentThread().interrupt();
}
if (logger.isDebugEnabled()) {
logger.debug("Failing over to " + this.delegate.getConnectionId());
}
}
}
}
finally {
this.lock.unlock();
}
}
@Override

View File

@@ -18,6 +18,8 @@ package org.springframework.integration.ip.tcp.connection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.SSLSession;
@@ -34,11 +36,14 @@ import org.springframework.messaging.support.ErrorMessage;
*
* @author Gary Russell
* @author Kazuki Shimizu
* @author Christian Tzolov
*
* @since 2.0
*/
public abstract class TcpConnectionInterceptorSupport extends TcpConnectionSupport implements TcpConnectionInterceptor {
private final Lock lock = new ReentrantLock();
private TcpConnectionSupport theConnection;
private TcpListener tcpListener;
@@ -238,17 +243,23 @@ public abstract class TcpConnectionInterceptorSupport extends TcpConnectionSuppo
}
@Override
public synchronized void removeDeadConnection(TcpConnection connection) {
if (this.removed) {
return;
public void removeDeadConnection(TcpConnection connection) {
this.lock.lock();
try {
if (this.removed) {
return;
}
this.removed = true;
if (this.theConnection instanceof TcpConnectionInterceptorSupport && !this.theConnection.equals(this)) {
((TcpConnectionInterceptorSupport) this.theConnection).removeDeadConnection(this);
}
TcpSender sender = getSender();
if (sender != null && !(sender instanceof TcpConnectionInterceptorSupport)) {
this.interceptedSenders.forEach(snder -> snder.removeDeadConnection(connection));
}
}
this.removed = true;
if (this.theConnection instanceof TcpConnectionInterceptorSupport && !this.theConnection.equals(this)) {
((TcpConnectionInterceptorSupport) this.theConnection).removeDeadConnection(this);
}
TcpSender sender = getSender();
if (sender != null && !(sender instanceof TcpConnectionInterceptorSupport)) {
this.interceptedSenders.forEach(snder -> snder.removeDeadConnection(connection));
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2001-2021 the original author or authors.
* Copyright 2001-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import java.io.UncheckedIOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import javax.net.ssl.SSLSession;
@@ -43,12 +45,15 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*
*/
public class TcpNetConnection extends TcpConnectionSupport implements SchedulingAwareRunnable {
private final Lock lock = new ReentrantLock();
private final Socket socket;
private volatile OutputStream socketOutputStream;
@@ -102,7 +107,8 @@ public class TcpNetConnection extends TcpConnectionSupport implements Scheduling
@Override
@SuppressWarnings("unchecked")
public synchronized void send(Message<?> message) {
public void send(Message<?> message) {
this.lock.lock();
try {
if (this.socketOutputStream == null) {
int writeBufferSize = this.socket.getSendBufferSize();
@@ -121,6 +127,9 @@ public class TcpNetConnection extends TcpConnectionSupport implements Scheduling
closeConnection(true);
throw mex;
}
finally {
this.lock.unlock();
}
if (logger.isDebugEnabled()) {
logger.debug(getConnectionId() + " Message sent " + message);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*
@@ -170,12 +171,16 @@ public class TcpNioClientConnectionFactory extends
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (!isActive()) {
setActive(true);
getTaskExecutor().execute(this);
}
}
finally {
this.lifecycleMonitor.unlock();
}
super.start();
}

View File

@@ -37,6 +37,8 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.SSLSession;
@@ -56,6 +58,7 @@ import org.springframework.util.Assert;
* @author John Anderson
* @author Artem Bilan
* @author David Herschler Shvo
* @author Christian Tzolov
*
* @since 2.0
*
@@ -72,14 +75,20 @@ public class TcpNioConnection extends TcpConnectionSupport {
private static final byte[] EOF = new byte[0]; // EOF marker buffer
private final Lock lock = new ReentrantLock();
private final SocketChannel socketChannel;
private final Lock socketChannelMonitor = new ReentrantLock();
private final ChannelOutputStream channelOutputStream = new ChannelOutputStream();
private final ChannelInputStream channelInputStream = new ChannelInputStream();
private final AtomicInteger executionControl = new AtomicInteger();
private final Lock executionControlMonitor = new ReentrantLock();
private boolean usingDirectBuffers;
private long pipeTimeout = DEFAULT_PIPE_TIMEOUT;
@@ -154,7 +163,8 @@ public class TcpNioConnection extends TcpConnectionSupport {
@Override
@SuppressWarnings("unchecked")
public void send(Message<?> message) {
synchronized (this.socketChannel) {
this.socketChannelMonitor.lock();
try {
try {
if (this.bufferedOutputStream == null) {
int writeBufferSize = this.socketChannel.socket().getSendBufferSize();
@@ -177,6 +187,9 @@ public class TcpNioConnection extends TcpConnectionSupport {
logger.debug(getConnectionId() + " Message sent " + message);
}
}
finally {
this.socketChannelMonitor.unlock();
}
}
@Override
@@ -311,7 +324,8 @@ public class TcpNioConnection extends TcpConnectionSupport {
// timing was such that we were the last assembler and
// a new one wasn't run
if (dataAvailable()) {
synchronized (this.executionControl) {
this.executionControlMonitor.lock();
try {
if (this.executionControl.incrementAndGet() <= 1) {
// only continue if we don't already have another assembler running
this.executionControl.set(1);
@@ -322,6 +336,9 @@ public class TcpNioConnection extends TcpConnectionSupport {
this.executionControl.decrementAndGet();
}
}
finally {
this.executionControlMonitor.unlock();
}
}
if (moreDataAvailable) {
if (logger.isTraceEnabled()) {
@@ -352,43 +369,50 @@ public class TcpNioConnection extends TcpConnectionSupport {
* @throws IOException an IO exception
*/
@Nullable
private synchronized Message<?> convert() throws IOException {
if (logger.isTraceEnabled()) {
logger.trace(getConnectionId() + " checking data avail (convert): " + this.channelInputStream.available() +
" pending: " + (this.writingToPipe));
}
if (this.channelInputStream.available() <= 0) {
try {
if (this.writingLatch.await(SIXTY, TimeUnit.SECONDS)) {
if (this.channelInputStream.available() <= 0) {
return null;
private Message<?> convert() throws IOException {
this.lock.lock();
try {
if (logger.isTraceEnabled()) {
logger.trace(
getConnectionId() + " checking data avail (convert): " + this.channelInputStream.available() +
" pending: " + (this.writingToPipe));
}
if (this.channelInputStream.available() <= 0) {
try {
if (this.writingLatch.await(SIXTY, TimeUnit.SECONDS)) {
if (this.channelInputStream.available() <= 0) {
return null;
}
}
else { // should never happen
throw new IOException("Timed out waiting for IO");
}
}
else { // should never happen
throw new IOException("Timed out waiting for IO");
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted waiting for IO", e);
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted waiting for IO", e);
try {
return getMapper().toMessage(this);
}
catch (Exception e) {
closeConnection(true);
if (e instanceof SocketTimeoutException) { // NOSONAR instanceof
if (logger.isDebugEnabled()) {
logger.debug("Closing socket after timeout " + getConnectionId());
}
}
else {
if (!(e instanceof SoftEndOfStreamException)) { // NOSONAR instanceof
throw e;
}
}
return null;
}
}
try {
return getMapper().toMessage(this);
}
catch (Exception e) {
closeConnection(true);
if (e instanceof SocketTimeoutException) { // NOSONAR instanceof
if (logger.isDebugEnabled()) {
logger.debug("Closing socket after timeout " + getConnectionId());
}
}
else {
if (!(e instanceof SoftEndOfStreamException)) { // NOSONAR instanceof
throw e;
}
}
return null;
finally {
this.lock.unlock();
}
}
@@ -468,7 +492,8 @@ public class TcpNioConnection extends TcpConnectionSupport {
}
private void checkForAssembler() {
synchronized (this.executionControl) {
this.executionControlMonitor.lock();
try {
if (this.executionControl.incrementAndGet() <= 1) {
// only execute run() if we don't already have one running
this.executionControl.set(1);
@@ -489,6 +514,9 @@ public class TcpNioConnection extends TcpConnectionSupport {
this.executionControl.decrementAndGet();
}
}
finally {
this.executionControlMonitor.unlock();
}
}
/**
@@ -605,6 +633,8 @@ public class TcpNioConnection extends TcpConnectionSupport {
private int soTimeout;
private final Lock innerLock = new ReentrantLock();
@Override
public void write(int b) throws IOException {
byte[] bytes = new byte[1];
@@ -632,28 +662,35 @@ public class TcpNioConnection extends TcpConnectionSupport {
doWrite(buffer);
}
protected synchronized void doWrite(ByteBuffer buffer) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug(getConnectionId() + " writing " + buffer.remaining());
}
TcpNioConnection.this.socketChannel.write(buffer);
int remaining = buffer.remaining();
if (remaining == 0) {
return;
}
if (this.selector == null) {
this.selector = Selector.open();
this.soTimeout = TcpNioConnection.this.socketChannel.socket().getSoTimeout();
}
TcpNioConnection.this.socketChannel.register(this.selector, SelectionKey.OP_WRITE);
while (remaining > 0) {
int selectionCount = this.selector.select(this.soTimeout);
if (selectionCount == 0) {
throw new SocketTimeoutException("Timeout on write");
protected void doWrite(ByteBuffer buffer) throws IOException {
this.innerLock.lock();
try {
if (logger.isDebugEnabled()) {
logger.debug(getConnectionId() + " writing " + buffer.remaining());
}
this.selector.selectedKeys().clear();
TcpNioConnection.this.socketChannel.write(buffer);
remaining = buffer.remaining();
int remaining = buffer.remaining();
if (remaining == 0) {
return;
}
if (this.selector == null) {
this.selector = Selector.open();
this.soTimeout = TcpNioConnection.this.socketChannel.socket().getSoTimeout();
}
TcpNioConnection.this.socketChannel.register(this.selector, SelectionKey.OP_WRITE);
while (remaining > 0) {
int selectionCount = this.selector.select(this.soTimeout);
if (selectionCount == 0) {
throw new SocketTimeoutException("Timeout on write");
}
this.selector.selectedKeys().clear();
TcpNioConnection.this.socketChannel.write(buffer);
remaining = buffer.remaining();
}
}
finally {
this.innerLock.unlock();
}
}
@@ -680,6 +717,8 @@ public class TcpNioConnection extends TcpConnectionSupport {
private volatile boolean isClosed;
private final Lock innerLock = new ReentrantLock();
@Override
public int read(byte[] b, int off, int len) throws IOException {
Assert.notNull(b, "byte[] cannot be null");
@@ -708,30 +747,36 @@ public class TcpNioConnection extends TcpConnectionSupport {
}
@Override
public synchronized int read() throws IOException {
if (this.isClosed && this.available.get() == 0) {
if (TcpNioConnection.this.timedOut) {
throw new SocketTimeoutException("Connection has timed out");
}
return -1;
}
if (this.currentBuffer == null) {
this.currentBuffer = getNextBuffer();
this.currentOffset = 0;
if (this.currentBuffer == null) {
public int read() throws IOException {
this.innerLock.lock();
try {
if (this.isClosed && this.available.get() == 0) {
if (TcpNioConnection.this.timedOut) {
throw new SocketTimeoutException("Connection has timed out");
}
return -1;
}
if (this.currentBuffer == null) {
this.currentBuffer = getNextBuffer();
this.currentOffset = 0;
if (this.currentBuffer == null) {
if (TcpNioConnection.this.timedOut) {
throw new SocketTimeoutException("Connection has timed out");
}
return -1;
}
}
int bite;
bite = this.currentBuffer[this.currentOffset++] & 0xff; // NOSONAR
this.available.decrementAndGet();
if (this.currentOffset >= this.currentBuffer.length) {
this.currentBuffer = null;
}
return bite;
}
int bite;
bite = this.currentBuffer[this.currentOffset++] & 0xff; // NOSONAR
this.available.decrementAndGet();
if (this.currentOffset >= this.currentBuffer.length) {
this.currentBuffer = null;
finally {
this.innerLock.unlock();
}
return bite;
}
@Nullable

View File

@@ -21,6 +21,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
@@ -51,6 +53,7 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.2
*
@@ -67,7 +70,7 @@ public class TcpNioSSLConnection extends TcpNioConnection {
private final Semaphore semaphore = new Semaphore(0);
private final Object monitorLock = new Object();
private final Lock monitorLock = new ReentrantLock();
private int handshakeTimeout = DEFAULT_HANDSHAKE_TIMEOUT;
@@ -285,12 +288,16 @@ public class TcpNioSSLConnection extends TcpNioConnection {
@Override
protected ChannelOutputStream getChannelOutputStream() {
synchronized (this.monitorLock) {
this.monitorLock.lock();
try {
if (this.sslChannelOutputStream == null) {
this.sslChannelOutputStream = new SSLChannelOutputStream(super.getChannelOutputStream());
}
return this.sslChannelOutputStream;
}
finally {
this.monitorLock.unlock();
}
}
protected SSLChannelOutputStream getSSLChannelOutputStream() {
@@ -318,6 +325,8 @@ public class TcpNioSSLConnection extends TcpNioConnection {
*/
final class SSLChannelOutputStream extends ChannelOutputStream {
private final Lock lock = new ReentrantLock();
private final ChannelOutputStream channelOutputStream;
SSLChannelOutputStream(ChannelOutputStream channelOutputStream) {
@@ -331,7 +340,8 @@ public class TcpNioSSLConnection extends TcpNioConnection {
* and multiple writes will be necessary.
*/
@Override
protected synchronized void doWrite(ByteBuffer plainText) throws IOException {
protected void doWrite(ByteBuffer plainText) throws IOException {
this.lock.lock();
try {
TcpNioSSLConnection.this.writerActive = true;
int remaining = plainText.remaining();
@@ -357,6 +367,7 @@ public class TcpNioSSLConnection extends TcpNioConnection {
}
finally {
TcpNioSSLConnection.this.writerActive = false;
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ import org.springframework.messaging.MessagingException;
* @author Gary Russell
* @author Marcin Pilaczynski
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -39,7 +40,6 @@ public class MulticastReceivingChannelAdapter extends UnicastReceivingChannelAda
private final String group;
/**
* Constructs a MulticastReceivingChannelAdapter that listens for packets on the
* specified multichannel address (group) and port.
@@ -65,24 +65,31 @@ public class MulticastReceivingChannelAdapter extends UnicastReceivingChannelAda
}
@Override
public synchronized DatagramSocket getSocket() {
if (getTheSocket() == null) {
try {
int port = getPort();
MulticastSocket socket = port == 0 ? new MulticastSocket() : new MulticastSocket(port);
String localAddress = getLocalAddress();
if (localAddress != null) {
socket.setNetworkInterface(NetworkInterface.getByInetAddress(InetAddress.getByName(localAddress)));
public DatagramSocket getSocket() {
this.lock.lock();
try {
if (getTheSocket() == null) {
try {
int port = getPort();
MulticastSocket socket = port == 0 ? new MulticastSocket() : new MulticastSocket(port);
String localAddress = getLocalAddress();
if (localAddress != null) {
socket.setNetworkInterface(
NetworkInterface.getByInetAddress(InetAddress.getByName(localAddress)));
}
setSocketAttributes(socket);
socket.joinGroup(new InetSocketAddress(this.group, 0), null);
setSocket(socket);
}
catch (IOException e) {
throw new MessagingException("failed to create DatagramSocket", e);
}
setSocketAttributes(socket);
socket.joinGroup(new InetSocketAddress(this.group, 0), null);
setSocket(socket);
}
catch (IOException e) {
throw new MessagingException("failed to create DatagramSocket", e);
}
return super.getSocket();
}
finally {
this.lock.unlock();
}
return super.getSocket();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2001-2020 the original author or authors.
* Copyright 2001-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ import org.springframework.messaging.Message;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -129,11 +130,17 @@ public class MulticastSendingMessageHandler extends UnicastSendingMessageHandler
}
@Override
protected synchronized DatagramSocket getSocket() throws IOException {
if (getTheSocket() == null) {
createSocket();
protected DatagramSocket getSocket() throws IOException {
this.lock.lock();
try {
if (getTheSocket() == null) {
createSocket();
}
return super.getSocket();
}
finally {
this.lock.unlock();
}
return super.getSocket();
}
private void createSocket() throws IOException {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@ import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,6 +46,7 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -53,6 +56,8 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
private final DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
protected final Lock lock = new ReentrantLock();
private DatagramSocket socket;
private boolean socketExplicitlySet;
@@ -248,27 +253,33 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
return this.socket;
}
public synchronized DatagramSocket getSocket() {
if (this.socket == null) {
try {
DatagramSocket datagramSocket;
String localAddress = getLocalAddress();
int port = super.getPort();
if (localAddress == null) {
datagramSocket = port == 0 ? new DatagramSocket() : new DatagramSocket(port);
public DatagramSocket getSocket() {
this.lock.lock();
try {
if (this.socket == null) {
try {
DatagramSocket datagramSocket;
String localAddress = getLocalAddress();
int port = super.getPort();
if (localAddress == null) {
datagramSocket = port == 0 ? new DatagramSocket() : new DatagramSocket(port);
}
else {
InetAddress whichNic = InetAddress.getByName(localAddress);
datagramSocket = new DatagramSocket(new InetSocketAddress(whichNic, port));
}
setSocketAttributes(datagramSocket);
this.socket = datagramSocket;
}
else {
InetAddress whichNic = InetAddress.getByName(localAddress);
datagramSocket = new DatagramSocket(new InetSocketAddress(whichNic, port));
catch (IOException e) {
throw new MessagingException("failed to create DatagramSocket", e);
}
setSocketAttributes(datagramSocket);
this.socket = datagramSocket;
}
catch (IOException e) {
throw new MessagingException("failed to create DatagramSocket", e);
}
return this.socket;
}
finally {
this.lock.unlock();
}
return this.socket;
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2001-2022 the original author or authors.
* Copyright 2001-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,8 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
@@ -58,6 +60,7 @@ import org.springframework.util.StringUtils;
* @author Gary Russell
* @author Marcin Pilaczynski
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
@@ -66,6 +69,8 @@ public class UnicastSendingMessageHandler extends
private static final int DEFAULT_ACK_TIMEOUT = 5000;
protected final Lock lock = new ReentrantLock();
private final DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
private final Map<String, CountDownLatch> ackControl = Collections.synchronizedMap(new HashMap<>());
@@ -316,7 +321,8 @@ public class UnicastSendingMessageHandler extends
public void startAckThread() {
if (!this.ackThreadRunning) {
synchronized (this) {
this.lock.lock();
try {
if (!this.ackThreadRunning) {
try {
getSocket();
@@ -334,6 +340,9 @@ public class UnicastSendingMessageHandler extends
}
}
}
finally {
this.lock.unlock();
}
}
}
@@ -386,28 +395,34 @@ public class UnicastSendingMessageHandler extends
return this.socket;
}
protected synchronized DatagramSocket getSocket() throws IOException {
if (this.socket == null) {
if (this.acknowledge) {
if (this.localAddress == null) {
this.socket = this.ackPort == 0 ? new DatagramSocket() : new DatagramSocket(this.ackPort);
protected DatagramSocket getSocket() throws IOException {
this.lock.lock();
try {
if (this.socket == null) {
if (this.acknowledge) {
if (this.localAddress == null) {
this.socket = this.ackPort == 0 ? new DatagramSocket() : new DatagramSocket(this.ackPort);
}
else {
InetAddress whichNic = InetAddress.getByName(this.localAddress);
this.socket = new DatagramSocket(new InetSocketAddress(whichNic, this.ackPort));
}
if (this.soReceiveBufferSize > 0) {
this.socket.setReceiveBufferSize(this.soReceiveBufferSize);
}
logger.debug(() -> "Listening for acks on port: " + getAckPort());
updateAckAddress();
}
else {
InetAddress whichNic = InetAddress.getByName(this.localAddress);
this.socket = new DatagramSocket(new InetSocketAddress(whichNic, this.ackPort));
this.socket = new DatagramSocket();
}
if (this.soReceiveBufferSize > 0) {
this.socket.setReceiveBufferSize(this.soReceiveBufferSize);
}
logger.debug(() -> "Listening for acks on port: " + getAckPort());
updateAckAddress();
setSocketAttributes(this.socket);
}
else {
this.socket = new DatagramSocket();
}
setSocketAttributes(this.socket);
return this.socket;
}
finally {
this.lock.unlock();
}
return this.socket;
}
protected void updateAckAddress() {
@@ -424,8 +439,14 @@ public class UnicastSendingMessageHandler extends
}
@Override
public synchronized void setLocalAddress(String localAddress) {
this.localAddress = localAddress;
public void setLocalAddress(String localAddress) {
this.lock.lock();
try {
this.localAddress = localAddress;
}
finally {
this.lock.unlock();
}
}
public void setTaskExecutor(Executor taskExecutor) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.sql.DataSource;
@@ -51,6 +53,7 @@ import org.springframework.util.Assert;
* @author Gunnar Hillert
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 2.1
*
@@ -63,7 +66,7 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
private final DataSource dataSource;
private final Object jdbcCallOperationsMapMonitor = new Object();
private final Lock jdbcCallOperationsMapMonitor = new ReentrantLock();
private Map<String, RowMapper<?>> returningResultSetRowMappers = new HashMap<>(0);
@@ -301,10 +304,14 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
private SimpleJdbcCallOperations obtainSimpleJdbcCall(String storedProcedureName) {
SimpleJdbcCallOperations operations = this.jdbcCallOperationsMap.get(storedProcedureName);
if (operations == null) {
synchronized (this.jdbcCallOperationsMapMonitor) {
this.jdbcCallOperationsMapMonitor.lock();
try {
operations =
this.jdbcCallOperationsMap.computeIfAbsent(storedProcedureName, this::createSimpleJdbcCall);
}
finally {
this.jdbcCallOperationsMapMonitor.unlock();
}
}
return operations;
}

View File

@@ -26,6 +26,8 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.postgresql.PGNotification;
import org.postgresql.jdbc.PgConnection;
@@ -61,6 +63,7 @@ import org.springframework.util.Assert;
* @author Rafael Winterhalter
* @author Artem Bilan
* @author Igor Lovich
* @author Christian Tzolov
*
* @since 6.0
*/
@@ -68,6 +71,8 @@ public final class PostgresChannelMessageTableSubscriber implements SmartLifecyc
private static final LogAccessor LOGGER = new LogAccessor(PostgresChannelMessageTableSubscriber.class);
private final Lock lock = new ReentrantLock();
private final Map<String, Set<Subscription>> subscriptionsMap = new ConcurrentHashMap<>();
private final PgConnectionSupplier connectionSupplier;
@@ -111,8 +116,14 @@ public final class PostgresChannelMessageTableSubscriber implements SmartLifecyc
* @deprecated since 6.2 in favor of {@link #setTaskExecutor(AsyncTaskExecutor)}
*/
@Deprecated(since = "6.2", forRemoval = true)
public synchronized void setExecutor(ExecutorService executor) {
setTaskExecutor(new TaskExecutorAdapter(executor));
public void setExecutor(ExecutorService executor) {
this.lock.lock();
try {
setTaskExecutor(new TaskExecutorAdapter(executor));
}
finally {
this.lock.unlock();
}
}
/**
@@ -149,85 +160,96 @@ public final class PostgresChannelMessageTableSubscriber implements SmartLifecyc
}
@Override
public synchronized void start() {
if (this.latch.getCount() > 0) {
return;
}
public void start() {
this.lock.lock();
try {
if (this.latch.getCount() > 0) {
return;
}
this.latch = new CountDownLatch(1);
this.latch = new CountDownLatch(1);
CountDownLatch startingLatch = new CountDownLatch(1);
this.future = this.taskExecutor.submit(() -> {
doStart(startingLatch);
});
CountDownLatch startingLatch = new CountDownLatch(1);
this.future = this.taskExecutor.submit(() -> {
try {
while (isActive()) {
try {
PgConnection conn = this.connectionSupplier.get();
try (Statement stmt = conn.createStatement()) {
stmt.execute("LISTEN " + this.tablePrefix.toLowerCase() + "channel_message_notify");
}
catch (Exception ex) {
try {
conn.close();
}
catch (Exception suppressed) {
ex.addSuppressed(suppressed);
}
throw ex;
}
this.subscriptionsMap.values()
.forEach(subscriptions -> subscriptions.forEach(Subscription::notifyUpdate));
try {
this.connection = conn;
while (isActive()) {
startingLatch.countDown();
if (!startingLatch.await(5, TimeUnit.SECONDS)) {
throw new IllegalStateException("Failed to start " + this);
}
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException("Failed to start " + this, ex);
}
}
finally {
this.lock.unlock();
}
}
PGNotification[] notifications = conn.getNotifications(0);
// Unfortunately, there is no good way of interrupting a notification
// poll but by closing its connection.
if (!isActive()) {
return;
}
if (notifications != null) {
for (PGNotification notification : notifications) {
String parameter = notification.getParameter();
Set<Subscription> subscriptions = this.subscriptionsMap.get(parameter);
if (subscriptions == null) {
continue;
}
for (Subscription subscription : subscriptions) {
subscription.notifyUpdate();
}
private void doStart(CountDownLatch startingLatch) {
try {
while (isActive()) {
try {
PgConnection conn = this.connectionSupplier.get();
try (Statement stmt = conn.createStatement()) {
stmt.execute("LISTEN " + this.tablePrefix.toLowerCase() + "channel_message_notify");
}
catch (Exception ex) {
try {
conn.close();
}
catch (Exception suppressed) {
ex.addSuppressed(suppressed);
}
throw ex;
}
this.subscriptionsMap.values()
.forEach(subscriptions -> subscriptions.forEach(Subscription::notifyUpdate));
try {
this.connection = conn;
while (isActive()) {
startingLatch.countDown();
PGNotification[] notifications = conn.getNotifications(0);
// Unfortunately, there is no good way of interrupting a notification
// poll but by closing its connection.
if (!isActive()) {
return;
}
if (notifications != null) {
for (PGNotification notification : notifications) {
String parameter = notification.getParameter();
Set<Subscription> subscriptions = this.subscriptionsMap.get(parameter);
if (subscriptions == null) {
continue;
}
for (Subscription subscription : subscriptions) {
subscription.notifyUpdate();
}
}
}
}
finally {
conn.close();
}
}
catch (Exception e) {
// The getNotifications method does not throw a meaningful message on interruption.
// Therefore, we do not log an error, unless it occurred while active.
if (isActive()) {
LOGGER.error(e, "Failed to poll notifications from Postgres database");
}
finally {
conn.close();
}
}
catch (Exception e) {
// The getNotifications method does not throw a meaningful message on interruption.
// Therefore, we do not log an error, unless it occurred while active.
if (isActive()) {
LOGGER.error(e, "Failed to poll notifications from Postgres database");
}
}
}
finally {
this.latch.countDown();
}
});
}
finally {
this.latch.countDown();
}
try {
if (!startingLatch.await(5, TimeUnit.SECONDS)) {
throw new IllegalStateException("Failed to start " + this);
}
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException("Failed to start " + this, ex);
}
}
private boolean isActive() {
@@ -239,25 +261,31 @@ public final class PostgresChannelMessageTableSubscriber implements SmartLifecyc
}
@Override
public synchronized void stop() {
if (this.future.isDone()) {
return;
}
this.future.cancel(true);
PgConnection conn = this.connection;
if (conn != null) {
try {
conn.close();
}
catch (SQLException ignored) {
}
}
public void stop() {
this.lock.lock();
try {
if (!this.latch.await(5, TimeUnit.SECONDS)) {
throw new IllegalStateException("Failed to stop " + this);
if (this.future.isDone()) {
return;
}
this.future.cancel(true);
PgConnection conn = this.connection;
if (conn != null) {
try {
conn.close();
}
catch (SQLException ignored) {
}
}
try {
if (!this.latch.await(5, TimeUnit.SECONDS)) {
throw new IllegalStateException("Failed to stop " + this);
}
}
catch (InterruptedException ignored) {
}
}
catch (InterruptedException ignored) {
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,6 +54,7 @@ import org.springframework.util.Assert;
* @author Olivier Hubaut
* @author Fran Aranda
* @author Unseok Kim
* @author Christian Tzolov
*
* @since 4.3
*/
@@ -63,6 +64,8 @@ public class JdbcLockRegistry implements ExpirableLockRegistry, RenewableLockReg
private static final int DEFAULT_CAPACITY = 100_000;
private final Lock lock = new ReentrantLock();
private final Map<String, JdbcLock> locks =
new LinkedHashMap<String, JdbcLock>(16, 0.75F, true) {
@@ -111,9 +114,13 @@ public class JdbcLockRegistry implements ExpirableLockRegistry, RenewableLockReg
public Lock obtain(Object lockKey) {
Assert.isInstanceOf(String.class, lockKey);
String path = pathFor((String) lockKey);
synchronized (this.locks) {
this.lock.lock();
try {
return this.locks.computeIfAbsent(path, key -> new JdbcLock(this.client, this.idleBetweenTries, key));
}
finally {
this.lock.unlock();
}
}
private String pathFor(String input) {
@@ -123,13 +130,17 @@ public class JdbcLockRegistry implements ExpirableLockRegistry, RenewableLockReg
@Override
public void expireUnusedOlderThan(long age) {
long now = System.currentTimeMillis();
synchronized (this.locks) {
this.lock.lock();
try {
this.locks.entrySet()
.removeIf(entry -> {
JdbcLock lock = entry.getValue();
return now - lock.getLastUsed() > age && !lock.isAcquiredInThisProcess();
});
}
finally {
this.lock.unlock();
}
}
@Override
@@ -137,9 +148,14 @@ public class JdbcLockRegistry implements ExpirableLockRegistry, RenewableLockReg
Assert.isInstanceOf(String.class, lockKey);
String path = pathFor((String) lockKey);
JdbcLock jdbcLock;
synchronized (this.locks) {
this.lock.lock();
try {
jdbcLock = this.locks.get(path);
}
finally {
this.lock.unlock();
}
if (jdbcLock == null) {
throw new IllegalStateException("Could not found mutex at " + path);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,8 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import jakarta.jms.Connection;
import jakarta.jms.ConnectionFactory;
@@ -81,6 +83,7 @@ import org.springframework.util.StringUtils;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
implements ManageableLifecycle, MessageListener {
@@ -90,7 +93,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
*/
public static final long DEFAULT_RECEIVE_TIMEOUT = 5000L;
private final Object initializationMonitor = new Object();
private final Lock initializationMonitor = new ReentrantLock();
private final AtomicLong correlationId = new AtomicLong();
@@ -100,10 +103,12 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
private final ConcurrentHashMap<String, TimedReply> earlyOrLateReplies = new ConcurrentHashMap<>();
private final Lock earlyOrLateRepliesMonitor = new ReentrantLock();
private final Map<String, CompletableFuture<AbstractIntegrationMessageBuilder<?>>> futures =
new ConcurrentHashMap<>();
private final Object lifeCycleMonitor = new Object();
private final Lock lifeCycleMonitor = new ReentrantLock();
private Destination requestDestination;
@@ -512,7 +517,8 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
@Override
protected void doInit() {
synchronized (this.initializationMonitor) {
this.initializationMonitor.lock();
try {
if (this.initialized) {
return;
}
@@ -539,6 +545,9 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
initializeReplyContainer();
this.initialized = true;
}
finally {
this.initializationMonitor.unlock();
}
}
private void initializeReplyContainer() {
@@ -667,7 +676,8 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
@Override
public void start() {
synchronized (this.lifeCycleMonitor) {
this.lifeCycleMonitor.lock();
try {
if (!this.active) {
if (this.replyContainer != null) {
TaskScheduler taskScheduler = getTaskScheduler();
@@ -689,11 +699,15 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
this.active = true;
}
}
finally {
this.lifeCycleMonitor.unlock();
}
}
@Override
public void stop() {
synchronized (this.lifeCycleMonitor) {
this.lifeCycleMonitor.lock();
try {
if (this.replyContainer != null) {
this.replyContainer.shutdown();
this.wasStopped = true;
@@ -708,6 +722,10 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
}
this.active = false;
}
finally {
this.lifeCycleMonitor.unlock();
}
}
@Override
@@ -727,7 +745,8 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
}
else {
if (this.idleReplyContainerTimeout > 0) {
synchronized (this.lifeCycleMonitor) {
this.lifeCycleMonitor.lock();
try {
this.lastSend = System.currentTimeMillis();
if (!this.replyContainer.isRunning()) {
logger.debug(() -> getComponentName() + ": Starting reply container.");
@@ -738,6 +757,9 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
Duration.ofMillis(this.idleReplyContainerTimeout / 2));
}
}
finally {
this.lifeCycleMonitor.unlock();
}
}
reply = sendAndReceiveWithContainer(requestMessage);
}
@@ -1121,13 +1143,17 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
/*
* Check to see if the reply arrived before we obtained the correlationId
*/
synchronized (this.earlyOrLateReplies) {
this.earlyOrLateRepliesMonitor.lock();
try {
TimedReply timedReply = this.earlyOrLateReplies.remove(correlation);
if (timedReply != null) {
logger.debug(() -> "Found early reply with correlationId " + correlationToLog);
replyQueue.add(timedReply.getReply());
}
}
finally {
this.earlyOrLateRepliesMonitor.unlock();
}
return obtainReplyFromContainer(correlation, replyQueue);
}
@@ -1298,13 +1324,17 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
}
throw new IllegalStateException("No sender waiting for reply");
}
synchronized (this.earlyOrLateReplies) {
this.earlyOrLateRepliesMonitor.lock();
try {
queue = this.replies.get(correlationId);
if (queue == null) {
logger.debug(() -> "Reply for correlationId " + correlationId + " received early or late");
this.earlyOrLateReplies.put(correlationId, new TimedReply(message));
}
}
finally {
this.earlyOrLateRepliesMonitor.unlock();
}
}
if (queue != null) {
logger.debug(() -> "Received reply with correlationId " + correlationId);
@@ -1466,7 +1496,8 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
@Override
public void run() {
synchronized (JmsOutboundGateway.this.lifeCycleMonitor) {
JmsOutboundGateway.this.lifeCycleMonitor.lock();
try {
if (System.currentTimeMillis() - JmsOutboundGateway.this.lastSend >
JmsOutboundGateway.this.idleReplyContainerTimeout
&& JmsOutboundGateway.this.replies.size() == 0 &&
@@ -1478,6 +1509,9 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler
JmsOutboundGateway.this.idleTask = null;
}
}
finally {
JmsOutboundGateway.this.lifeCycleMonitor.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,8 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -95,6 +97,7 @@ import org.springframework.util.StringUtils;
* @author Mark Norkin
* @author Artem Bilan
* @author Anshul Mehra
* @author Christian Tzolov
*
* @since 5.4
*
@@ -111,11 +114,13 @@ public class KafkaMessageSource<K, V> extends AbstractMessageSource<Object> impl
*/
public static final String REMAINING_RECORDS = KafkaHeaders.PREFIX + "remainingRecords";
private final Lock lock = new ReentrantLock();
private final ConsumerFactory<K, V> consumerFactory;
private final KafkaAckCallbackFactory<K, V> ackCallbackFactory;
private final Object consumerMonitor = new Object();
private final Lock consumerMonitor = new ReentrantLock();
private final Map<TopicPartition, Set<KafkaAckInfo<K, V>>> inflightRecords = new ConcurrentHashMap<>();
@@ -385,31 +390,61 @@ public class KafkaMessageSource<K, V> extends AbstractMessageSource<Object> impl
}
@Override
public synchronized boolean isRunning() {
return this.running;
public boolean isRunning() {
this.lock.lock();
try {
return this.running;
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void start() {
this.running = true;
this.stopped = false;
public void start() {
this.lock.lock();
try {
this.running = true;
this.stopped = false;
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void stop() {
stopConsumer();
this.running = false;
this.stopped = true;
public void stop() {
this.lock.lock();
try {
stopConsumer();
this.running = false;
this.stopped = true;
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void pause() {
this.pausing = true;
public void pause() {
this.lock.lock();
try {
this.pausing = true;
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void resume() {
this.pausing = false;
public void resume() {
this.lock.lock();
try {
this.pausing = false;
}
finally {
this.lock.unlock();
}
}
@Override
@@ -418,35 +453,43 @@ public class KafkaMessageSource<K, V> extends AbstractMessageSource<Object> impl
}
@Override // NOSONAR - not so complex
protected synchronized Object doReceive() {
if (this.stopped) {
this.logger.debug("Message source is stopped; no records will be returned");
return null;
}
if (this.consumer == null) {
createConsumer();
this.running = true;
}
if (this.pausing && !this.paused && this.assignedPartitions.size() > 0) {
this.consumer.pause(this.assignedPartitions);
this.paused = true;
}
else if (this.paused && !this.pausing) {
this.consumer.resume(this.assignedPartitions);
this.paused = false;
}
if (this.paused && this.recordsIterator == null) {
this.logger.debug("Consumer is paused; no records will be returned");
}
ConsumerRecord<K, V> record = pollRecord();
protected Object doReceive() {
this.lock.lock();
try {
return record != null
? recordToMessage(record)
: null;
if (this.stopped) {
this.logger.debug("Message source is stopped; no records will be returned");
return null;
}
if (this.consumer == null) {
createConsumer();
this.running = true;
}
if (this.pausing && !this.paused && this.assignedPartitions.size() > 0) {
this.consumer.pause(this.assignedPartitions);
this.paused = true;
}
else if (this.paused && !this.pausing) {
this.consumer.resume(this.assignedPartitions);
this.paused = false;
}
if (this.paused && this.recordsIterator == null) {
this.logger.debug("Consumer is paused; no records will be returned");
}
ConsumerRecord<K, V> record = pollRecord();
return record != null
? recordToMessage(record)
: null;
}
finally {
this.lock.unlock();
}
}
protected void createConsumer() {
synchronized (this.consumerMonitor) {
this.consumerMonitor.lock();
try {
this.consumer = this.consumerFactory.createConsumer(this.consumerProperties.getGroupId(),
this.consumerProperties.getClientId(), null, this.consumerProperties.getKafkaConsumerProperties());
@@ -466,6 +509,9 @@ public class KafkaMessageSource<K, V> extends AbstractMessageSource<Object> impl
rebalanceCallback);
}
}
finally {
this.consumerMonitor.unlock();
}
}
private void assignAndSeekPartitions(TopicPartitionOffset[] partitions) {
@@ -522,7 +568,8 @@ public class KafkaMessageSource<K, V> extends AbstractMessageSource<Object> impl
return nextRecord();
}
else {
synchronized (this.consumerMonitor) {
this.consumerMonitor.lock();
try {
try {
ConsumerRecords<K, V> records = this.consumer
.poll(this.assignedPartitions.isEmpty() ? this.assignTimeout : this.pollTimeout);
@@ -545,6 +592,9 @@ public class KafkaMessageSource<K, V> extends AbstractMessageSource<Object> impl
return null;
}
}
finally {
this.consumerMonitor.unlock();
}
}
}
@@ -590,18 +640,28 @@ public class KafkaMessageSource<K, V> extends AbstractMessageSource<Object> impl
}
@Override
public synchronized void destroy() {
stopConsumer();
public void destroy() {
this.lock.lock();
try {
stopConsumer();
}
finally {
this.lock.unlock();
}
}
private void stopConsumer() {
synchronized (this.consumerMonitor) {
this.consumerMonitor.lock();
try {
if (this.consumer != null) {
this.consumer.close(this.closeTimeout);
this.consumer = null;
this.assignedPartitions.clear();
}
}
finally {
this.consumerMonitor.unlock();
}
}
private class IntegrationConsumerRebalanceListener implements ConsumerRebalanceListener {

View File

@@ -19,6 +19,8 @@ package org.springframework.integration.mqtt.core;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -38,6 +40,7 @@ import org.springframework.util.Assert;
*
* @author Artem Vozhdayenko
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 6.0
*/
@@ -47,6 +50,8 @@ public abstract class AbstractMqttClientManager<T, C> implements ClientManager<T
private static final int DEFAULT_MANAGER_PHASE = 0;
protected final Lock lock = new ReentrantLock();
private final Set<ConnectCallback> connectCallbacks = Collections.synchronizedSet(new HashSet<>());
private final String clientId;
@@ -92,8 +97,14 @@ public abstract class AbstractMqttClientManager<T, C> implements ClientManager<T
return this.applicationEventPublisher;
}
protected synchronized void setClient(T client) {
this.client = client;
protected void setClient(T client) {
this.lock.lock();
try {
this.client = client;
}
finally {
this.lock.unlock();
}
}
protected Set<ConnectCallback> getCallbacks() {
@@ -134,8 +145,14 @@ public abstract class AbstractMqttClientManager<T, C> implements ClientManager<T
}
@Override
public synchronized T getClient() {
return this.client;
public T getClient() {
this.lock.lock();
try {
return this.client;
}
finally {
this.lock.unlock();
}
}
@Override
@@ -177,8 +194,14 @@ public abstract class AbstractMqttClientManager<T, C> implements ClientManager<T
return this.connectCallbacks.remove(connectCallback);
}
public synchronized boolean isRunning() {
return this.client != null;
public boolean isRunning() {
this.lock.lock();
try {
return this.client != null;
}
finally {
this.lock.unlock();
}
}
/**

View File

@@ -36,6 +36,7 @@ import org.springframework.util.Assert;
*
* @author Artem Vozhdayenko
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 6.0
*/
@@ -87,40 +88,46 @@ public class Mqttv3ClientManager
}
@Override
public synchronized void start() {
var client = getClient();
if (client == null) {
try {
client = createClient();
}
catch (MqttException e) {
throw new IllegalStateException("could not start client manager", e);
}
}
setClient(client);
public void start() {
this.lock.lock();
try {
client.connect(this.connectionOptions).waitForCompletion(getCompletionTimeout());
}
catch (MqttException ex) {
// See GH-3822
if (this.connectionOptions.isAutomaticReconnect()) {
var client = getClient();
if (client == null) {
try {
client.reconnect();
client = createClient();
}
catch (MqttException re) {
logger.error("MQTT client failed to connect. Never happens.", re);
catch (MqttException e) {
throw new IllegalStateException("could not start client manager", e);
}
}
else {
var applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, ex));
setClient(client);
try {
client.connect(this.connectionOptions).waitForCompletion(getCompletionTimeout());
}
catch (MqttException ex) {
// See GH-3822
if (this.connectionOptions.isAutomaticReconnect()) {
try {
client.reconnect();
}
catch (MqttException re) {
logger.error("MQTT client failed to connect. Never happens.", re);
}
}
else {
logger.error("Could not start client manager, client_id=" + getClientId(), ex);
var applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, ex));
}
else {
logger.error("Could not start client manager, client_id=" + getClientId(), ex);
}
}
}
}
finally {
this.lock.unlock();
}
}
private IMqttAsyncClient createClient() throws MqttException {
@@ -133,31 +140,43 @@ public class Mqttv3ClientManager
}
@Override
public synchronized void stop() {
var client = getClient();
if (client == null) {
return;
}
public void stop() {
this.lock.lock();
try {
client.disconnectForcibly(getDisconnectCompletionTimeout());
}
catch (MqttException e) {
logger.error("Could not disconnect from the client", e);
}
finally {
var client = getClient();
if (client == null) {
return;
}
try {
client.close();
client.disconnectForcibly(getDisconnectCompletionTimeout());
}
catch (MqttException e) {
logger.error("Could not close the client", e);
logger.error("Could not disconnect from the client", e);
}
setClient(null);
finally {
try {
client.close();
}
catch (MqttException e) {
logger.error("Could not close the client", e);
}
setClient(null);
}
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void connectionLost(Throwable cause) {
logger.error("Connection lost, client_id=" + getClientId(), cause);
public void connectionLost(Throwable cause) {
this.lock.lock();
try {
logger.error("Connection lost, client_id=" + getClientId(), cause);
}
finally {
this.lock.unlock();
}
}
@Override

View File

@@ -38,6 +38,7 @@ import org.springframework.util.Assert;
*
* @author Artem Vozhdayenko
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 6.0
*/
@@ -89,39 +90,45 @@ public class Mqttv5ClientManager
}
@Override
public synchronized void start() {
var client = getClient();
if (client == null) {
try {
client = createClient();
}
catch (MqttException e) {
throw new IllegalStateException("Could not start client manager", e);
}
}
setClient(client);
public void start() {
this.lock.lock();
try {
client.connect(this.connectionOptions).waitForCompletion(getCompletionTimeout());
}
catch (MqttException ex) {
if (this.connectionOptions.isAutomaticReconnect()) {
var client = getClient();
if (client == null) {
try {
client.reconnect();
client = createClient();
}
catch (MqttException re) {
logger.error("MQTT client failed to connect. Never happens.", re);
catch (MqttException e) {
throw new IllegalStateException("Could not start client manager", e);
}
}
else {
var applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, ex));
setClient(client);
try {
client.connect(this.connectionOptions).waitForCompletion(getCompletionTimeout());
}
catch (MqttException ex) {
if (this.connectionOptions.isAutomaticReconnect()) {
try {
client.reconnect();
}
catch (MqttException re) {
logger.error("MQTT client failed to connect. Never happens.", re);
}
}
else {
logger.error("Could not start client manager, client_id=" + getClientId(), ex);
var applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, ex));
}
else {
logger.error("Could not start client manager, client_id=" + getClientId(), ex);
}
}
}
}
finally {
this.lock.unlock();
}
}
private MqttAsyncClient createClient() throws MqttException {
@@ -134,26 +141,32 @@ public class Mqttv5ClientManager
}
@Override
public synchronized void stop() {
var client = getClient();
if (client == null) {
return;
}
public void stop() {
this.lock.lock();
try {
client.disconnectForcibly(getDisconnectCompletionTimeout());
}
catch (MqttException e) {
logger.error("Could not disconnect from the client", e);
}
finally {
var client = getClient();
if (client == null) {
return;
}
try {
client.close();
client.disconnectForcibly(getDisconnectCompletionTimeout());
}
catch (MqttException e) {
logger.error("Could not close the client", e);
logger.error("Could not disconnect from the client", e);
}
setClient(null);
finally {
try {
client.close();
}
catch (MqttException e) {
logger.error("Could not close the client", e);
}
setClient(null);
}
}
finally {
this.lock.unlock();
}
}

View File

@@ -17,6 +17,8 @@
package org.springframework.integration.mqtt.inbound;
import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
@@ -64,6 +66,8 @@ public class MqttPahoMessageDrivenChannelAdapter
extends AbstractMqttMessageDrivenChannelAdapter<IMqttAsyncClient, MqttConnectOptions>
implements MqttCallbackExtended, MqttPahoComponent {
private final Lock lock = new ReentrantLock();
private final MqttPahoClientFactory clientFactory;
private volatile IMqttAsyncClient client;
@@ -179,46 +183,58 @@ public class MqttPahoMessageDrivenChannelAdapter
}
@SuppressWarnings("deprecation")
private synchronized void connect() throws MqttException {
MqttConnectOptions connectionOptions = this.clientFactory.getConnectionOptions();
var clientManager = getClientManager();
if (clientManager == null) {
Assert.state(getUrl() != null || connectionOptions.getServerURIs() != null,
"If no 'url' provided, connectionOptions.getServerURIs() must not be null");
this.client = this.clientFactory.getAsyncClientInstance(getUrl(), getClientId());
this.client.setCallback(this);
this.client.connect(connectionOptions).waitForCompletion(getCompletionTimeout());
this.client.setManualAcks(isManualAcks());
private void connect() throws MqttException {
this.lock.lock();
try {
MqttConnectOptions connectionOptions = this.clientFactory.getConnectionOptions();
var clientManager = getClientManager();
if (clientManager == null) {
Assert.state(getUrl() != null || connectionOptions.getServerURIs() != null,
"If no 'url' provided, connectionOptions.getServerURIs() must not be null");
this.client = this.clientFactory.getAsyncClientInstance(getUrl(), getClientId());
this.client.setCallback(this);
this.client.connect(connectionOptions).waitForCompletion(getCompletionTimeout());
this.client.setManualAcks(isManualAcks());
}
else {
this.client = clientManager.getClient();
}
}
else {
this.client = clientManager.getClient();
finally {
this.lock.unlock();
}
}
@Override
protected synchronized void doStop() {
this.readyToSubscribeOnStart = false;
protected void doStop() {
this.lock.lock();
try {
if (this.clientFactory.getConnectionOptions().isCleanSession()) {
this.client.unsubscribe(getTopic());
// Have to re-subscribe on next start if connection is not lost.
this.readyToSubscribeOnStart = true;
this.readyToSubscribeOnStart = false;
try {
if (this.clientFactory.getConnectionOptions().isCleanSession()) {
this.client.unsubscribe(getTopic());
// Have to re-subscribe on next start if connection is not lost.
this.readyToSubscribeOnStart = true;
}
}
catch (MqttException ex1) {
logger.error(ex1, "Exception while unsubscribing");
}
if (getClientManager() != null) {
return;
}
try {
this.client.disconnectForcibly(getDisconnectCompletionTimeout());
}
catch (MqttException ex) {
logger.error(ex, "Exception while disconnecting");
}
}
catch (MqttException ex1) {
logger.error(ex1, "Exception while unsubscribing");
}
if (getClientManager() != null) {
return;
}
try {
this.client.disconnectForcibly(getDisconnectCompletionTimeout());
}
catch (MqttException ex) {
logger.error(ex, "Exception while disconnecting");
finally {
this.lock.unlock();
}
}
@@ -322,17 +338,23 @@ public class MqttPahoMessageDrivenChannelAdapter
}
@Override
public synchronized void connectionLost(Throwable cause) {
if (isRunning()) {
this.logger.error(() -> "Lost connection: " + cause.getMessage());
ApplicationEventPublisher applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, cause));
public void connectionLost(Throwable cause) {
this.lock.lock();
try {
if (isRunning()) {
this.logger.error(() -> "Lost connection: " + cause.getMessage());
ApplicationEventPublisher applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, cause));
}
}
else {
// The 'connectComplete()' re-subscribes or sets this flag otherwise.
this.readyToSubscribeOnStart = false;
}
}
else {
// The 'connectComplete()' re-subscribes or sets this flag otherwise.
this.readyToSubscribeOnStart = false;
finally {
this.lock.unlock();
}
}

View File

@@ -18,6 +18,8 @@ package org.springframework.integration.mqtt.inbound;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;
import org.eclipse.paho.mqttv5.client.IMqttAsyncClient;
@@ -81,6 +83,8 @@ public class Mqttv5PahoMessageDrivenChannelAdapter
extends AbstractMqttMessageDrivenChannelAdapter<IMqttAsyncClient, MqttConnectionOptions>
implements MqttCallback, MqttComponent<MqttConnectionOptions> {
private final Lock lock = new ReentrantLock();
private final MqttConnectionOptions connectionOptions;
private IMqttAsyncClient mqttClient;
@@ -211,13 +215,19 @@ public class Mqttv5PahoMessageDrivenChannelAdapter
}
}
private synchronized void connect() throws MqttException {
var clientManager = getClientManager();
if (clientManager == null) {
this.mqttClient.connect(this.connectionOptions).waitForCompletion(getCompletionTimeout());
private void connect() throws MqttException {
this.lock.lock();
try {
var clientManager = getClientManager();
if (clientManager == null) {
this.mqttClient.connect(this.connectionOptions).waitForCompletion(getCompletionTimeout());
}
else {
this.mqttClient = clientManager.getClient();
}
}
else {
this.mqttClient = clientManager.getClient();
finally {
this.lock.unlock();
}
}

View File

@@ -17,6 +17,8 @@
package org.springframework.integration.mqtt.outbound;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.ApplicationEventPublisher;
@@ -63,6 +65,8 @@ public abstract class AbstractMqttMessageHandler<T, C> extends AbstractMessageHa
private static final MessageProcessor<String> DEFAULT_TOPIC_PROCESSOR =
(message) -> message.getHeaders().get(MqttHeaders.TOPIC, String.class);
protected final Lock lock = new ReentrantLock();
private final AtomicBoolean running = new AtomicBoolean();
private final String url;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ import org.springframework.util.Assert;
* @author Gary Russell
* @author Artem Bilan
* @author Artem Vozhdayenko
* @author Christian Tzolov
*
* @since 4.0
*
@@ -184,41 +185,47 @@ public class MqttPahoMessageHandler extends AbstractMqttMessageHandler<IMqttAsyn
}
}
private synchronized IMqttAsyncClient checkConnection() throws MqttException {
var theClientManager = getClientManager();
if (theClientManager != null) {
return theClientManager.getClient();
}
private IMqttAsyncClient checkConnection() throws MqttException {
this.lock.lock();
try {
var theClientManager = getClientManager();
if (theClientManager != null) {
return theClientManager.getClient();
}
if (this.client != null && !this.client.isConnected()) {
this.client.setCallback(null);
this.client.close();
this.client = null;
}
if (this.client == null) {
try {
MqttConnectOptions connectionOptions = this.clientFactory.getConnectionOptions();
Assert.state(this.getUrl() != null || connectionOptions.getServerURIs() != null,
"If no 'url' provided, connectionOptions.getServerURIs() must not be null");
this.client = this.clientFactory.getAsyncClientInstance(this.getUrl(), this.getClientId());
incrementClientInstance();
this.client.setCallback(this);
this.client.connect(connectionOptions).waitForCompletion(getCompletionTimeout());
logger.debug("Client connected");
if (this.client != null && !this.client.isConnected()) {
this.client.setCallback(null);
this.client.close();
this.client = null;
}
catch (MqttException e) {
if (this.client != null) {
this.client.close();
this.client = null;
if (this.client == null) {
try {
MqttConnectOptions connectionOptions = this.clientFactory.getConnectionOptions();
Assert.state(this.getUrl() != null || connectionOptions.getServerURIs() != null,
"If no 'url' provided, connectionOptions.getServerURIs() must not be null");
this.client = this.clientFactory.getAsyncClientInstance(this.getUrl(), this.getClientId());
incrementClientInstance();
this.client.setCallback(this);
this.client.connect(connectionOptions).waitForCompletion(getCompletionTimeout());
logger.debug("Client connected");
}
ApplicationEventPublisher applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, e));
catch (MqttException e) {
if (this.client != null) {
this.client.close();
this.client = null;
}
ApplicationEventPublisher applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, e));
}
throw new MessagingException("Failed to connect", e);
}
throw new MessagingException("Failed to connect", e);
}
return this.client;
}
finally {
this.lock.unlock();
}
return this.client;
}
@Override
@@ -252,22 +259,28 @@ public class MqttPahoMessageHandler extends AbstractMqttMessageHandler<IMqttAsyn
}
@Override
public synchronized void connectionLost(Throwable cause) {
logger.error("Lost connection; will attempt reconnect on next request");
if (this.client != null) {
try {
this.client.setCallback(null);
this.client.close();
}
catch (MqttException e) {
// NOSONAR
}
this.client = null;
ApplicationEventPublisher applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, cause));
public void connectionLost(Throwable cause) {
this.lock.lock();
try {
logger.error("Lost connection; will attempt reconnect on next request");
if (this.client != null) {
try {
this.client.setCallback(null);
this.client.close();
}
catch (MqttException e) {
// NOSONAR
}
this.client = null;
ApplicationEventPublisher applicationEventPublisher = getApplicationEventPublisher();
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new MqttConnectionFailedEvent(this, cause));
}
}
}
finally {
this.lock.unlock();
}
}
@Override

View File

@@ -82,6 +82,7 @@ import org.springframework.util.ReflectionUtils;
* @author Vedran Pavic
* @author Unseok Kim
* @author Anton Gabov
* @author Christian Tzolov
*
* @since 4.0
*
@@ -94,6 +95,8 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
private static final int DEFAULT_CAPACITY = 100_000;
private final Lock lock = new ReentrantLock();
private final Map<String, RedisLock> locks =
new LinkedHashMap<String, RedisLock>(16, 0.75F, true) {
@@ -224,15 +227,20 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
public Lock obtain(Object lockKey) {
Assert.isInstanceOf(String.class, lockKey);
String path = (String) lockKey;
synchronized (this.locks) {
this.lock.lock();
try {
return this.locks.computeIfAbsent(path, getRedisLockConstructor(this.redisLockType));
}
finally {
this.lock.unlock();
}
}
@Override
public void expireUnusedOlderThan(long age) {
long now = System.currentTimeMillis();
synchronized (this.locks) {
this.lock.lock();
try {
this.locks.entrySet()
.removeIf(entry -> {
RedisLock lock = entry.getValue();
@@ -243,6 +251,9 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
&& !lock.isAcquiredInThisProcess();
});
}
finally {
this.lock.unlock();
}
}
@Override
@@ -631,7 +642,8 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
}
private void runRedisMessageListenerContainer() {
synchronized (RedisLockRegistry.this.locks) {
RedisLockRegistry.this.lock.tryLock();
try {
if (!(RedisLockRegistry.this.isRunningRedisMessageListenerContainer
&& RedisLockRegistry.this.redisMessageListenerContainer != null
&& RedisLockRegistry.this.redisMessageListenerContainer.isRunning())) {
@@ -645,6 +657,9 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
RedisLockRegistry.this.isRunningRedisMessageListenerContainer = true;
}
}
finally {
RedisLockRegistry.this.lock.unlock();
}
}
private static final class RedisUnLockNotifyMessageListener implements MessageListener {

View File

@@ -62,11 +62,14 @@ import org.springframework.util.Assert;
* @author Artem Bilan
* @author Krzysztof Debski
* @author Auke Zaaiman
* @author Christian Tzolov
*
* @since 2.0
*/
public class DefaultSftpSessionFactory implements SessionFactory<SftpClient.DirEntry>, SharedSessionCapable {
private final Lock lock = new ReentrantLock();
private final SshClient sshClient;
private volatile boolean initialized;
@@ -323,12 +326,16 @@ public class DefaultSftpSessionFactory implements SessionFactory<SftpClient.DirE
private void initClient() throws IOException {
if (!this.initialized) {
synchronized (this) {
this.lock.lock();
try {
if (!this.initialized) {
doInitClient();
this.initialized = true;
}
}
finally {
this.lock.unlock();
}
}
}

View File

@@ -22,6 +22,8 @@ import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.SocketAddress;
import java.time.Duration;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -46,11 +48,14 @@ import org.springframework.util.StringUtils;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 2.0
*/
public class SftpSession implements Session<SftpClient.DirEntry> {
private final Lock lock = new ReentrantLock();
private final SftpClient sftpClient;
public SftpSession(SftpClient sftpClient) {
@@ -122,15 +127,20 @@ public class SftpSession implements Session<SftpClient.DirEntry> {
@Override
public void write(InputStream inputStream, String destination) throws IOException {
synchronized (this.sftpClient) {
this.lock.lock();
try {
OutputStream outputStream = this.sftpClient.write(destination);
FileCopyUtils.copy(inputStream, outputStream);
}
finally {
this.lock.unlock();
}
}
@Override
public void append(InputStream inputStream, String destination) throws IOException {
synchronized (this.sftpClient) {
this.lock.lock();
try {
OutputStream outputStream =
this.sftpClient.write(destination,
SftpClient.OpenMode.Create,
@@ -138,6 +148,9 @@ public class SftpSession implements Session<SftpClient.DirEntry> {
SftpClient.OpenMode.Append);
FileCopyUtils.copy(inputStream, outputStream);
}
finally {
this.lock.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.integration.smb.session;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import jcifs.CIFSContext;
import jcifs.CIFSException;
@@ -41,6 +43,7 @@ import org.springframework.util.StringUtils;
* @author Gregory Bragg
* @author Adam Jones
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 6.0
*/
@@ -48,6 +51,8 @@ public class SmbShare extends SmbFile {
private static final Log logger = LogFactory.getLog(SmbShare.class);
private final Lock lock = new ReentrantLock();
private final AtomicBoolean open = new AtomicBoolean(false);
private final AtomicBoolean closeContext = new AtomicBoolean(false);
@@ -126,17 +131,23 @@ public class SmbShare extends SmbFile {
}
@Override
public synchronized void close() {
this.open.set(false);
if (this.closeContext.get()) {
try {
getContext().close();
}
catch (CIFSException e) {
logger.error("Unable to close share: " + this);
public void close() {
this.lock.lock();
try {
this.open.set(false);
if (this.closeContext.get()) {
try {
getContext().close();
}
catch (CIFSException e) {
logger.error("Unable to close share: " + this);
}
}
super.close();
}
finally {
this.lock.unlock();
}
super.close();
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;
import org.apache.commons.logging.Log;
@@ -66,6 +68,7 @@ import org.springframework.util.concurrent.ListenableFuture;
*
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 4.2
*/
@@ -80,7 +83,9 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
private final CompositeStompSessionHandler compositeStompSessionHandler = new CompositeStompSessionHandler();
private final Object lifecycleMonitor = new Object();
private final Lock lifecycleMonitor = new ReentrantLock();
private final Lock lock = new ReentrantLock();
private final AtomicInteger epoch = new AtomicInteger();
@@ -177,41 +182,47 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
return this.phase;
}
private synchronized void connect() {
if (this.connecting || this.connected) {
this.logger.debug("Aborting connect; another thread is connecting.");
return;
}
final int currentEpoch = this.epoch.get();
this.connecting = true;
if (this.logger.isDebugEnabled()) {
this.logger.debug("Connecting " + this);
}
private void connect() {
this.lock.lock();
try {
this.stompSessionFuture = doConnect(this.compositeStompSessionHandler);
}
catch (Exception e) {
if (currentEpoch == this.epoch.get()) {
scheduleReconnect(e);
if (this.connecting || this.connected) {
this.logger.debug("Aborting connect; another thread is connecting.");
return;
}
else {
this.logger.error("STOMP doConnect() error for " + this, e);
final int currentEpoch = this.epoch.get();
this.connecting = true;
if (this.logger.isDebugEnabled()) {
this.logger.debug("Connecting " + this);
}
return;
}
CountDownLatch connectLatch = addStompSessionCallback(currentEpoch);
try {
if (!connectLatch.await(30, TimeUnit.SECONDS)) { // NOSONAR magic number
this.logger.error("No response to connection attempt");
try {
this.stompSessionFuture = doConnect(this.compositeStompSessionHandler);
}
catch (Exception e) {
if (currentEpoch == this.epoch.get()) {
scheduleReconnect(null);
scheduleReconnect(e);
}
else {
this.logger.error("STOMP doConnect() error for " + this, e);
}
return;
}
CountDownLatch connectLatch = addStompSessionCallback(currentEpoch);
try {
if (!connectLatch.await(30, TimeUnit.SECONDS)) { // NOSONAR magic number
this.logger.error("No response to connection attempt");
if (currentEpoch == this.epoch.get()) {
scheduleReconnect(null);
}
}
}
catch (InterruptedException e1) {
this.logger.error("Interrupted while waiting for connection attempt");
Thread.currentThread().interrupt();
}
}
catch (InterruptedException e1) {
this.logger.error("Interrupted while waiting for connection attempt");
Thread.currentThread().interrupt();
finally {
this.lock.unlock();
}
}
@@ -294,7 +305,8 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (!isRunning()) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Starting " + this);
@@ -303,11 +315,15 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
this.running = true;
}
}
finally {
this.lifecycleMonitor.unlock();
}
}
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (isRunning()) {
this.running = false;
if (this.logger.isInfoEnabled()) {
@@ -316,6 +332,9 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
destroy();
}
}
finally {
this.lifecycleMonitor.unlock();
}
}
@Override
@@ -355,18 +374,24 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
private final List<StompSessionHandler> delegates = Collections.synchronizedList(new ArrayList<>());
private final Lock delegatesMonitor = new ReentrantLock();
private volatile StompSession session;
CompositeStompSessionHandler() {
}
void addHandler(StompSessionHandler delegate) {
synchronized (this.delegates) {
this.delegatesMonitor.lock();
try {
if (this.session != null) {
delegate.afterConnected(this.session, getConnectHeaders());
}
this.delegates.add(delegate);
}
finally {
this.delegatesMonitor.unlock();
}
}
void removeHandler(StompSessionHandler delegate) {
@@ -375,23 +400,31 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
@Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
synchronized (this.delegates) {
this.delegatesMonitor.lock();
try {
this.session = session;
for (StompSessionHandler delegate : this.delegates) {
delegate.afterConnected(session, connectedHeaders);
}
}
finally {
this.delegatesMonitor.unlock();
}
}
@Override
public void handleException(StompSession session, @Nullable StompCommand command, StompHeaders headers,
byte[] payload, Throwable exception) {
synchronized (this.delegates) {
this.delegatesMonitor.lock();
try {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleException(session, command, headers, payload, exception);
}
}
finally {
this.delegatesMonitor.unlock();
}
}
@Override
@@ -400,20 +433,28 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
exception);
this.session = null;
scheduleReconnect(exception);
synchronized (this.delegates) {
this.delegatesMonitor.lock();
try {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleTransportError(session, exception);
}
}
finally {
this.delegatesMonitor.unlock();
}
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
synchronized (this.delegates) {
this.delegatesMonitor.lock();
try {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleFrame(headers, payload);
}
}
finally {
this.delegatesMonitor.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2020 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.integration.stomp.outbound;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
@@ -51,6 +53,7 @@ import org.springframework.util.Assert;
*
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 4.2
*/
@@ -59,6 +62,8 @@ public class StompMessageHandler extends AbstractMessageHandler
private static final int DEFAULT_CONNECT_TIMEOUT = 3000;
private final Lock lock = new ReentrantLock();
private final StompSessionHandler sessionHandler = new IntegrationOutboundStompSessionHandler();
private final StompSessionManager stompSessionManager;
@@ -178,7 +183,8 @@ public class StompMessageHandler extends AbstractMessageHandler
}
private void connectIfNecessary() throws InterruptedException {
synchronized (this.connectSemaphore) {
this.lock.lock();
try {
if (this.stompSession == null || !this.stompSessionManager.isConnected()) {
this.stompSessionManager.disconnect(this.sessionHandler);
this.stompSessionManager.connect(this.sessionHandler);
@@ -199,6 +205,9 @@ public class StompMessageHandler extends AbstractMessageHandler
}
}
}
finally {
this.lock.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -247,7 +247,8 @@ public class StompInboundChannelAdapterWebSocketIntegrationTests {
}
@Bean
public WebSocketStompClient stompClient(TaskScheduler taskScheduler) {
public WebSocketStompClient stompClient(
@Qualifier("taskScheduler") TaskScheduler taskScheduler) {
WebSocketStompClient webSocketStompClient = new WebSocketStompClient(webSocketClient());
webSocketStompClient.setMessageConverter(new MappingJackson2MessageConverter());
webSocketStompClient.setTaskScheduler(taskScheduler);
@@ -347,6 +348,7 @@ public class StompInboundChannelAdapterWebSocketIntegrationTests {
//SimpleBrokerMessageHandler doesn't support RECEIPT frame, hence we emulate it this way
@Bean
public ApplicationListener<SessionSubscribeEvent> webSocketEventListener(
@Qualifier("clientOutboundChannel")
final AbstractSubscribableChannel clientOutboundChannel) {
return event -> {
Message<byte[]> message = event.getMessage();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.integration.stream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.integration.endpoint.AbstractMessageSource;
import org.springframework.messaging.MessagingException;
@@ -28,9 +30,12 @@ import org.springframework.messaging.MessagingException;
*
* @author Mark Fisher
* @author Artem Bilan
* @author Christian Tzolov
*/
public class ByteStreamReadingMessageSource extends AbstractMessageSource<byte[]> {
private final Lock lock = new ReentrantLock();
private final BufferedInputStream stream;
private int bytesPerMessage = 1024; // NOSONAR magic number
@@ -73,13 +78,17 @@ public class ByteStreamReadingMessageSource extends AbstractMessageSource<byte[]
try {
byte[] bytes;
int bytesRead = 0;
synchronized (this.stream) {
this.lock.lock();
try {
if (this.stream.available() == 0) {
return null;
}
bytes = new byte[this.bytesPerMessage];
bytesRead = this.stream.read(bytes, 0, bytes.length);
}
finally {
this.lock.unlock();
}
if (bytesRead <= 0) {
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
@@ -34,10 +36,13 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public class CharacterStreamReadingMessageSource extends AbstractMessageSource<String>
implements ApplicationEventPublisherAware {
private final Lock lock = new ReentrantLock();
private final BufferedReader reader;
private final boolean blockToDetectEOF;
@@ -112,7 +117,8 @@ public class CharacterStreamReadingMessageSource extends AbstractMessageSource<S
@Override
public String doReceive() {
try {
synchronized (this.reader) {
this.lock.lock();
try {
if (!this.blockToDetectEOF && !this.reader.ready()) {
return null;
}
@@ -122,6 +128,9 @@ public class CharacterStreamReadingMessageSource extends AbstractMessageSource<S
}
return line;
}
finally {
this.lock.unlock();
}
}
catch (IOException e) {
throw new MessagingException("IO failure occurred in adapter", e);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 the original author or authors.
* Copyright 2017-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.integration.test.mock;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -51,11 +53,14 @@ import org.springframework.messaging.Message;
* </pre>
*
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 5.0
*/
public class MockMessageHandler extends AbstractMessageProducingHandler {
private final Lock lock = new ReentrantLock();
protected final List<Function<Message<?>, ?>> messageFunctions = new LinkedList<>(); // NOSONAR final
private final CapturingMatcher<Message<?>> capturingMatcher;
@@ -110,13 +115,17 @@ public class MockMessageHandler extends AbstractMessageProducingHandler {
Function<Message<?>, ?> function = this.lastFunction;
synchronized (this) {
this.lock.lock();
try {
Iterator<Function<Message<?>, ?>> iterator = this.messageFunctions.iterator();
if (iterator.hasNext()) {
function = iterator.next();
iterator.remove();
}
}
finally {
this.lock.unlock();
}
Object result = function.apply(message);

View File

@@ -46,6 +46,7 @@ import org.springframework.web.socket.client.WebSocketClient;
*
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 4.1
*/
@@ -183,12 +184,18 @@ public final class ClientWebSocketContainer extends IntegrationWebSocketContaine
}
@Override
public synchronized void start() {
if (!isRunning()) {
this.clientSession = null;
this.openConnectionException = null;
this.connectionLatch = new CountDownLatch(1);
this.connectionManager.start();
public void start() {
this.lock.lock();
try {
if (!isRunning()) {
this.clientSession = null;
this.openConnectionException = null;
this.connectionLatch = new CountDownLatch(1);
this.connectionManager.start();
}
}
finally {
this.lock.unlock();
}
}

View File

@@ -21,6 +21,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -67,6 +69,8 @@ public abstract class IntegrationWebSocketContainer implements DisposableBean {
protected final Log logger = LogFactory.getLog(getClass()); // NOSONAR
protected final Lock lock = new ReentrantLock();
private WebSocketHandler webSocketHandler = new IntegrationWebSocketHandler();
protected final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>(); // NOSONAR

View File

@@ -47,6 +47,7 @@ import org.springframework.web.socket.sockjs.transport.TransportHandler;
*
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*
* @since 4.1
*/
@@ -224,9 +225,15 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer
}
@Override
public synchronized void start() {
if (this.handshakeHandler instanceof Lifecycle && !isRunning()) {
((Lifecycle) this.handshakeHandler).start();
public void start() {
this.lock.lock();
try {
if (this.handshakeHandler instanceof Lifecycle && !isRunning()) {
((Lifecycle) this.handshakeHandler).start();
}
}
finally {
this.lock.unlock();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@ import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.transform.TransformerException;
@@ -55,9 +57,12 @@ import org.springframework.xml.transform.TransformerObjectSupport;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*/
public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyProducingMessageHandler {
private final Lock lock = new ReentrantLock();
protected final DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); // NOSONAR - final
private final String uri;
@@ -108,10 +113,14 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
* @param uriVariableExpressions The URI variable expressions.
*/
public void setUriVariableExpressions(Map<String, Expression> uriVariableExpressions) {
synchronized (this.uriVariableExpressions) {
this.lock.lock();
try {
this.uriVariableExpressions.clear();
this.uriVariableExpressions.putAll(uriVariableExpressions);
}
finally {
this.lock.unlock();
}
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.StringReader;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -43,11 +45,13 @@ import org.springframework.xml.DocumentBuilderFactoryUtils;
*
* @author Jonas Partner
* @author Artem Bilan
* @author Christian Tzolov
*/
public class DefaultXmlPayloadConverter implements XmlPayloadConverter {
private final DocumentBuilderFactory documentBuilderFactory;
private final Lock lock = new ReentrantLock();
public DefaultXmlPayloadConverter() {
this(DocumentBuilderFactoryUtils.newInstance());
@@ -142,13 +146,17 @@ public class DefaultXmlPayloadConverter implements XmlPayloadConverter {
}
}
protected synchronized DocumentBuilder getDocumentBuilder() {
protected DocumentBuilder getDocumentBuilder() {
this.lock.lock();
try {
return this.documentBuilderFactory.newDocumentBuilder();
}
catch (ParserConfigurationException e) {
throw new MessagingException("failed to create a new DocumentBuilder", e);
}
finally {
this.lock.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.integration.xml.result;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -30,11 +33,16 @@ import org.springframework.xml.DocumentBuilderFactoryUtils;
* @author Jonas Partner
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*/
public class DomResultFactory implements ResultFactory {
private final DocumentBuilderFactory documentBuilderFactory;
private final Lock documentBuilderFactoryMonitor = new ReentrantLock();
private final Lock lock = new ReentrantLock();
public DomResultFactory() {
this(DocumentBuilderFactoryUtils.newInstance());
@@ -48,7 +56,8 @@ public class DomResultFactory implements ResultFactory {
@Override
public synchronized Result createResult(Object payload) {
public Result createResult(Object payload) {
this.lock.lock();
try {
return new DOMResult(getNewDocumentBuilder().newDocument());
}
@@ -56,12 +65,19 @@ public class DomResultFactory implements ResultFactory {
throw new MessagingException("failed to create Result for payload type [" +
payload.getClass().getName() + "]", e);
}
finally {
this.lock.unlock();
}
}
protected DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException {
synchronized (this.documentBuilderFactory) {
this.documentBuilderFactoryMonitor.lock();
try {
return this.documentBuilderFactory.newDocumentBuilder();
}
finally {
this.documentBuilderFactoryMonitor.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.integration.xml.source;
import java.io.File;
import java.io.StringReader;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -39,9 +41,12 @@ import org.springframework.xml.DocumentBuilderFactoryUtils;
* @author Jonas Partner
* @author Mark Fisher
* @author Artem Bilan
* @author Christian Tzolov
*/
public class DomSourceFactory implements SourceFactory {
private final Lock lock = new ReentrantLock();
private final DocumentBuilderFactory documentBuilderFactory;
@@ -99,9 +104,13 @@ public class DomSourceFactory implements SourceFactory {
}
private DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException {
synchronized (this.documentBuilderFactory) {
this.lock.lock();
try {
return this.documentBuilderFactory.newDocumentBuilder();
}
finally {
this.lock.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.integration.xml.source;
import java.io.File;
import java.io.FileReader;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
@@ -40,9 +42,12 @@ import org.springframework.xml.transform.TransformerFactoryUtils;
* @author Jonas Partner
* @author Mark Fisher
* @author Artem Bilan
* @author Christian Tzolov
*/
public class StringSourceFactory implements SourceFactory {
private final Lock lock = new ReentrantLock();
private final TransformerFactory transformerFactory;
@@ -96,13 +101,17 @@ public class StringSourceFactory implements SourceFactory {
}
}
private synchronized Transformer getTransformer() {
private Transformer getTransformer() {
this.lock.lock();
try {
return this.transformerFactory.newTransformer();
}
catch (Exception e) {
throw new MessagingException("Exception creating transformer", e);
}
finally {
this.lock.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import javax.xml.parsers.DocumentBuilder;
@@ -67,12 +69,15 @@ import org.springframework.xml.xpath.XPathExpressionFactory;
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
* @author Christian Tzolov
*/
public class XPathMessageSplitter extends AbstractMessageSplitter {
private final TransformerFactory transformerFactory;
private final Object documentBuilderFactoryMonitor = new Object();
private final Lock documentBuilderFactoryMonitor = new ReentrantLock();
private final Lock transformerFactoryMonitor = new ReentrantLock();
private final XPathExpression xpathExpression;
@@ -249,9 +254,13 @@ public class XPathMessageSplitter extends AbstractMessageSplitter {
private Object splitDocument(Document document) throws ParserConfigurationException, TransformerException {
Object nodes = splitNode(document);
final Transformer transformer;
synchronized (this.transformerFactory) {
this.transformerFactoryMonitor.lock();
try {
transformer = this.transformerFactory.newTransformer();
}
finally {
this.transformerFactoryMonitor.unlock();
}
if (this.outputProperties != null) {
transformer.setOutputProperties(this.outputProperties);
}
@@ -317,9 +326,13 @@ public class XPathMessageSplitter extends AbstractMessageSplitter {
}
private DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException {
synchronized (this.documentBuilderFactoryMonitor) {
this.documentBuilderFactoryMonitor.lock();
try {
return this.documentBuilderFactory.newDocumentBuilder();
}
finally {
this.documentBuilderFactoryMonitor.unlock();
}
}
private final class NodeListIterator implements Iterator<Node> {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.xml.transformer;
import java.io.StringReader;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -38,9 +40,12 @@ import org.springframework.xml.transform.StringResult;
*
* @author Jonas Partner
* @author Artem Bilan
* @author Christian Tzolov
*/
public class ResultToDocumentTransformer implements ResultTransformer {
private final Lock lock = new ReentrantLock();
// Not guaranteed to be thread safe
private final DocumentBuilderFactory documentBuilderFactory;
@@ -84,13 +89,17 @@ public class ResultToDocumentTransformer implements ResultTransformer {
}
}
private synchronized DocumentBuilder getDocumentBuilder() {
private DocumentBuilder getDocumentBuilder() {
this.lock.lock();
try {
return this.documentBuilderFactory.newDocumentBuilder();
}
catch (ParserConfigurationException e) {
throw new MessagingException("failed to create a new DocumentBuilder", e);
}
finally {
this.lock.unlock();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.xml.transformer;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
@@ -38,9 +40,12 @@ import org.springframework.xml.transform.TransformerFactoryUtils;
* @author Jonas Partner
* @author Mark Fisher
* @author Artem Bilan
* @author Christian Tzolov
*/
public class ResultToStringTransformer implements ResultTransformer {
private final Lock lock = new ReentrantLock();
private final TransformerFactory transformerFactory;
private Properties outputProperties;
@@ -90,9 +95,13 @@ public class ResultToStringTransformer implements ResultTransformer {
private Transformer getNewTransformer() throws TransformerConfigurationException {
Transformer transformer;
synchronized (this.transformerFactory) {
this.lock.lock();
try {
transformer = this.transformerFactory.newTransformer();
}
finally {
this.lock.unlock();
}
if (this.outputProperties != null) {
transformer.setOutputProperties(this.outputProperties);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.integration.xmpp.config;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.roster.Roster;
@@ -40,6 +43,7 @@ import org.springframework.util.StringUtils;
* @author Artem Bilan
* @author Philipp Etschel
* @author Gary Russell
* @author Christian Tzolov
*
* @since 2.0
*
@@ -47,7 +51,7 @@ import org.springframework.util.StringUtils;
*/
public class XmppConnectionFactoryBean extends AbstractFactoryBean<XMPPConnection> implements SmartLifecycle {
private final Object lifecycleMonitor = new Object();
private final Lock lifecycleMonitor = new ReentrantLock();
private XMPPTCPConnectionConfiguration connectionConfiguration;
@@ -172,7 +176,8 @@ public class XmppConnectionFactoryBean extends AbstractFactoryBean<XMPPConnectio
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (this.running) {
return;
}
@@ -195,16 +200,23 @@ public class XmppConnectionFactoryBean extends AbstractFactoryBean<XMPPConnectio
+ connection.getXMPPServiceDomain(), e);
}
}
finally {
this.lifecycleMonitor.unlock();
}
}
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (this.isRunning()) {
getConnection().disconnect();
this.running = false;
}
}
finally {
this.lifecycleMonitor.unlock();
}
}
@Override

View File

@@ -20,6 +20,8 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import org.apache.commons.logging.Log;
@@ -58,6 +60,7 @@ import org.springframework.util.Assert;
* The address for this socket is {@code "inproc://" + beanName + ".capture"}.
*
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 5.4
*
@@ -67,6 +70,8 @@ public class ZeroMqProxy implements InitializingBean, SmartLifecycle, BeanNameAw
private static final Log LOG = LogFactory.getLog(ZeroMqProxy.class);
private final Lock lock = new ReentrantLock();
private final ZContext context;
private final Type type;
@@ -247,65 +252,78 @@ public class ZeroMqProxy implements InitializingBean, SmartLifecycle, BeanNameAw
}
@Override
public synchronized void start() {
if (!this.running.get()) {
this.proxyExecutor
.execute(() -> {
ZMQ.Socket captureSocket = null;
if (this.exposeCaptureSocket) {
captureSocket = this.context.createSocket(SocketType.PUB);
}
try (
ZMQ.Socket frontendSocket = this.context.createSocket(this.type.getFrontendSocketType());
ZMQ.Socket backendSocket = this.context.createSocket(this.type.getBackendSocketType());
ZMQ.Socket controlSocket = this.context.createSocket(SocketType.PAIR)
) {
if (this.frontendSocketConfigurer != null) {
this.frontendSocketConfigurer.accept(frontendSocket);
public void start() {
this.lock.lock();
try {
if (!this.running.get()) {
this.proxyExecutor
.execute(() -> {
ZMQ.Socket captureSocket = null;
if (this.exposeCaptureSocket) {
captureSocket = this.context.createSocket(SocketType.PUB);
}
try (
ZMQ.Socket frontendSocket = this.context
.createSocket(this.type.getFrontendSocketType());
ZMQ.Socket backendSocket = this.context
.createSocket(this.type.getBackendSocketType());
ZMQ.Socket controlSocket = this.context.createSocket(SocketType.PAIR)) {
if (this.backendSocketConfigurer != null) {
this.backendSocketConfigurer.accept(backendSocket);
}
if (this.frontendSocketConfigurer != null) {
this.frontendSocketConfigurer.accept(frontendSocket);
}
this.frontendPort.set(bindSocket(frontendSocket, this.frontendPort.get())); // NOSONAR
this.backendPort.set(bindSocket(backendSocket, this.backendPort.get())); // NOSONAR
boolean bound = controlSocket.bind(this.controlAddress); // NOSONAR
if (!bound) {
throw new IllegalArgumentException("Cannot bind ZeroMQ socket to address: "
+ this.controlAddress);
}
if (captureSocket != null) {
bound = captureSocket.bind(this.captureAddress);
if (this.backendSocketConfigurer != null) {
this.backendSocketConfigurer.accept(backendSocket);
}
this.frontendPort.set(bindSocket(frontendSocket, this.frontendPort.get())); // NOSONAR
this.backendPort.set(bindSocket(backendSocket, this.backendPort.get())); // NOSONAR
boolean bound = controlSocket.bind(this.controlAddress); // NOSONAR
if (!bound) {
throw new IllegalArgumentException("Cannot bind ZeroMQ socket to address: "
+ this.captureAddress);
+ this.controlAddress);
}
if (captureSocket != null) {
bound = captureSocket.bind(this.captureAddress);
if (!bound) {
throw new IllegalArgumentException("Cannot bind ZeroMQ socket to address: "
+ this.captureAddress);
}
}
this.running.set(true);
ZMQ.proxy(frontendSocket, backendSocket, captureSocket, controlSocket);
}
catch (Exception ex) { // NOSONAR
LOG.error("Cannot start ZeroMQ proxy from bean: " + this.beanName, ex);
}
finally {
if (captureSocket != null) {
captureSocket.close();
}
}
this.running.set(true);
ZMQ.proxy(frontendSocket, backendSocket, captureSocket, controlSocket);
}
catch (Exception ex) { // NOSONAR
LOG.error("Cannot start ZeroMQ proxy from bean: " + this.beanName, ex);
}
finally {
if (captureSocket != null) {
captureSocket.close();
}
}
});
});
}
}
finally {
this.lock.unlock();
}
}
@Override
public synchronized void stop() {
if (this.running.getAndSet(false)) {
try (ZMQ.Socket commandSocket = this.context.createSocket(SocketType.PAIR)) {
commandSocket.connect(this.controlAddress); // NOSONAR
commandSocket.send(zmq.ZMQ.PROXY_TERMINATE);
public void stop() {
this.lock.lock();
try {
if (this.running.getAndSet(false)) {
try (ZMQ.Socket commandSocket = this.context.createSocket(SocketType.PAIR)) {
commandSocket.connect(this.controlAddress); // NOSONAR
commandSocket.send(zmq.ZMQ.PROXY_TERMINATE);
}
}
}
finally {
this.lock.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2019 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.integration.zookeeper.config;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
@@ -31,12 +34,13 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 4.2
*/
public class CuratorFrameworkFactoryBean implements FactoryBean<CuratorFramework>, SmartLifecycle {
private final Object lifecycleLock = new Object();
private final Lock lifecycleLock = new ReentrantLock();
private final CuratorFramework client;
@@ -109,7 +113,8 @@ public class CuratorFrameworkFactoryBean implements FactoryBean<CuratorFramework
@Override
public void start() {
synchronized (this.lifecycleLock) {
this.lifecycleLock.lock();
try {
if (!this.running) {
if (this.client != null) {
this.client.start();
@@ -117,16 +122,23 @@ public class CuratorFrameworkFactoryBean implements FactoryBean<CuratorFramework
this.running = true;
}
}
finally {
this.lifecycleLock.unlock();
}
}
@Override
public void stop() {
synchronized (this.lifecycleLock) {
this.lifecycleLock.lock();
try {
if (this.running) {
CloseableUtils.closeQuietly(this.client);
this.running = false;
}
}
finally {
this.lifecycleLock.unlock();
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2020 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ import org.springframework.util.Assert;
*
* @author Gary Russell
* @author Artem Bilan
* @author Christian Tzolov
*
* @since 4.2
*/
@@ -177,7 +178,7 @@ public class LeaderInitiatorFactoryBean
}
@Override
public synchronized LeaderInitiator getObject() {
public LeaderInitiator getObject() {
return this.leaderInitiator;
}

View File

@@ -18,6 +18,8 @@ package org.springframework.integration.zookeeper.leader;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -43,6 +45,7 @@ import org.springframework.util.StringUtils;
* @author Gary Russell
* @author Artem Bilan
* @author Ivan Zaitsev
* @author Christian Tzolov
*
* @since 4.2
*/
@@ -66,7 +69,7 @@ public class LeaderInitiator implements SmartLifecycle {
*/
private final Candidate candidate;
private final Object lifecycleMonitor = new Object();
private final Lock lifecycleMonitor = new ReentrantLock();
/**
* Base path in a zookeeper
@@ -159,7 +162,8 @@ public class LeaderInitiator implements SmartLifecycle {
*/
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (!this.running) {
if (this.client.getState() != CuratorFrameworkState.STARTED) {
// we want to do curator start here because it needs to
@@ -177,6 +181,9 @@ public class LeaderInitiator implements SmartLifecycle {
LOGGER.debug("Started LeaderInitiator");
}
}
finally {
this.lifecycleMonitor.unlock();
}
}
/**
@@ -185,13 +192,17 @@ public class LeaderInitiator implements SmartLifecycle {
*/
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
this.lifecycleMonitor.lock();
try {
if (this.running) {
this.leaderSelector.close();
this.running = false;
LOGGER.debug("Stopped LeaderInitiator");
}
}
finally {
this.lifecycleMonitor.unlock();
}
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2021 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
@@ -44,6 +45,7 @@ import org.springframework.util.Assert;
* @author Artem Bilan
* @author Vedran Pavic
* @author Unseok Kim
* @author Christian Tzolov
*
* @since 4.2
*
@@ -58,6 +60,8 @@ public class ZookeeperLockRegistry implements ExpirableLockRegistry, DisposableB
private static final int DEFAULT_CAPACITY = 30_000;
private final Lock locksLock = new ReentrantLock();
private final Map<String, ZkLock> locks =
new LinkedHashMap<String, ZkLock>(16, 0.75F, true) {
@@ -145,9 +149,13 @@ public class ZookeeperLockRegistry implements ExpirableLockRegistry, DisposableB
Assert.isInstanceOf(String.class, lockKey);
String path = this.keyToPath.pathFor((String) lockKey);
ZkLock lock;
synchronized (this.locks) {
this.locksLock.lock();
try {
lock = this.locks.computeIfAbsent(path, p -> new ZkLock(this.client, this.mutexTaskExecutor, p));
}
finally {
this.locksLock.unlock();
}
if (this.trackingTime) {
lock.setLastUsed(System.currentTimeMillis());
}
@@ -155,10 +163,9 @@ public class ZookeeperLockRegistry implements ExpirableLockRegistry, DisposableB
}
/**
* Remove locks last acquired more than 'age' ago that are not currently locked.
* Expiry is not supported if the {@link KeyToPathStrategy} is bounded (returns a finite
* number of paths). With such a {@link KeyToPathStrategy}, the overhead of tracking when
* a lock is obtained is avoided.
* Remove locks last acquired more than 'age' ago that are not currently locked. Expiry is not supported if the
* {@link KeyToPathStrategy} is bounded (returns a finite number of paths). With such a {@link KeyToPathStrategy},
* the overhead of tracking when a lock is obtained is avoided.
* @param age the time since the lock was last obtained.
*/
@Override
@@ -168,13 +175,18 @@ public class ZookeeperLockRegistry implements ExpirableLockRegistry, DisposableB
}
long now = System.currentTimeMillis();
synchronized (this.locks) {
this.locksLock.lock();
try {
this.locks.entrySet()
.removeIf(entry -> {
ZkLock lock = entry.getValue();
return now - lock.getLastUsed() > age && !lock.isAcquiredInThisProcess();
});
}
finally {
this.locksLock.unlock();
}
}
@Override

Some files were not shown because too many files have changed in this diff Show More