From e901c89fef3eea00ddf6d503ae9926667a1d6972 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 14 Apr 2021 13:50:00 -0400 Subject: [PATCH] Fix Cyclomatic Complexity in Splitter * Upgrade some dependencies * Migrate `SplitterIntegrationTests` to JUnit 5 --- build.gradle | 44 +++--- .../splitter/AbstractMessageSplitter.java | 142 +++++++++--------- .../splitter/SplitterIntegrationTests.java | 114 ++++++-------- 3 files changed, 143 insertions(+), 157 deletions(-) diff --git a/build.gradle b/build.gradle index 844f64517b..552a01aaeb 100644 --- a/build.gradle +++ b/build.gradle @@ -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 { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/splitter/AbstractMessageSplitter.java b/spring-integration-core/src/main/java/org/springframework/integration/splitter/AbstractMessageSplitter.java index 5e2988cd30..944878e3d9 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/splitter/AbstractMessageSplitter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/splitter/AbstractMessageSplitter.java @@ -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 iterator = null; - Flux 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 iterable = (Iterable) 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 iter = (Iterator) result; sequenceSize = obtainSizeIfPossible(iter); - if (reactive) { - flux = Flux.fromIterable(() -> iter); - } - else { - iterator = iter; - } + flux = Flux.fromIterable(() -> iter); } else if (result instanceof Stream) { Stream stream = ((Stream) result); sequenceSize = 0; - if (reactive) { - flux = Flux.fromStream(stream); - } - else { - iterator = stream.iterator(); - } + flux = Flux.fromStream(stream); } else if (result instanceof Publisher) { Publisher publisher = (Publisher) result; sequenceSize = 0; - if (reactive) { - flux = Flux.from(publisher); - } - else { - iterator = Flux.from((Publisher) 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 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 iterable = (Iterable) 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 iter = (Iterator) result; + sequenceSize = obtainSizeIfPossible(iter); + iterator = iter; + } + else if (result instanceof Stream) { + Stream stream = ((Stream) 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 messageBuilderFunction = prepareMessageBuilderFunction(message, sequenceSize); + + return new FunctionIterator<>( + result instanceof AutoCloseable && !result.equals(iterator) ? (AutoCloseable) result : null, + iterator, messageBuilderFunction); + } + + private Function prepareMessageBuilderFunction(Message message, int sequenceSize) { Map messageHeaders = message.getHeaders(); if (willAddHeaders(message)) { messageHeaders = new HashMap<>(messageHeaders); addHeaders(message, messageHeaders); } - final Map headers = messageHeaders; - final Object correlationId = message.getHeaders().getId(); - final AtomicInteger sequenceNumber = new AtomicInteger(1); + Map headers = messageHeaders; + Object correlationId = message.getHeaders().getId(); + AtomicInteger sequenceNumber = new AtomicInteger(1); - Function> 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(); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/splitter/SplitterIntegrationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/splitter/SplitterIntegrationTests.java index 99bca15c33..4617d71a16 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/splitter/SplitterIntegrationTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/splitter/SplitterIntegrationTests.java @@ -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 receivedWords = new ArrayList(); + + private final List 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 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(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(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>(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("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 message = MessageBuilder.withPayload("fooBar") .setReplyChannelName("out").build();