Fix phase for TaskScheduler instances in tests
Related to: #8856 Many tests create their own `ThreadPoolTaskScheduler` beans. Therefore, its default phase might affect the memory and performance. * Use `phase = SmartLifecycle.DEFAULT_PHASE / 2` for manual `ThreadPoolTaskScheduler` beans * Migrate affected tests classes to JUnit 5 * Make some other configuration adjustments for better performance **Cherry-pick to `6.2.x`**
This commit is contained in:
@@ -1,19 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/task https://www.springframework.org/schema/task/spring-task.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
|
||||
|
||||
<context:property-placeholder properties-ref="props"/>
|
||||
|
||||
<util:properties id="props"/>
|
||||
<util:properties id="props"/>
|
||||
|
||||
<channel id="outputChannel">
|
||||
<queue capacity="5"/>
|
||||
@@ -40,96 +38,99 @@
|
||||
<beans:bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager"/>
|
||||
|
||||
<aggregator id="completelyDefinedAggregator"
|
||||
input-channel="completelyDefinedAggregatorInput"
|
||||
output-channel="outputChannel"
|
||||
discard-channel="discardChannel"
|
||||
ref="aggregatorBean"
|
||||
release-strategy="releaseStrategy"
|
||||
correlation-strategy="correlationStrategy"
|
||||
send-timeout="86420000"
|
||||
send-partial-result-on-expiry="true"
|
||||
expire-groups-upon-completion="true"
|
||||
expire-groups-upon-timeout="false"
|
||||
empty-group-min-timeout="123"
|
||||
group-timeout="456"
|
||||
lock-registry="lockRegistry"
|
||||
scheduler="scheduler"
|
||||
message-store="store"
|
||||
pop-sequence="false"
|
||||
order="5"
|
||||
expire-duration="10000"
|
||||
expire-timeout="250">
|
||||
<expire-transactional/>
|
||||
input-channel="completelyDefinedAggregatorInput"
|
||||
output-channel="outputChannel"
|
||||
discard-channel="discardChannel"
|
||||
ref="aggregatorBean"
|
||||
release-strategy="releaseStrategy"
|
||||
correlation-strategy="correlationStrategy"
|
||||
send-timeout="86420000"
|
||||
send-partial-result-on-expiry="true"
|
||||
expire-groups-upon-completion="true"
|
||||
expire-groups-upon-timeout="false"
|
||||
empty-group-min-timeout="123"
|
||||
group-timeout="456"
|
||||
lock-registry="lockRegistry"
|
||||
scheduler="scheduler"
|
||||
message-store="store"
|
||||
pop-sequence="false"
|
||||
order="5"
|
||||
expire-duration="10000"
|
||||
expire-timeout="250">
|
||||
<expire-transactional/>
|
||||
</aggregator>
|
||||
|
||||
<beans:bean id="lockRegistry" class="org.springframework.integration.support.locks.DefaultLockRegistry"/>
|
||||
|
||||
<task:scheduler id="scheduler"/>
|
||||
<beans:bean id="scheduler"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<beans:property name="phase" value="1073741823"/>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="store" class="org.springframework.integration.store.SimpleMessageStore"/>
|
||||
|
||||
<channel id="aggregatorWithExpressionsInput"/>
|
||||
<channel id="aggregatorWithExpressionsOutput"/>
|
||||
<aggregator id="aggregatorWithExpressions"
|
||||
input-channel="aggregatorWithExpressionsInput"
|
||||
output-channel="aggregatorWithExpressionsOutput"
|
||||
expression="?[payload.startsWith('1')].![payload]"
|
||||
release-strategy-expression="#root.size()>2"
|
||||
correlation-strategy-expression="headers['foo']"/>
|
||||
input-channel="aggregatorWithExpressionsInput"
|
||||
output-channel="aggregatorWithExpressionsOutput"
|
||||
expression="?[payload.startsWith('1')].![payload]"
|
||||
release-strategy-expression="#root.size()>2"
|
||||
correlation-strategy-expression="headers['foo']"/>
|
||||
|
||||
<channel id="aggregatorWithReferenceAndMethodInput"/>
|
||||
<aggregator id="aggregatorWithReferenceAndMethod"
|
||||
ref="adderBean"
|
||||
method="add"
|
||||
input-channel="aggregatorWithReferenceAndMethodInput"
|
||||
output-channel="outputChannel"/>
|
||||
ref="adderBean"
|
||||
method="add"
|
||||
input-channel="aggregatorWithReferenceAndMethodInput"
|
||||
output-channel="outputChannel"/>
|
||||
|
||||
<channel id="aggregatorWithPojoReleaseStrategyInput"/>
|
||||
<aggregator id="aggregatorWithPojoReleaseStrategy"
|
||||
input-channel="aggregatorWithPojoReleaseStrategyInput"
|
||||
output-channel="outputChannel"
|
||||
ref="adderBean"
|
||||
method="add"
|
||||
release-strategy="pojoReleaseStrategy"
|
||||
release-strategy-method="checkCompletenessAsList"/>
|
||||
input-channel="aggregatorWithPojoReleaseStrategyInput"
|
||||
output-channel="outputChannel"
|
||||
ref="adderBean"
|
||||
method="add"
|
||||
release-strategy="pojoReleaseStrategy"
|
||||
release-strategy-method="checkCompletenessAsList"/>
|
||||
|
||||
<channel id="aggregatorWithPojoReleaseStrategyInputAsCollection"/>
|
||||
<aggregator id="aggregatorWithPojoReleaseStrategyAsCollection"
|
||||
input-channel="aggregatorWithPojoReleaseStrategyInputAsCollection"
|
||||
output-channel="outputChannel"
|
||||
ref="adderBean"
|
||||
method="add"
|
||||
release-strategy="pojoReleaseStrategy"
|
||||
release-strategy-method="checkCompletenessAsCollection"/>
|
||||
input-channel="aggregatorWithPojoReleaseStrategyInputAsCollection"
|
||||
output-channel="outputChannel"
|
||||
ref="adderBean"
|
||||
method="add"
|
||||
release-strategy="pojoReleaseStrategy"
|
||||
release-strategy-method="checkCompletenessAsCollection"/>
|
||||
|
||||
<channel id="aggregatorWithExpressionsAndPojoAggregatorInput"/>
|
||||
<aggregator id="aggregatorWithExpressionsAndPojoAggregator"
|
||||
input-channel="aggregatorWithExpressionsAndPojoAggregatorInput"
|
||||
ref="aggregatorBean"
|
||||
release-strategy-expression="size() == 2"
|
||||
correlation-strategy-expression="headers['foo']"
|
||||
empty-group-min-timeout="60000"/>
|
||||
input-channel="aggregatorWithExpressionsAndPojoAggregatorInput"
|
||||
ref="aggregatorBean"
|
||||
release-strategy-expression="size() == 2"
|
||||
correlation-strategy-expression="headers['foo']"
|
||||
empty-group-min-timeout="60000"/>
|
||||
|
||||
<beans:bean id="aggregatorBean"
|
||||
class="org.springframework.integration.config.TestAggregatorBean" />
|
||||
class="org.springframework.integration.config.TestAggregatorBean"/>
|
||||
|
||||
<beans:bean id="aggregatorMGPBean"
|
||||
class="org.springframework.integration.aggregator.SimpleMessageGroupProcessor" />
|
||||
class="org.springframework.integration.aggregator.SimpleMessageGroupProcessor"/>
|
||||
|
||||
<beans:bean id="aggregatorCustomMGPBean"
|
||||
class="org.springframework.integration.config.AggregatorParserTests$MyMGP" />
|
||||
class="org.springframework.integration.config.AggregatorParserTests$MyMGP"/>
|
||||
|
||||
<beans:bean id="adderBean"
|
||||
class="org.springframework.integration.config.Adder" />
|
||||
<beans:bean id="adderBean"
|
||||
class="org.springframework.integration.config.Adder"/>
|
||||
|
||||
<beans:bean id="releaseStrategy"
|
||||
class="org.springframework.integration.config.TestReleaseStrategy" />
|
||||
class="org.springframework.integration.config.TestReleaseStrategy"/>
|
||||
|
||||
<beans:bean id="correlationStrategy" class="org.springframework.integration.config.TestCorrelationStrategy"/>
|
||||
|
||||
<beans:bean id="pojoReleaseStrategy"
|
||||
class="org.springframework.integration.config.MaxValueReleaseStrategy">
|
||||
<beans:constructor-arg value="10" />
|
||||
class="org.springframework.integration.config.MaxValueReleaseStrategy">
|
||||
<beans:constructor-arg value="10"/>
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
|
||||
<beans:bean id="testScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"
|
||||
p:poolSize="7"
|
||||
p:phase="1073741823"
|
||||
p:waitForTasksToCompleteOnShutdown="true"/>
|
||||
|
||||
<beans:bean id="testMessageStore" class="org.springframework.integration.store.SimpleMessageStore"/>
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
|
||||
<beans:bean id="multiThreadScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"
|
||||
p:poolSize="5"
|
||||
p:phase="1073741823"
|
||||
p:waitForTasksToCompleteOnShutdown="true"/>
|
||||
|
||||
<service-activator input-channel="outputB" output-channel="outputB1" method="processMessage" ref="sampleHandler"/>
|
||||
|
||||
@@ -24,10 +24,14 @@
|
||||
default-request-channel="requestChannel"
|
||||
proxy-default-methods="true"/>
|
||||
|
||||
<channel id="receiveChannel">
|
||||
<queue />
|
||||
</channel>
|
||||
|
||||
<gateway id="solicitResponse"
|
||||
service-interface="org.springframework.integration.gateway.TestService"
|
||||
default-reply-channel="replyChannel"
|
||||
default-reply-timeout="3000"/>
|
||||
default-reply-channel="receiveChannel"
|
||||
default-reply-timeout="5000"/>
|
||||
|
||||
<gateway id="requestReply"
|
||||
service-interface="org.springframework.integration.gateway.TestService"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -72,7 +72,7 @@ import static org.mockito.Mockito.when;
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
@DirtiesContext
|
||||
public class GatewayParserTests {
|
||||
|
||||
@Autowired
|
||||
@@ -132,7 +132,7 @@ public class GatewayParserTests {
|
||||
|
||||
@Test
|
||||
public void testSolicitResponse() {
|
||||
PollableChannel channel = (PollableChannel) context.getBean("replyChannel");
|
||||
PollableChannel channel = (PollableChannel) context.getBean("receiveChannel");
|
||||
channel.send(new GenericMessage<>("foo"));
|
||||
TestService service = (TestService) context.getBean("solicitResponse");
|
||||
String result = service.solicitResponse();
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
https://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<beans:bean id="taskScheduler"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler" />
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<beans:property name="phase" value="1073741823"/>
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2023 the original author or authors.
|
||||
* Copyright 2016-2024 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.
|
||||
@@ -41,6 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
@@ -586,6 +587,7 @@ public class IntegrationFlowTests {
|
||||
@Bean(name = IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME)
|
||||
public TaskScheduler taskScheduler() {
|
||||
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
threadPoolTaskScheduler.setPhase(SmartLifecycle.DEFAULT_PHASE / 2);
|
||||
threadPoolTaskScheduler.setPoolSize(100);
|
||||
return threadPoolTaskScheduler;
|
||||
}
|
||||
@@ -925,7 +927,9 @@ public class IntegrationFlowTests {
|
||||
|
||||
@Bean
|
||||
public TaskScheduler dedicatedTaskScheduler() {
|
||||
return new ThreadPoolTaskScheduler();
|
||||
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
threadPoolTaskScheduler.setPhase(SmartLifecycle.DEFAULT_PHASE / 2);
|
||||
return threadPoolTaskScheduler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -80,6 +80,7 @@ public class ExpressionEvaluatingMessageSourceIntegrationTests {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
messages.add(channel.receive(1000));
|
||||
}
|
||||
adapter.stop();
|
||||
scheduler.destroy();
|
||||
Message<?> message1 = messages.get(0);
|
||||
assertThat(message1.getPayload()).isEqualTo("test-1");
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/task https://www.springframework.org/schema/task/spring-task.xsd
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/task https://www.springframework.org/schema/task/spring-task.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
<task:scheduled ref="reaper3" method="run" fixed-rate="100"/>
|
||||
</task:scheduled-tasks>
|
||||
|
||||
<task:scheduler id="scheduler"/>
|
||||
<bean id="scheduler"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<property name="phase" value="1073741823"/>
|
||||
</bean>
|
||||
|
||||
<bean id="messageStore2" class="org.springframework.integration.store.SimpleMessageStore">
|
||||
<property name="expiryCallbacks" ref="expiryCallback2"/>
|
||||
@@ -35,21 +38,21 @@
|
||||
<property name="messageGroupStore" ref="messageStore2"/>
|
||||
</bean>
|
||||
|
||||
<bean id="messageStore3" class="org.springframework.integration.store.SimpleMessageStore" />
|
||||
<bean id="messageStore3" class="org.springframework.integration.store.SimpleMessageStore"/>
|
||||
|
||||
<bean id="reaper3" class="org.springframework.integration.store.MessageGroupStoreReaper">
|
||||
<property name="messageGroupStore" ref="messageStore3"/>
|
||||
<property name="timeout" value="50" />
|
||||
<property name="timeout" value="50"/>
|
||||
</bean>
|
||||
|
||||
<int:aggregator input-channel="aggChannel"
|
||||
discard-channel="discards"
|
||||
message-store="messageStore3" />
|
||||
discard-channel="discards"
|
||||
message-store="messageStore3"/>
|
||||
|
||||
<int:channel id="aggChannel" />
|
||||
<int:channel id="aggChannel"/>
|
||||
|
||||
<int:channel id="discards">
|
||||
<int:queue />
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -543,6 +543,7 @@ public class FileWritingMessageHandlerTests {
|
||||
}
|
||||
assertThat(flushes.get()).isGreaterThanOrEqualTo(2);
|
||||
handler.stop();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -586,6 +587,7 @@ public class FileWritingMessageHandlerTests {
|
||||
verify(out).write(any(byte[].class), anyInt(), anyInt());
|
||||
assertThat(closeWhileWriting.get()).isFalse();
|
||||
handler.stop();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int-file="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int-file="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/file https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
|
||||
http://www.springframework.org/schema/task https://www.springframework.org/schema/task/spring-task.xsd
|
||||
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
|
||||
@@ -16,37 +15,37 @@
|
||||
<int:channel id="tailErrorChannel"/>
|
||||
|
||||
<int-file:tail-inbound-channel-adapter id="default"
|
||||
channel="input"
|
||||
task-executor="exec"
|
||||
file="/tmp/baz"
|
||||
auto-startup="true"
|
||||
phase="123"
|
||||
error-channel="tailErrorChannel"/>
|
||||
channel="input"
|
||||
task-executor="exec"
|
||||
file="/tmp/baz"
|
||||
auto-startup="true"
|
||||
phase="123"
|
||||
error-channel="tailErrorChannel"/>
|
||||
|
||||
<int-file:tail-inbound-channel-adapter id="native"
|
||||
channel="input"
|
||||
native-options="-F -n 6"
|
||||
task-executor="exec"
|
||||
task-scheduler="sched"
|
||||
enable-status-reader="false"
|
||||
file-delay="456"
|
||||
file="/tmp/foo"
|
||||
auto-startup="true"
|
||||
delay="${empty}"
|
||||
end="${empty}"
|
||||
reopen="${empty}"
|
||||
phase="123" />
|
||||
channel="input"
|
||||
native-options="-F -n 6"
|
||||
task-executor="exec"
|
||||
task-scheduler="scheduler"
|
||||
enable-status-reader="false"
|
||||
file-delay="456"
|
||||
file="/tmp/foo"
|
||||
auto-startup="true"
|
||||
delay="${empty}"
|
||||
end="${empty}"
|
||||
reopen="${empty}"
|
||||
phase="123"/>
|
||||
|
||||
<int-file:tail-inbound-channel-adapter id="apacheDefault"
|
||||
channel="input"
|
||||
native-options="${empty}"
|
||||
task-executor="exec"
|
||||
file="/tmp/bar"
|
||||
delay="${foo}"
|
||||
file-delay="10000"
|
||||
idle-event-interval="10000"
|
||||
auto-startup="false"
|
||||
phase="123" />
|
||||
channel="input"
|
||||
native-options="${empty}"
|
||||
task-executor="exec"
|
||||
file="/tmp/bar"
|
||||
delay="${foo}"
|
||||
file-delay="10000"
|
||||
idle-event-interval="10000"
|
||||
auto-startup="false"
|
||||
phase="123"/>
|
||||
|
||||
<context:property-placeholder properties-ref="props"/>
|
||||
|
||||
@@ -56,20 +55,23 @@
|
||||
</util:properties>
|
||||
|
||||
<int-file:tail-inbound-channel-adapter id="apacheEndReopen"
|
||||
channel="input"
|
||||
task-executor="exec"
|
||||
file="/tmp/qux"
|
||||
delay="2000"
|
||||
file-delay="10000"
|
||||
end="false"
|
||||
reopen="true"
|
||||
auto-startup="false"
|
||||
phase="123" />
|
||||
channel="input"
|
||||
task-executor="exec"
|
||||
file="/tmp/qux"
|
||||
delay="2000"
|
||||
file-delay="10000"
|
||||
end="false"
|
||||
reopen="true"
|
||||
auto-startup="false"
|
||||
phase="123"/>
|
||||
|
||||
<int:channel id="input" />
|
||||
<int:channel id="input"/>
|
||||
|
||||
<task:scheduler id="sched" />
|
||||
<bean id="scheduler"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<property name="phase" value="1073741823"/>
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.integration.file.config.FileTailInboundChannelAdapterParserTests$Config" />
|
||||
<bean class="org.springframework.integration.file.config.FileTailInboundChannelAdapterParserTests$Config"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -66,7 +66,7 @@ public class FileTailInboundChannelAdapterParserTests {
|
||||
private TaskExecutor exec;
|
||||
|
||||
@Autowired
|
||||
private TaskScheduler sched;
|
||||
private TaskScheduler scheduler;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel tailErrorChannel;
|
||||
@@ -95,7 +95,7 @@ public class FileTailInboundChannelAdapterParserTests {
|
||||
assertThat(normalizedName).isEqualTo("/tmp/foo");
|
||||
assertThat(TestUtils.getPropertyValue(nativeAdapter, "command")).isEqualTo("tail -F -n 6 " + fileName);
|
||||
assertThat(TestUtils.getPropertyValue(nativeAdapter, "taskExecutor")).isSameAs(exec);
|
||||
assertThat(TestUtils.getPropertyValue(nativeAdapter, "taskScheduler")).isSameAs(sched);
|
||||
assertThat(TestUtils.getPropertyValue(nativeAdapter, "taskScheduler")).isSameAs(scheduler);
|
||||
assertThat(TestUtils.getPropertyValue(nativeAdapter, "autoStartup", Boolean.class)).isTrue();
|
||||
assertThat(TestUtils.getPropertyValue(nativeAdapter, "enableStatusReader", Boolean.class)).isFalse();
|
||||
assertThat(TestUtils.getPropertyValue(nativeAdapter, "phase")).isEqualTo(123);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -144,8 +144,7 @@ public class FileTailingMessageProducerTests {
|
||||
if (event instanceof FileTailingIdleEvent) {
|
||||
idleCountDownLatch.countDown();
|
||||
}
|
||||
if (event instanceof FileTailingEvent) {
|
||||
FileTailingEvent fileTailingEvent = (FileTailingEvent) event;
|
||||
if (event instanceof FileTailingEvent fileTailingEvent) {
|
||||
if (fileTailingEvent.getMessage().contains("File not found")) {
|
||||
fileExistCountDownLatch.countDown();
|
||||
}
|
||||
@@ -184,6 +183,7 @@ public class FileTailingMessageProducerTests {
|
||||
assertThat(eventRaised).as("idle event did not emit").isTrue();
|
||||
adapter.stop();
|
||||
file.delete();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
private void testGuts(FileTailingMessageProducerSupport adapter, String field) throws Exception {
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
|
||||
<groovy:control-bus input-channel="controlBus" output-channel="controlBusOutput"/>
|
||||
|
||||
<task:scheduler id="scheduler"/>
|
||||
<beans:bean id="scheduler"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<beans:property name="phase" value="1073741823"/>
|
||||
</beans:bean>
|
||||
|
||||
<delayer id="testDelayer" input-channel="delayerInput" output-channel="output"
|
||||
default-delay="1000"
|
||||
|
||||
@@ -399,7 +399,7 @@
|
||||
client-mode="true"
|
||||
retry-interval="123000"
|
||||
auto-startup="false"
|
||||
scheduler="sched" />
|
||||
scheduler="scheduler" />
|
||||
|
||||
<ip:tcp-connection-factory id="cfC4"
|
||||
type="client"
|
||||
@@ -414,7 +414,7 @@
|
||||
connection-factory="cfC4"
|
||||
client-mode="true"
|
||||
retry-interval="124000"
|
||||
scheduler="sched"
|
||||
scheduler="scheduler"
|
||||
auto-startup="false" />
|
||||
|
||||
<ip:tcp-connection-factory id="cfC5"
|
||||
@@ -432,11 +432,14 @@
|
||||
reply-timeout="456"
|
||||
client-mode="true"
|
||||
retry-interval="125000"
|
||||
scheduler="sched"
|
||||
scheduler="scheduler"
|
||||
auto-startup="false"
|
||||
/>
|
||||
|
||||
<task:scheduler id="sched"/>
|
||||
<bean id="scheduler"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<property name="phase" value="1073741823"/>
|
||||
</bean>
|
||||
|
||||
<ip:tcp-inbound-channel-adapter id="tcpAutoChannel" />
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -236,7 +236,7 @@ public class ParserUnitTests {
|
||||
TcpInboundGateway inGatewayClientMode;
|
||||
|
||||
@Autowired
|
||||
TaskScheduler sched;
|
||||
TaskScheduler scheduler;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("tcpOutClientMode.handler")
|
||||
@@ -624,7 +624,7 @@ public class ParserUnitTests {
|
||||
assertThat(dfa.getPropertyValue("clientConnectionFactory")).isSameAs(cfC3);
|
||||
assertThat(dfa.getPropertyValue("serverConnectionFactory")).isNull();
|
||||
assertThat(dfa.getPropertyValue("isClientMode")).isEqualTo(Boolean.TRUE);
|
||||
assertThat(dfa.getPropertyValue("taskScheduler")).isSameAs(sched);
|
||||
assertThat(dfa.getPropertyValue("taskScheduler")).isSameAs(scheduler);
|
||||
assertThat(dfa.getPropertyValue("retryInterval")).isEqualTo(123000L);
|
||||
}
|
||||
|
||||
@@ -634,7 +634,7 @@ public class ParserUnitTests {
|
||||
assertThat(dfa.getPropertyValue("clientConnectionFactory")).isSameAs(cfC4);
|
||||
assertThat(dfa.getPropertyValue("serverConnectionFactory")).isNull();
|
||||
assertThat(dfa.getPropertyValue("isClientMode")).isEqualTo(Boolean.TRUE);
|
||||
assertThat(dfa.getPropertyValue("taskScheduler")).isSameAs(sched);
|
||||
assertThat(dfa.getPropertyValue("taskScheduler")).isSameAs(scheduler);
|
||||
assertThat(dfa.getPropertyValue("retryInterval")).isEqualTo(124000L);
|
||||
}
|
||||
|
||||
@@ -644,7 +644,7 @@ public class ParserUnitTests {
|
||||
assertThat(dfa.getPropertyValue("clientConnectionFactory")).isSameAs(cfC5);
|
||||
assertThat(dfa.getPropertyValue("serverConnectionFactory")).isNull();
|
||||
assertThat(dfa.getPropertyValue("isClientMode")).isEqualTo(Boolean.TRUE);
|
||||
assertThat(dfa.getPropertyValue("taskScheduler")).isSameAs(sched);
|
||||
assertThat(dfa.getPropertyValue("taskScheduler")).isSameAs(scheduler);
|
||||
assertThat(dfa.getPropertyValue("retryInterval")).isEqualTo(125000L);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -193,6 +193,7 @@ public class TcpInboundGatewayTests {
|
||||
assertThat(done.get()).isTrue();
|
||||
gateway.stop();
|
||||
executorService.shutdown();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.net.ServerSocketFactory;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.serializer.DefaultDeserializer;
|
||||
@@ -150,6 +150,7 @@ public class TcpReceivingChannelAdapterTests extends AbstractTcpChannelAdapterTe
|
||||
latch2.countDown();
|
||||
ccf.stop();
|
||||
serverSocket.get().close();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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,7 +39,7 @@ import javax.net.SocketFactory;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
@@ -217,11 +217,12 @@ public class TcpSendingMessageHandlerTests extends AbstractTcpChannelAdapterTest
|
||||
adapter.stop();
|
||||
ccf.stop();
|
||||
serverSocket.get().close();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNioCrLf() throws Exception {
|
||||
final AtomicReference<ServerSocket> serverSocket = new AtomicReference<ServerSocket>();
|
||||
final AtomicReference<ServerSocket> serverSocket = new AtomicReference<>();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean done = new AtomicBoolean();
|
||||
this.executor.execute(() -> {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
<beans:bean id="taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<beans:property name="daemon" value="true" />
|
||||
<beans:property name="phase" value="1073741823" />
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -149,6 +149,7 @@ public class JmsOutboundGatewayTests extends ActiveMQMultiContextTests {
|
||||
finally {
|
||||
gateway.stop();
|
||||
exec.shutdownNow();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/task https://www.springframework.org/schema/task/spring-task.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<import resource="common-context.xml" />
|
||||
<import resource="common-context.xml"/>
|
||||
|
||||
<int:channel id="input">
|
||||
<int:queue />
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int:bridge input-channel="input" output-channel="input2">
|
||||
<int:poller fixed-delay="200" />
|
||||
<int:poller fixed-delay="200"/>
|
||||
</int:bridge>
|
||||
|
||||
|
||||
<int:channel id="input2">
|
||||
<int:queue />
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int:inbound-channel-adapter id="explicit" ref="service" method="execute" channel="input">
|
||||
<int:poller fixed-rate="200" />
|
||||
<int:poller fixed-rate="200"/>
|
||||
</int:inbound-channel-adapter>
|
||||
|
||||
<bean id="service" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$SimpleService" />
|
||||
<bean id="service" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$SimpleService"/>
|
||||
|
||||
<bean id="activeChannel" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$ActiveChannelImpl" />
|
||||
<bean id="activeChannel"
|
||||
class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$ActiveChannelImpl"/>
|
||||
|
||||
<task:scheduler id="someScheduler" />
|
||||
|
||||
<task:executor id="someExecutor" />
|
||||
|
||||
<bean id="nonSpringExecutor" class="java.util.concurrent.Executors" factory-method="newSingleThreadExecutor" />
|
||||
|
||||
<bean id="otherActiveComponent" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$OtherActiveComponent">
|
||||
<property name="outputChannel" ref="input" />
|
||||
<bean id="someScheduler"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<property name="phase" value="1073741823"/>
|
||||
</bean>
|
||||
|
||||
<bean id="messageProducer" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$AMessageProducer">
|
||||
<property name="outputChannel" ref="input" />
|
||||
<task:executor id="someExecutor"/>
|
||||
|
||||
<bean id="nonSpringExecutor" class="java.util.concurrent.Executors" factory-method="newSingleThreadExecutor"/>
|
||||
|
||||
<bean id="otherActiveComponent"
|
||||
class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$OtherActiveComponent">
|
||||
<property name="outputChannel" ref="input"/>
|
||||
</bean>
|
||||
|
||||
<bean id="messageProducer"
|
||||
class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$AMessageProducer">
|
||||
<property name="outputChannel" ref="input"/>
|
||||
</bean>
|
||||
|
||||
<bean id="ignoreWrappedExecutor" class="org.springframework.core.task.support.ExecutorServiceAdapter">
|
||||
<constructor-arg ref="someExecutor" />
|
||||
<constructor-arg ref="someExecutor"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
|
||||
<bean id="activeChannel" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$ActiveChannelImpl" />
|
||||
|
||||
<task:scheduler id="someScheduler" />
|
||||
<bean id="someScheduler"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
<property name="phase" value="1073741823"/>
|
||||
</bean>
|
||||
|
||||
<task:executor id="someExecutor" />
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2022 the original author or authors.
|
||||
* Copyright 2015-2024 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.
|
||||
@@ -69,6 +69,8 @@ public class StompServerIntegrationTests {
|
||||
|
||||
private static final EmbeddedActiveMQ broker = new EmbeddedActiveMQ();
|
||||
|
||||
private static final ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
|
||||
|
||||
private static ReactorNettyTcpStompClient stompClient;
|
||||
|
||||
@BeforeAll
|
||||
@@ -89,7 +91,6 @@ public class StompServerIntegrationTests {
|
||||
|
||||
stompClient = new ReactorNettyTcpStompClient("127.0.0.1", TransportConstants.DEFAULT_STOMP_PORT);
|
||||
stompClient.setMessageConverter(new PassThruMessageConverter());
|
||||
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
|
||||
taskScheduler.afterPropertiesSet();
|
||||
stompClient.setTaskScheduler(taskScheduler);
|
||||
stompClient.setReceiptTimeLimit(5000);
|
||||
@@ -99,6 +100,7 @@ public class StompServerIntegrationTests {
|
||||
public static void teardown() throws Exception {
|
||||
stompClient.shutdown();
|
||||
broker.stop();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -36,6 +36,7 @@ import org.apache.logging.log4j.core.config.LoggerConfig;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -61,6 +62,27 @@ public abstract class TestUtils {
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(TestUtils.class);
|
||||
|
||||
/**
|
||||
* Obtain a value for the property from the provide object
|
||||
* and try to cast it to the provided type.
|
||||
* Supports nested properties via period delimiter.
|
||||
* @param root the object to obtain the property value
|
||||
* @param propertyPath the property name to obtain a value.
|
||||
* @param type the expected value type.
|
||||
* @param <T> the expected value type.
|
||||
* Can be nested path defined by the period.
|
||||
* @return the value of the property or null
|
||||
* @see DirectFieldAccessor
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getPropertyValue(Object root, String propertyPath, Class<T> type) {
|
||||
Object value = getPropertyValue(root, propertyPath);
|
||||
if (value != null) {
|
||||
Assert.isAssignable(type, value.getClass());
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a value for the property from the provide object.
|
||||
* Supports nested properties via period delimiter.
|
||||
@@ -90,27 +112,6 @@ public abstract class TestUtils {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a value for the property from the provide object
|
||||
* and try to cast it to the provided type.
|
||||
* Supports nested properties via period delimiter.
|
||||
* @param root the object to obtain the property value
|
||||
* @param propertyPath the property name to obtain a value.
|
||||
* @param type the expected value type.
|
||||
* @param <T> the expected value type.
|
||||
* Can be nested path defined by the period.
|
||||
* @return the value of the property or null
|
||||
* @see DirectFieldAccessor
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getPropertyValue(Object root, String propertyPath, Class<T> type) {
|
||||
Object value = getPropertyValue(root, propertyPath);
|
||||
if (value != null) {
|
||||
Assert.isAssignable(type, value.getClass());
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link TestApplicationContext} instance
|
||||
* supplied with the basic Spring Integration infrastructure.
|
||||
@@ -140,6 +141,7 @@ public abstract class TestUtils {
|
||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
scheduler.setPoolSize(poolSize);
|
||||
scheduler.setRejectedExecutionHandler(new CallerRunsPolicy());
|
||||
scheduler.setPhase(SmartLifecycle.DEFAULT_PHASE / 2);
|
||||
scheduler.afterPropertiesSet();
|
||||
return scheduler;
|
||||
}
|
||||
@@ -216,6 +218,7 @@ public abstract class TestUtils {
|
||||
*/
|
||||
public static Properties locateComponentInHistory(List<Properties> history, String componentName,
|
||||
int startingIndex) {
|
||||
|
||||
Assert.notNull(history, "'history' must not be null");
|
||||
Assert.isTrue(StringUtils.hasText(componentName), "'componentName' must be provided");
|
||||
Assert.isTrue(startingIndex < history.size(), "'startingIndex' can not be greater then size of history");
|
||||
|
||||
Reference in New Issue
Block a user