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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user