Fix Cyclomatic Complexity in Splitter

* Upgrade some dependencies
* Migrate `SplitterIntegrationTests` to JUnit 5
This commit is contained in:
Artem Bilan
2021-04-14 13:50:00 -04:00
parent 1a288cd527
commit e901c89fef
3 changed files with 143 additions and 157 deletions

View File

@@ -1,5 +1,5 @@
buildscript {
ext.kotlinVersion = '1.4.31'
ext.kotlinVersion = '1.4.32'
repositories {
mavenCentral()
maven { url 'https://plugins.gradle.org/m2' }
@@ -13,7 +13,7 @@ buildscript {
plugins {
id 'org.sonarqube' version '2.8'
id 'io.spring.nohttp' version '0.0.5.RELEASE' apply false
id 'io.spring.nohttp' version '0.0.6.RELEASE' apply false
id 'org.ajoberstar.grgit' version '4.1.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'com.jfrog.artifactory' version '4.21.0' apply false
@@ -51,7 +51,7 @@ ext {
aspectjVersion = '1.9.6'
assertjVersion = '3.19.0'
assertkVersion = '0.23.1'
avroVersion = '1.10.1'
avroVersion = '1.10.2'
awaitilityVersion = '4.0.3'
commonsDbcp2Version = '2.8.0'
commonsIoVersion = '2.8.0'
@@ -62,34 +62,34 @@ ext {
googleJsr305Version = '3.0.2'
groovyVersion = '3.0.7'
hamcrestVersion = '2.2'
hazelcastVersion = '4.1.2'
hibernateVersion = '5.4.29.Final'
hsqldbVersion = '2.5.1'
hazelcastVersion = '4.2'
hibernateVersion = '5.4.30.Final'
hsqldbVersion = '2.6.0'
h2Version = '1.4.200'
jacksonVersion = '2.12.2'
jacksonVersion = '2.12.3'
javaxActivationVersion = '1.2.0'
jaxbVersion = '2.3.3'
jaxbVersion = '2.3.4'
jeroMqVersion = '0.5.2'
jmsApiVersion = '2.0.1'
jpaApiVersion = '2.7.7'
jrubyVersion = '9.2.16.0'
jrubyVersion = '9.2.17.0'
jschVersion = '0.1.55'
jsonpathVersion = '2.5.0'
junit4Version = '4.13.2'
junitJupiterVersion = '5.7.1'
jythonVersion = '2.7.2'
kryoShadedVersion = '4.0.2'
lettuceVersion = '6.1.0.M1'
lettuceVersion = '6.1.1.RELEASE'
log4jVersion = '2.14.1'
mailVersion = '1.6.5'
micrometerVersion = '1.6.5'
mockitoVersion = '3.8.0'
mongoDriverVersion = '4.2.2'
mailVersion = '1.6.7'
micrometerVersion = '1.7.0-RC1'
mockitoVersion = '3.9.0'
mongoDriverVersion = '4.2.3'
mysqlVersion = '8.0.23'
pahoMqttClientVersion = '1.2.5'
postgresVersion = '42.2.19'
r2dbch2Version='0.8.4.RELEASE'
reactorVersion = '2020.0.5'
reactorVersion = '2020.0.6'
resilience4jVersion = '1.7.0'
romeToolsVersion = '1.15.0'
rsocketVersion = '1.1.0'
@@ -98,13 +98,13 @@ ext {
smackVersion = '4.3.5'
soapVersion = '1.4.0'
springAmqpVersion = project.hasProperty('springAmqpVersion') ? project.springAmqpVersion : '2.3.6'
springDataVersion = project.hasProperty('springDataVersion') ? project.springDataVersion : '2021.0.0-SNAPSHOT'
springKafkaVersion = '2.7.0-SNAPSHOT'
springDataVersion = project.hasProperty('springDataVersion') ? project.springDataVersion : '2021.0.0'
springKafkaVersion = '2.7.0'
springRetryVersion = '1.3.1'
springSecurityVersion = project.hasProperty('springSecurityVersion') ? project.springSecurityVersion : '5.5.0-SNAPSHOT'
springVersion = project.hasProperty('springVersion') ? project.springVersion : '5.3.6-SNAPSHOT'
springWsVersion = '3.1.0-SNAPSHOT'
tomcatVersion = '9.0.44'
springSecurityVersion = project.hasProperty('springSecurityVersion') ? project.springSecurityVersion : '5.5.0-RC1'
springVersion = project.hasProperty('springVersion') ? project.springVersion : '5.3.6'
springWsVersion = '3.1.0-M2'
tomcatVersion = '9.0.45'
xmlUnitVersion = '2.8.2'
xstreamVersion = '1.4.16'
@@ -341,7 +341,7 @@ configure(javaProjects) { subproject ->
checkstyle {
configDirectory.set(rootProject.file('src/checkstyle'))
toolVersion = project.hasProperty('checkstyleVersion') ? project.checkstyleVersion : '8.41'
toolVersion = project.hasProperty('checkstyleVersion') ? project.checkstyleVersion : '8.41.1'
}
jar {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@@ -120,7 +120,6 @@ public abstract class AbstractMessageSplitter extends AbstractReplyProducingMess
}
@Override
@SuppressWarnings("unchecked")
protected final Object handleRequestMessage(Message<?> message) {
Object result = splitMessage(message);
// return null if 'null'
@@ -131,72 +130,90 @@ public abstract class AbstractMessageSplitter extends AbstractReplyProducingMess
boolean reactive = getOutputChannel() instanceof ReactiveStreamsSubscribableChannel;
setAsync(reactive);
Iterator<Object> iterator = null;
Flux<Object> flux = null;
final int sequenceSize;
if (reactive) {
return prepareFluxResult(message, result);
}
else {
return prepareIteratorResult(message, result);
}
}
@SuppressWarnings("unchecked")
private Flux<?> prepareFluxResult(Message<?> message, Object result) {
int sequenceSize = 1;
Flux<?> flux = Flux.just(result);
if (result instanceof Iterable<?>) {
Iterable<Object> iterable = (Iterable<Object>) result;
sequenceSize = obtainSizeIfPossible(iterable);
if (reactive) {
flux = Flux.fromIterable(iterable);
}
else {
iterator = iterable.iterator();
}
flux = Flux.fromIterable(iterable);
}
else if (result.getClass().isArray()) {
Object[] items = ObjectUtils.toObjectArray(result);
sequenceSize = items.length;
if (reactive) {
flux = Flux.fromArray(items);
}
else {
iterator = Arrays.asList(items).iterator();
}
flux = Flux.fromArray(items);
}
else if (result instanceof Iterator<?>) {
Iterator<Object> iter = (Iterator<Object>) result;
sequenceSize = obtainSizeIfPossible(iter);
if (reactive) {
flux = Flux.fromIterable(() -> iter);
}
else {
iterator = iter;
}
flux = Flux.fromIterable(() -> iter);
}
else if (result instanceof Stream<?>) {
Stream<Object> stream = ((Stream<Object>) result);
sequenceSize = 0;
if (reactive) {
flux = Flux.fromStream(stream);
}
else {
iterator = stream.iterator();
}
flux = Flux.fromStream(stream);
}
else if (result instanceof Publisher<?>) {
Publisher<Object> publisher = (Publisher<Object>) result;
sequenceSize = 0;
if (reactive) {
flux = Flux.from(publisher);
}
else {
iterator = Flux.from((Publisher<Object>) result).toIterable().iterator();
}
}
else {
sequenceSize = 1;
if (reactive) {
flux = Flux.just(result);
}
else {
iterator = Collections.singleton(result).iterator();
}
flux = Flux.from(publisher);
}
if (iterator != null && !iterator.hasNext()) {
Function<Object, ?> messageBuilderFunction = prepareMessageBuilderFunction(message, sequenceSize);
return flux
.map(messageBuilderFunction)
.switchIfEmpty(
Mono.defer(() -> {
MessageChannel discardingChannel = getDiscardChannel();
if (discardingChannel != null) {
this.messagingTemplate.send(discardingChannel, message);
}
return Mono.empty();
}));
}
@SuppressWarnings("unchecked")
private Iterator<?> prepareIteratorResult(Message<?> message, Object result) {
int sequenceSize = 1;
Iterator<?> iterator = Collections.singleton(result).iterator();
if (result instanceof Iterable<?>) {
Iterable<Object> iterable = (Iterable<Object>) result;
sequenceSize = obtainSizeIfPossible(iterable);
iterator = iterable.iterator();
}
else if (result.getClass().isArray()) {
Object[] items = ObjectUtils.toObjectArray(result);
sequenceSize = items.length;
iterator = Arrays.asList(items).iterator();
}
else if (result instanceof Iterator<?>) {
Iterator<Object> iter = (Iterator<Object>) result;
sequenceSize = obtainSizeIfPossible(iter);
iterator = iter;
}
else if (result instanceof Stream<?>) {
Stream<Object> stream = ((Stream<Object>) result);
sequenceSize = 0;
iterator = stream.iterator();
}
else if (result instanceof Publisher<?>) {
sequenceSize = 0;
iterator = Flux.from((Publisher<?>) result).toIterable().iterator();
}
if (!iterator.hasNext()) {
MessageChannel discardingChannel = getDiscardChannel();
if (discardingChannel != null) {
this.messagingTemplate.send(discardingChannel, message);
@@ -204,35 +221,25 @@ public abstract class AbstractMessageSplitter extends AbstractReplyProducingMess
return null;
}
Function<Object, ?> messageBuilderFunction = prepareMessageBuilderFunction(message, sequenceSize);
return new FunctionIterator<>(
result instanceof AutoCloseable && !result.equals(iterator) ? (AutoCloseable) result : null,
iterator, messageBuilderFunction);
}
private Function<Object, ?> prepareMessageBuilderFunction(Message<?> message, int sequenceSize) {
Map<String, Object> messageHeaders = message.getHeaders();
if (willAddHeaders(message)) {
messageHeaders = new HashMap<>(messageHeaders);
addHeaders(message, messageHeaders);
}
final Map<String, Object> headers = messageHeaders;
final Object correlationId = message.getHeaders().getId();
final AtomicInteger sequenceNumber = new AtomicInteger(1);
Map<String, Object> headers = messageHeaders;
Object correlationId = message.getHeaders().getId();
AtomicInteger sequenceNumber = new AtomicInteger(1);
Function<Object, AbstractIntegrationMessageBuilder<?>> messageBuilderFunction =
object -> createBuilder(object, headers, correlationId, sequenceNumber.getAndIncrement(), sequenceSize);
if (reactive) {
return flux
.map(messageBuilderFunction)
.switchIfEmpty(
Mono.defer(() -> {
MessageChannel discardingChannel = getDiscardChannel();
if (discardingChannel != null) {
this.messagingTemplate.send(discardingChannel, message);
}
return Mono.empty();
}));
}
else {
return new FunctionIterator<>(result instanceof AutoCloseable && !result.equals(iterator)
? (AutoCloseable) result : null, iterator, messageBuilderFunction);
}
return object -> createBuilder(object, headers, correlationId, sequenceNumber.getAndIncrement(), sequenceSize);
}
/**
@@ -349,7 +356,6 @@ public abstract class AbstractMessageSplitter extends AbstractReplyProducingMess
return object instanceof TreeNode;
}
@SuppressWarnings("unchecked")
private static int nodeSize(Object node) {
return ((TreeNode) node).size();
}

View File

@@ -17,15 +17,15 @@
package org.springframework.integration.splitter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
@@ -39,19 +39,17 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Iwein Fuld
* @author Alexander Peters
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@SpringJUnitConfig
@DirtiesContext
public class SplitterIntegrationTests {
@Autowired
@@ -77,19 +75,21 @@ public class SplitterIntegrationTests {
@Autowired
Receiver receiver;
@Before
@BeforeEach
public void clearWords() {
receiver.receivedWords.clear();
}
@MessageEndpoint
public static class Receiver {
private final List<String> receivedWords = new ArrayList<String>();
private final List<String> receivedWords = new ArrayList<>();
@ServiceActivator(inputChannel = "out")
public void deliveredWords(String string) {
this.receivedWords.add(string);
}
}
@MessageEndpoint
@@ -99,88 +99,68 @@ public class SplitterIntegrationTests {
public Iterator<String> split(String sentence) {
return Arrays.asList(sentence.split("\\s")).iterator();
}
}
@Test
public void configOk() throws Exception {
// just checking the parsing
}
@Test
public void annotated() throws Exception {
inAnnotated.send(new GenericMessage<String>(sentence));
inAnnotated.send(new GenericMessage<>(sentence));
assertThat(this.receiver.receivedWords.containsAll(words)).isTrue();
assertThat(words.containsAll(this.receiver.receivedWords)).isTrue();
}
@Test
public void methodInvoking() throws Exception {
inMethodInvoking.send(new GenericMessage<String>(sentence));
public void methodInvoking() {
inMethodInvoking.send(new GenericMessage<>(sentence));
assertThat(receiver.receivedWords.containsAll(words)).isTrue();
assertThat(words.containsAll(this.receiver.receivedWords)).isTrue();
}
@Test
public void defaultSplitter() throws Exception {
inDefault.send(new GenericMessage<List<String>>(words));
public void defaultSplitter() {
inDefault.send(new GenericMessage<>(words));
assertThat(receiver.receivedWords.containsAll(words)).isTrue();
assertThat(words.containsAll(receiver.receivedWords)).isTrue();
}
@Test
public void delimiterSplitter() throws Exception {
inDelimiters.send(new GenericMessage<String>("one,two, three; four/five"));
public void delimiterSplitter() {
inDelimiters.send(new GenericMessage<>("one,two, three; four/five"));
assertThat(receiver.receivedWords.containsAll(Arrays.asList("one", "two", "three", "four", "five"))).isTrue();
}
@Test(expected = IllegalArgumentException.class)
public void delimitersNotAllowedWithRef() throws Throwable {
try {
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidRef.xml",
SplitterIntegrationTests.class).close();
}
catch (BeanCreationException e) {
Throwable cause = e.getMostSpecificCause();
assertThat(cause).isNotNull();
assertThat(cause instanceof IllegalArgumentException).isTrue();
assertThat(cause.getMessage().contains("'delimiters' property is only available")).isTrue();
throw cause;
}
}
@Test(expected = IllegalArgumentException.class)
public void delimitersNotAllowedWithInnerBean() throws Throwable {
try {
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidInnerBean.xml",
SplitterIntegrationTests.class).close();
}
catch (BeanCreationException e) {
Throwable cause = e.getMostSpecificCause();
assertThat(cause).isNotNull();
assertThat(cause instanceof IllegalArgumentException).isTrue();
assertThat(cause.getMessage().contains("'delimiters' property is only available")).isTrue();
throw cause;
}
}
@Test(expected = IllegalArgumentException.class)
public void delimitersNotAllowedWithExpression() throws Throwable {
try {
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidExpression.xml",
SplitterIntegrationTests.class).close();
}
catch (BeanCreationException e) {
Throwable cause = e.getMostSpecificCause();
assertThat(cause).isNotNull();
assertThat(cause instanceof IllegalArgumentException).isTrue();
assertThat(cause.getMessage().contains("'delimiters' property is only available")).isTrue();
throw cause;
}
@Test
public void delimitersNotAllowedWithRef() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() ->
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidRef.xml",
SplitterIntegrationTests.class))
.withRootCauseExactlyInstanceOf(IllegalArgumentException.class)
.withMessageContaining("'delimiters' property is only available");
}
@Test
public void channelResolver_isNotNull() throws Exception {
public void delimitersNotAllowedWithInnerBean() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() ->
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidInnerBean.xml",
SplitterIntegrationTests.class))
.withRootCauseExactlyInstanceOf(IllegalArgumentException.class)
.withMessageContaining("'delimiters' property is only available");
}
@Test
public void delimitersNotAllowedWithExpression() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() ->
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidExpression.xml",
SplitterIntegrationTests.class))
.withRootCauseExactlyInstanceOf(IllegalArgumentException.class)
.withMessageContaining("'delimiters' property is only available");
}
@Test
public void channelResolver_isNotNull() {
splitter.setOutputChannel(null);
Message<String> message = MessageBuilder.withPayload("fooBar")
.setReplyChannelName("out").build();