From e1e1b33aa8f88c73e8a09783d797a748b789b606 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Tue, 6 Apr 2021 14:24:39 +0200 Subject: [PATCH 01/30] Enable chunking for messages. --- .../encoding/EncodeMessageService.java | 46 +++- .../api/enums/TechnicalMessageType.kt | 55 +++-- .../parameters/MessageHeaderParameters.kt | 16 +- .../parameters/MessageParameterTuple.kt | 7 + .../service/parameters/PayloadParameters.kt | 32 +++ agrirouter-sdk-java-impl/pom.xml | 4 + .../impl/ChunkContextIdService.java | 16 ++ .../encoding/EncodeMessageServiceImpl.java | 233 ++++++++++++------ .../EncodeMessageServiceImplTest.java | 74 ++++++ pom.xml | 8 + 10 files changed, 381 insertions(+), 110 deletions(-) create mode 100644 agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageParameterTuple.kt create mode 100644 agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/ChunkContextIdService.java diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java index f345522e..7d7365fe 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java @@ -1,18 +1,44 @@ package com.dke.data.agrirouter.api.service.messaging.encoding; +import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; import com.dke.data.agrirouter.api.service.parameters.MessageHeaderParameters; +import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; -/** Encoding of messages. */ +import java.util.List; + +/** + * Encoding of messages. + */ public interface EncodeMessageService { - /** - * Encode a given message using the internal protobuf encoding mechanism. - * - * @param messageHeaderParameters - - * @param payloadParameters - - * @return - - */ - String encode( - MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters); + /** + * Encode a given message using the internal protobuf encoding mechanism. + * + * @param messageHeaderParameters - + * @param payloadParameters - + * @return - + */ + String encode( + MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters); + + /** + * Encode a number of messages. + * + * @param messageParameterTuples - + * @return - + */ + List encode(List messageParameterTuples); + + /** + * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are not longer + * in control of the application. + * + * @param messageHeaderParameters - + * @param payloadParameters - + * @return - + */ + List chunk( + MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters, OnboardingResponse onboardingResponse); + } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt index f8a139e2..67c4e068 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt @@ -1,32 +1,33 @@ package com.dke.data.agrirouter.api.enums -enum class TechnicalMessageType(val key: String) { - EMPTY(""), - DKE_CLOUD_ONBOARD_ENDPOINTS("dke:cloud_onboard_endpoints"), - DKE_CLOUD_OFFBOARD_ENDPOINTS("dke:cloud_offboard_endpoints"), - DKE_CAPABILITIES("dke:capabilities"), - DKE_SUBSCRIPTION("dke:subscription"), - DKE_LIST_ENDPOINTS("dke:list_endpoints"), - DKE_LIST_ENDPOINTS_UNFILTERED("dke:list_endpoints_unfiltered"), - DKE_FEED_CONFIRM("dke:feed_confirm"), - DKE_FEED_DELETE("dke:feed_delete"), - DKE_FEED_MESSAGE_QUERY("dke:feed_message_query"), - DKE_FEED_HEADER_QUERY("dke:feed_header_query"), +enum class TechnicalMessageType(val key: String, val needsChunking: Boolean) { + EMPTY("",false), + DKE_CLOUD_ONBOARD_ENDPOINTS("dke:cloud_onboard_endpoints",false), + DKE_CLOUD_OFFBOARD_ENDPOINTS("dke:cloud_offboard_endpoints",false), + DKE_CAPABILITIES("dke:capabilities",false), + DKE_SUBSCRIPTION("dke:subscription",false), + DKE_LIST_ENDPOINTS("dke:list_endpoints",false), + DKE_LIST_ENDPOINTS_UNFILTERED("dke:list_endpoints_unfiltered",false), + DKE_FEED_CONFIRM("dke:feed_confirm",false), + DKE_FEED_DELETE("dke:feed_delete",false), + DKE_FEED_MESSAGE_QUERY("dke:feed_message_query",false), + DKE_FEED_HEADER_QUERY("dke:feed_header_query",false), - DKE_OTHER("dke:other"), - ISO_11783_TASKDATA_ZIP("iso:11783:-10:taskdata:zip"), - ISO_11783_DEVICE_DESCRIPTION("iso:11783:-10:device_description:protobuf"), - ISO_11783_TIME_LOG("iso:11783:-10:time_log:protobuf"), - SHP_SHAPE_ZIP("shp:shape:zip"), - DOC_PDF("doc:pdf"), - IMG_JPEG("img:jpeg"), - IMG_PNG("img:png"), - IMG_BMP("img:bmp"), - VID_AVI("vid:avi"), - VID_MP4("vid:mp4"), - VID_WMV("vid:wmv"), - GPS_INFO("gps:info"), + DKE_OTHER("dke:other",true), + ISO_11783_TASKDATA_ZIP("iso:11783:-10:taskdata:zip",true), + ISO_11783_DEVICE_DESCRIPTION("iso:11783:-10:device_description:protobuf",false), + ISO_11783_TIME_LOG("iso:11783:-10:time_log:protobuf",false), + SHP_SHAPE_ZIP("shp:shape:zip",true), + DOC_PDF("doc:pdf",true), + IMG_JPEG("img:jpeg",true), + IMG_PNG("img:png",true), + IMG_BMP("img:bmp",true), + VID_AVI("vid:avi",true), + VID_MP4("vid:mp4",true), + VID_WMV("vid:wmv",true), + GPS_INFO("gps:info",false), + + TESTING_PURPOSE_INVALID("SOME_INVALID_TYPE", true), + TEST_OTHER("test:other", true); - TESTING_PURPOSE_INVALID("SOME_INVALID_TYPE"), - TEST_OTHER ("test:other") } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageHeaderParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageHeaderParameters.kt index b5a7224d..7351366e 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageHeaderParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageHeaderParameters.kt @@ -10,7 +10,7 @@ import java.util.* /** * Parameters class. Encapsulation for the services. */ -class MessageHeaderParameters : ParameterValidation { +class MessageHeaderParameters : ParameterValidation { var applicationMessageId: String? = null @@ -34,4 +34,18 @@ class MessageHeaderParameters : ParameterValidation { nullCheck(mode) } + /** + * Copy the content of the message header parameters into this class. + */ + fun copy(messageHeaderParameters: MessageHeaderParameters) { + applicationMessageId = messageHeaderParameters.applicationMessageId + applicationMessageSeqNo = messageHeaderParameters.applicationMessageSeqNo + technicalMessageType = messageHeaderParameters.technicalMessageType + teamSetContextId = messageHeaderParameters.teamSetContextId + mode = messageHeaderParameters.mode + recipients = messageHeaderParameters.recipients + chunkInfo = messageHeaderParameters.chunkInfo + metadata = messageHeaderParameters.metadata + } + } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageParameterTuple.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageParameterTuple.kt new file mode 100644 index 00000000..e7eb0630 --- /dev/null +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageParameterTuple.kt @@ -0,0 +1,7 @@ +package com.dke.data.agrirouter.api.service.parameters + +/** + * Containing a tuple for message sending, i.e. used after chunking the messages. + */ +class MessageParameterTuple(var messageHeaderParameters: MessageHeaderParameters, var payloadParameters: PayloadParameters) { +} \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index 1701da67..f4859247 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -4,6 +4,16 @@ import com.dke.data.agrirouter.api.service.ParameterValidation import com.dke.data.agrirouter.api.service.parameters.base.AbstractParameterBase import com.google.protobuf.ByteString +/** + * Every endpoint can send message based on their capabilities. The size of a message is however limited. A message contains 2 parts: Header and Body. The limitation of a message is defined as follows: + * - Body size is equivalent of 1024000 characters/signs + * - Total message size is limited to 1468000 characters/signs + * - Message that are above this limit will be rejected. + * The AR will return an error indicated that the message size is above the limit. + * If the message size is above 5 MB the AR will not return any error. In order to send message with size large that above threshold, the message must be split in chunks with the above limit. + */ +const val MAX_LENGTH_FOR_MESSAGES = 1024000 + /** * Parameters class. Encapsulation for the services. */ @@ -18,4 +28,26 @@ class PayloadParameters : AbstractParameterBase(), ParameterValidation { nullCheck(value) } + /** + * Determining whether the message should be chunked. + */ + fun shouldBeChunked(): Boolean { + return value!!.toStringUtf8().length > MAX_LENGTH_FOR_MESSAGES + } + + /** + * The maximum length for messages / the payload. + */ + fun maxLengthForMessages(): Int { + return MAX_LENGTH_FOR_MESSAGES + } + + fun copy(payload: PayloadParameters) { + applicationMessageId = payload.applicationMessageId + teamsetContextId = payload.teamsetContextId + sequenceNumber = payload.sequenceNumber + typeUrl = payload.typeUrl + value = payload.value + } + } \ No newline at end of file diff --git a/agrirouter-sdk-java-impl/pom.xml b/agrirouter-sdk-java-impl/pom.xml index 86a1dec1..e72ac42c 100644 --- a/agrirouter-sdk-java-impl/pom.xml +++ b/agrirouter-sdk-java-impl/pom.xml @@ -60,6 +60,10 @@ org.apache.httpcomponents httpcore + + com.google.guava + guava + diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/ChunkContextIdService.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/ChunkContextIdService.java new file mode 100644 index 00000000..a8c5a907 --- /dev/null +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/ChunkContextIdService.java @@ -0,0 +1,16 @@ +package com.dke.data.agrirouter.impl; + +import java.util.UUID; + +/** Service to provide a unique chunk context id. */ +public class ChunkContextIdService { + + /** + * Generate a unique id by using the internal UUID implementation. + * + * @return - + */ + public static String generateChunkContextId() { + return UUID.randomUUID().toString(); + } +} diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index 7a571fd9..6f0089f9 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -1,96 +1,185 @@ package com.dke.data.agrirouter.impl.messaging.encoding; +import agrirouter.commons.Chunk; import agrirouter.request.Request; +import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; import com.dke.data.agrirouter.api.exception.CouldNotEncodeMessageException; import com.dke.data.agrirouter.api.service.messaging.encoding.EncodeMessageService; import com.dke.data.agrirouter.api.service.parameters.MessageHeaderParameters; +import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; import com.dke.data.agrirouter.api.util.TimestampUtil; +import com.dke.data.agrirouter.impl.ChunkContextIdService; import com.dke.data.agrirouter.impl.NonEnvironmentalService; +import com.dke.data.agrirouter.impl.common.MessageIdService; +import com.dke.data.agrirouter.impl.messaging.SequenceNumberService; +import com.google.common.base.Splitter; import com.google.protobuf.Any; +import org.apache.commons.lang3.StringUtils; + import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.Base64; -import org.apache.commons.lang3.StringUtils; - -/** Internal service implementation. */ +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * Internal service implementation. + */ +@SuppressWarnings("ConstantConditions") public class EncodeMessageServiceImpl extends NonEnvironmentalService - implements EncodeMessageService { - - public String encode( - MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters) { - this.logMethodBegin(messageHeaderParameters, payloadParameters); - - if (null == messageHeaderParameters || null == payloadParameters) { - throw new IllegalArgumentException("Parameters cannot be NULL"); + implements EncodeMessageService { + + /** + * Encode a message. In this case chunking will be done by the application and not by the SDK. + * + * @param messageHeaderParameters - + * @param payloadParameters - + * @return Single message, encoded by the SDK. + */ + public String encode( + MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters) { + logMethodBegin(messageHeaderParameters, payloadParameters); + + if (null == messageHeaderParameters || null == payloadParameters) { + throw new IllegalArgumentException("Parameters cannot be NULL"); + } + messageHeaderParameters.validate(); + payloadParameters.validate(); + + try (ByteArrayOutputStream streamedMessage = new ByteArrayOutputStream()) { + + getNativeLogger().trace("Encode header."); + header(messageHeaderParameters).writeDelimitedTo(streamedMessage); + + getNativeLogger().trace("Encode payload."); + payload(payloadParameters).writeDelimitedTo(streamedMessage); + + getNativeLogger().trace("Encoding message."); + String encodedMessage = Base64.getEncoder().encodeToString(streamedMessage.toByteArray()); + + logMethodEnd(encodedMessage); + return encodedMessage; + } catch (IOException e) { + throw new CouldNotEncodeMessageException(e); + } } - messageHeaderParameters.validate(); - payloadParameters.validate(); - - try (ByteArrayOutputStream streamedMessage = new ByteArrayOutputStream()) { - - this.getNativeLogger().trace("Encode header."); - this.header(messageHeaderParameters).writeDelimitedTo(streamedMessage); - this.getNativeLogger().trace("Encode payload."); - this.payload(payloadParameters).writeDelimitedTo(streamedMessage); - - this.getNativeLogger().trace("Encoding message."); - String encodedMessage = Base64.getEncoder().encodeToString(streamedMessage.toByteArray()); - - this.logMethodEnd(encodedMessage); - return encodedMessage; - } catch (IOException e) { - throw new CouldNotEncodeMessageException(e); - } - } - - private Request.RequestEnvelope header(MessageHeaderParameters parameters) { - this.logMethodBegin(parameters); - - this.getNativeLogger().trace("Create message header."); - agrirouter.request.Request.RequestEnvelope.Builder messageHeader = - Request.RequestEnvelope.newBuilder(); - messageHeader.setApplicationMessageId(parameters.getApplicationMessageId()); - messageHeader.setApplicationMessageSeqNo(parameters.getApplicationMessageSeqNo()); - messageHeader.setTechnicalMessageType(parameters.getTechnicalMessageType().getKey()); - messageHeader.setMode(parameters.getMode()); - if (null != parameters.getMetadata()) { - messageHeader.setMetadata(parameters.getMetadata()); - } - if (StringUtils.isNotBlank(parameters.getTeamSetContextId())) { - messageHeader.setTeamSetContextId(parameters.getTeamSetContextId()); + /** + * Encode a number of messages. + * @param messageParameterTuples - + * @return - + */ + public List encode(List messageParameterTuples){ + return messageParameterTuples.stream() + .map(messageParameterTuple -> encode(messageParameterTuple.getMessageHeaderParameters(), messageParameterTuple.getPayloadParameters())) + .collect(Collectors.toList()); } - if (!parameters.getRecipients().isEmpty()) { - messageHeader.addAllRecipients(parameters.getRecipients()); + /** + * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are not longer + * in control of the application. + * + * @param messageHeaderParameters - + * @param payloadParameters - + * @return - + */ + public List chunk( + MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters, OnboardingResponse onboardingResponse) { + logMethodBegin(messageHeaderParameters, payloadParameters); + + if (null == messageHeaderParameters || null == payloadParameters) { + throw new IllegalArgumentException("Parameters cannot be NULL"); + } + messageHeaderParameters.validate(); + payloadParameters.validate(); + + if (null != messageHeaderParameters.getTechnicalMessageType() + && messageHeaderParameters.getTechnicalMessageType().getNeedsChunking() + && payloadParameters.shouldBeChunked()) { + getNativeLogger().debug("The message should be chunked, current size of the payload ({}) is above the limitation.", payloadParameters.getValue().toStringUtf8().length()); + String wholeMessage = payloadParameters.getValue().toStringUtf8(); + final List messageChunks = Splitter.fixedLength(payloadParameters.maxLengthForMessages()).splitToList(wholeMessage); + List tuples = new ArrayList<>(); + AtomicInteger chunkNr = new AtomicInteger(1); + final String chunkContextId = ChunkContextIdService.generateChunkContextId(); + messageChunks.forEach(chunk -> { + final String messageIdForChunk = MessageIdService.generateMessageId(); + final long sequenceNumberForChunk = SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse); + + final MessageHeaderParameters header = new MessageHeaderParameters(); + header.copy(messageHeaderParameters); + header.setApplicationMessageId(messageIdForChunk); + header.setApplicationMessageSeqNo(sequenceNumberForChunk); + Chunk.ChunkComponent.Builder chunkInfo = Chunk.ChunkComponent.newBuilder(); + chunkInfo.setContextId(chunkContextId); + chunkInfo.setCurrent(chunkNr.get()); + chunkInfo.setTotal(messageChunks.size()); + chunkInfo.setTotalSize(wholeMessage.length()); + header.setChunkInfo(chunkInfo.build()); + + final PayloadParameters payload = new PayloadParameters(); + payload.copy(payload); + + tuples.add(new MessageParameterTuple(header, payload)); + + chunkNr.getAndIncrement(); + }); + return tuples; + } else { + getNativeLogger().debug("The message is not chunked since the current size of the payload ({}) is not above the limitation or the technical message type '{}' does not support chunking.", payloadParameters.getValue().toStringUtf8().length(), messageHeaderParameters.getTechnicalMessageType().getKey()); + return Collections.singletonList(new MessageParameterTuple(messageHeaderParameters, payloadParameters)); + } } - if (parameters.getChunkInfo() != null) { - messageHeader.setChunkInfo(parameters.getChunkInfo()); - } - messageHeader.setTimestamp(new TimestampUtil().current()); - - this.getNativeLogger().trace("Build message envelope."); - Request.RequestEnvelope requestEnvelope = messageHeader.build(); - this.logMethodEnd(requestEnvelope); - return requestEnvelope; - } + private Request.RequestEnvelope header(MessageHeaderParameters parameters) { + logMethodBegin(parameters); + + getNativeLogger().trace("Create message header."); + agrirouter.request.Request.RequestEnvelope.Builder messageHeader = + Request.RequestEnvelope.newBuilder(); + messageHeader.setApplicationMessageId(parameters.getApplicationMessageId()); + messageHeader.setApplicationMessageSeqNo(parameters.getApplicationMessageSeqNo()); + messageHeader.setTechnicalMessageType(parameters.getTechnicalMessageType().getKey()); + messageHeader.setMode(parameters.getMode()); + if (null != parameters.getMetadata()) { + messageHeader.setMetadata(parameters.getMetadata()); + } + if (StringUtils.isNotBlank(parameters.getTeamSetContextId())) { + messageHeader.setTeamSetContextId(parameters.getTeamSetContextId()); + } + if (!parameters.getRecipients().isEmpty()) { + messageHeader.addAllRecipients(parameters.getRecipients()); + } + if (parameters.getChunkInfo() != null) { + messageHeader.setChunkInfo(parameters.getChunkInfo()); + } + messageHeader.setTimestamp(new TimestampUtil().current()); + + getNativeLogger().trace("Build message envelope."); + Request.RequestEnvelope requestEnvelope = messageHeader.build(); + + logMethodEnd(requestEnvelope); + return requestEnvelope; + } - private Request.RequestPayloadWrapper payload(PayloadParameters parameters) { - this.logMethodBegin(parameters); + private Request.RequestPayloadWrapper payload(PayloadParameters parameters) { + logMethodBegin(parameters); - this.getNativeLogger().trace("Create message payload."); - Request.RequestPayloadWrapper.Builder messagePayload = - Request.RequestPayloadWrapper.newBuilder(); - Any.Builder builder = Any.newBuilder(); - builder.setTypeUrl(parameters.getTypeUrl()); - builder.setValue(parameters.getValue()); - messagePayload.setDetails(builder.build()); + getNativeLogger().trace("Create message payload."); + Request.RequestPayloadWrapper.Builder messagePayload = + Request.RequestPayloadWrapper.newBuilder(); + Any.Builder builder = Any.newBuilder(); + builder.setTypeUrl(parameters.getTypeUrl()); + builder.setValue(parameters.getValue()); + messagePayload.setDetails(builder.build()); - this.getNativeLogger().trace("Message message payload wrapper."); - Request.RequestPayloadWrapper requestPayloadWrapper = messagePayload.build(); + getNativeLogger().trace("Message message payload wrapper."); + Request.RequestPayloadWrapper requestPayloadWrapper = messagePayload.build(); - this.logMethodEnd(requestPayloadWrapper); - return requestPayloadWrapper; - } + logMethodEnd(requestPayloadWrapper); + return requestPayloadWrapper; + } } diff --git a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java index 63b8472a..ee602346 100644 --- a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java +++ b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java @@ -1,20 +1,87 @@ package com.dke.data.agrirouter.impl.messaging.encoding; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import agrirouter.request.Request; import agrirouter.request.payload.endpoint.Capabilities; import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; +import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; import com.dke.data.agrirouter.api.enums.TechnicalMessageType; import com.dke.data.agrirouter.api.service.messaging.encoding.EncodeMessageService; import com.dke.data.agrirouter.api.service.parameters.MessageHeaderParameters; +import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; import com.google.protobuf.ByteString; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.List; + class EncodeMessageServiceImplTest { + @Test + void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks(){ + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(""); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1,chunks.size()); + } + + @Test + void givenSingleChunkMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks(){ + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1,chunks.size()); + } + + @Test + void givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks(){ + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000)); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1,chunks.size()); + } + + @Test + @SuppressWarnings("ConstantConditions") + void givenMultipleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks(){ + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024001)); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(2,chunks.size()); + + Assertions.assertEquals(2,chunks.get(0).getMessageHeaderParameters().getChunkInfo().getTotal()); + Assertions.assertEquals(2,chunks.get(1).getMessageHeaderParameters().getChunkInfo().getTotal()); + + Assertions.assertEquals(1,chunks.get(0).getMessageHeaderParameters().getChunkInfo().getCurrent()); + Assertions.assertEquals(2,chunks.get(1).getMessageHeaderParameters().getChunkInfo().getCurrent()); + + Assertions.assertEquals(chunks.get(0).getMessageHeaderParameters().getChunkInfo().getContextId(), chunks.get(1).getMessageHeaderParameters().getChunkInfo().getContextId()); + } + @Test void givenValidParametersEncodeAndDecodeBackShouldNotFail() { EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); @@ -83,4 +150,11 @@ private PayloadParameters getPayloadParameters(ByteString toSendMessage) { payloadParameters.setValue(toSendMessage); return payloadParameters; } + + private OnboardingResponse fakeOnboardingResponse() { + OnboardingResponse onboardingResponse = new OnboardingResponse(); + onboardingResponse.setSensorAlternateId("THIS_IS_FAKE"); + return onboardingResponse; + } + } diff --git a/pom.xml b/pom.xml index 05ea02bf..585b5be5 100644 --- a/pom.xml +++ b/pom.xml @@ -100,6 +100,7 @@ 3.0.2 3.1.0 2.9 + 30.1.1-jre @@ -268,6 +269,13 @@ ${javax.ws.rs-api.version} + + + com.google.guava + guava + ${guava.version} + + From de162a81306812ee3567e38c1a9c9abe9e2774f7 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Tue, 6 Apr 2021 16:20:10 +0200 Subject: [PATCH 02/30] Add testcase for sending chunks. --- .../api/service/ParameterValidation.java | 139 +++++++++--------- .../AuthorizationRequestParameters.kt | 2 +- .../parameters/CloudOffboardingParameters.kt | 6 +- .../parameters/CloudOnboardingParameters.kt | 10 +- .../parameters/DeleteMessageParameters.kt | 15 +- .../parameters/FetchMessageParameters.kt | 2 +- .../parameters/ListEndpointsParameters.kt | 6 +- ...irmationForAllPendingMessagesParameters.kt | 2 +- .../MessageConfirmationParameters.kt | 4 +- .../parameters/MessageHeaderParameters.kt | 6 +- .../parameters/MessageQueryParameters.kt | 15 +- .../parameters/OnboardingParameters.kt | 14 +- .../service/parameters/PayloadParameters.kt | 4 +- .../RegistrationCodeRequestParameters.kt | 2 +- .../service/parameters/RevokeParameters.kt | 12 +- .../parameters/SecuredOnboardingParameters.kt | 16 +- .../parameters/SendMessageParameters.kt | 6 +- .../parameters/SetCapabilitiesParameters.kt | 12 +- .../parameters/SetSubscriptionParameters.kt | 4 +- .../encoding/EncodeMessageServiceImpl.java | 6 +- .../rest/SendChunkedMessageTest.java | 113 ++++++++++++++ 21 files changed, 266 insertions(+), 130 deletions(-) create mode 100644 agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java index 541f5450..402d0a93 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java @@ -1,89 +1,90 @@ package com.dke.data.agrirouter.api.service; import com.dke.data.agrirouter.api.exception.IllegalParameterDefinitionException; -import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** Parameter validation using bean validation. */ -public interface ParameterValidation { +import java.util.Collection; - Logger LOGGER = LogManager.getLogger(); +/** + * Parameter validation using bean validation. + */ +public interface ParameterValidation { - /** - * Validation of the parameters. If there are any constraint violations, there will be a - * exception. - * - * @throws IllegalParameterDefinitionException - - */ - default void validate() { - LOGGER.debug("Validating parameters."); - LOGGER.trace("Technical validation."); - this.technicalValidation(); - LOGGER.trace("Business validation."); - this.businessValidation(); - } + Logger LOGGER = LogManager.getLogger(); - /** Technical validation. Empty by default. */ - default void technicalValidation() { - // - } + /** + * Validation of the parameters. If there are any constraint violations, there will be a + * exception. + * + * @throws IllegalParameterDefinitionException - + */ + default void validate() { + LOGGER.debug("Validating parameters."); + LOGGER.trace("Technical validation."); + this.technicalValidation(); + LOGGER.trace("Business validation."); + this.businessValidation(); + } - /** Business validation. Empty by default. */ - default void businessValidation() { - // - } + /** + * Technical validation. Empty by default. + */ + default void technicalValidation() { + // + } - /** Rise an exception if the parameter was not valid. */ - default void rise() { - throw new IllegalParameterDefinitionException( - "Parameter was not defined correctly, please check the values."); - } + /** + * Business validation. Empty by default. + */ + default void businessValidation() { + // + } - /** - * Rise an exception if the parameter was not valid. - * - * @param message - - */ - default void rise(String message) { - throw new IllegalParameterDefinitionException( - String.format( - "Parameter was not defined correctly, please check the values. Error message is '%s'.", - message)); - } + /** + * Rise an exception if the parameter was not valid. + * + * @param message - + */ + default void rise(String message, String parameterName) { + throw new IllegalParameterDefinitionException( + String.format( + "Parameter '%s' was not defined correctly, please check the values. Error message is '%s'.", + parameterName, message)); + } - /** - * Check for null values. - * - * @param o - - */ - default void nullCheck(Object o) { - if (null == o) { - this.rise("The parameter should not have been null, please check your values."); + /** + * Check for null values. + * + * @param o - + */ + default void nullCheck(String parameterName, Object o) { + if (null == o) { + this.rise("The parameter '%s' should not have been null, please check your values.", parameterName); + } } - } - /** - * Check for null or empty values. - * - * @param s - - */ - default void isBlank(String s) { - if (StringUtils.isBlank(s)) { - this.rise("The parameter should not have been blank, please check your values."); + /** + * Check for null or empty values. + * + * @param s - + */ + default void isBlank(String parameterName, String s) { + if (StringUtils.isBlank(s)) { + this.rise("The parameter '%s' should not have been blank, please check your values.", parameterName); + } } - } - /** - * Check for null or empty values. - * - * @param c - - */ - default void nullOrEmpty(Collection c) { - nullCheck(c); - if (c.isEmpty()) { - this.rise("The parameter should not have been empty, please check your values."); + /** + * Check for null or empty values. + * + * @param c - + */ + default void nullOrEmpty(String parameterName, Collection c) { + nullCheck(parameterName, c); + if (c.isEmpty()) { + this.rise("The parameter '%s' should not have been empty, please check your values.", parameterName); + } } - } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/AuthorizationRequestParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/AuthorizationRequestParameters.kt index 044cca69..6e89233a 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/AuthorizationRequestParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/AuthorizationRequestParameters.kt @@ -18,7 +18,7 @@ class AuthorizationRequestParameters : AbstractParameterBase(), ParameterValidat var responseType: SecuredOnboardingResponseType = SecuredOnboardingResponseType.ONBOARD override fun technicalValidation() { - isBlank(applicationId) + isBlank("applicationId",applicationId) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/CloudOffboardingParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/CloudOffboardingParameters.kt index 044a5c39..250afc43 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/CloudOffboardingParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/CloudOffboardingParameters.kt @@ -14,12 +14,12 @@ class CloudOffboardingParameters : AbstractParameterBase(), ParameterValidation var endpointIds: List? = null override fun technicalValidation() { - nullCheck(onboardingResponse) - nullCheck(endpointIds) + nullCheck("onboardingResponse",onboardingResponse) + nullCheck("endpointIds",endpointIds) } override fun businessValidation() { - nullOrEmpty(endpointIds) + nullOrEmpty("endpointIds",endpointIds) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/CloudOnboardingParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/CloudOnboardingParameters.kt index f2e69cdf..6be3305a 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/CloudOnboardingParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/CloudOnboardingParameters.kt @@ -14,12 +14,12 @@ class CloudOnboardingParameters : AbstractParameterBase(), ParameterValidation { var endpointDetails: List? = null override fun technicalValidation() { - nullCheck(onboardingResponse) - nullCheck(endpointDetails) + nullCheck("onboardingResponse",onboardingResponse) + nullCheck("endpointDetails",endpointDetails) } override fun businessValidation() { - nullOrEmpty(endpointDetails) + nullOrEmpty("endpointDetails",endpointDetails) endpointDetails?.forEach { it.validate() } @@ -32,8 +32,8 @@ class CloudOnboardingParameters : AbstractParameterBase(), ParameterValidation { var endpointName: String? = null override fun technicalValidation() { - isBlank(endpointId) - isBlank(endpointName) + isBlank("endpointId",endpointId) + isBlank("endpointName",endpointName) } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/DeleteMessageParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/DeleteMessageParameters.kt index 404f907e..255b2e9b 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/DeleteMessageParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/DeleteMessageParameters.kt @@ -20,12 +20,21 @@ class DeleteMessageParameters : AbstractParameterBase(), ParameterValidation { var sentToInSeconds: Long? = null override fun technicalValidation() { - nullCheck(onboardingResponse) + nullCheck("onboardingResponse", onboardingResponse) } override fun businessValidation() { - if (null == messageIds && null == senderIds && null == sentFromInSeconds && null == sentToInSeconds) { - rise("There has to be a filter criteria for the query.") + if (null == messageIds) { + rise("messageIds", "There has to be a filter criteria for the query.") + } + if (null == senderIds) { + rise("senderIds", "There has to be a filter criteria for the query.") + } + if (null == sentFromInSeconds) { + rise("sentFromInSeconds", "There has to be a filter criteria for the query.") + } + if (null == sentToInSeconds) { + rise("sentToInSeconds", "There has to be a filter criteria for the query.") } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/FetchMessageParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/FetchMessageParameters.kt index 01959a80..911d61a4 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/FetchMessageParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/FetchMessageParameters.kt @@ -12,7 +12,7 @@ open class FetchMessageParameters : AbstractParameterBase(), ParameterValidation var onboardingResponse: OnboardingResponse? = null override fun technicalValidation() { - nullCheck(onboardingResponse) + nullCheck("onboardingResponse",onboardingResponse) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/ListEndpointsParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/ListEndpointsParameters.kt index 6e4117b3..a6731d23 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/ListEndpointsParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/ListEndpointsParameters.kt @@ -20,9 +20,9 @@ class ListEndpointsParameters : AbstractParameterBase(), ParameterValidation { var unfilteredList: Boolean = false override fun technicalValidation() { - nullCheck(onboardingResponse) - nullCheck(technicalMessageType) - nullCheck(direction) + nullCheck("onboardingResponse",onboardingResponse) + nullCheck("technicalMessageType",technicalMessageType) + nullCheck("direction",direction) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageConfirmationForAllPendingMessagesParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageConfirmationForAllPendingMessagesParameters.kt index b02d62dc..37c82538 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageConfirmationForAllPendingMessagesParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageConfirmationForAllPendingMessagesParameters.kt @@ -12,7 +12,7 @@ class MessageConfirmationForAllPendingMessagesParameters : AbstractParameterBase var onboardingResponse: OnboardingResponse? = null override fun technicalValidation() { - nullCheck(onboardingResponse) + nullCheck("onboardingResponse",onboardingResponse) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageConfirmationParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageConfirmationParameters.kt index d3b92503..88502407 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageConfirmationParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageConfirmationParameters.kt @@ -14,8 +14,8 @@ class MessageConfirmationParameters : AbstractParameterBase(), ParameterValidati var messageIds: List? = null override fun technicalValidation() { - nullCheck(onboardingResponse) - nullCheck(messageIds) + nullCheck("onboardingResponse",onboardingResponse) + nullCheck("messageIds",messageIds) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageHeaderParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageHeaderParameters.kt index 7351366e..47cd46bb 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageHeaderParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageHeaderParameters.kt @@ -29,9 +29,9 @@ class MessageHeaderParameters : ParameterValidation { var metadata: MessageOuterClass.Metadata? = null override fun technicalValidation() { - isBlank(applicationMessageId) - nullCheck(technicalMessageType) - nullCheck(mode) + isBlank("applicationMessageId",applicationMessageId) + nullCheck("technicalMessageType",technicalMessageType) + nullCheck("mode",mode) } /** diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageQueryParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageQueryParameters.kt index ada86bc6..eed06957 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageQueryParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageQueryParameters.kt @@ -20,12 +20,21 @@ class MessageQueryParameters : AbstractParameterBase(), ParameterValidation { var sentToInSeconds: Long? = null override fun technicalValidation() { - nullCheck(onboardingResponse) + nullCheck("onboardingResponse", onboardingResponse) } override fun businessValidation() { - if (null == messageIds && null == senderIds && null == sentFromInSeconds && null == sentToInSeconds) { - this.rise("There has to be a filter criteria for the query.") + if (null == messageIds) { + rise("messageIds", "There has to be a filter criteria for the query.") + } + if (null == senderIds) { + rise("senderIds", "There has to be a filter criteria for the query.") + } + if (null == sentFromInSeconds) { + rise("sentFromInSeconds", "There has to be a filter criteria for the query.") + } + if (null == sentToInSeconds) { + rise("sentToInSeconds", "There has to be a filter criteria for the query.") } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/OnboardingParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/OnboardingParameters.kt index 1754c18f..58aa8a04 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/OnboardingParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/OnboardingParameters.kt @@ -25,12 +25,12 @@ class OnboardingParameters : AbstractParameterBase(), ParameterValidation { var registrationCode: String? = null override fun technicalValidation() { - isBlank(applicationId) - isBlank(uuid) - isBlank(certificationVersionId) - isBlank(gatewayId) - nullCheck(certificationType) - nullCheck(applicationType) - isBlank(registrationCode) + isBlank("applicationId",applicationId) + isBlank("uuid",uuid) + isBlank("certificationVersionId",certificationVersionId) + isBlank("gatewayId",gatewayId) + nullCheck("certificationType",certificationType) + nullCheck("applicationType",applicationType) + isBlank("registrationCode",registrationCode) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index f4859247..0c2521ef 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -24,8 +24,8 @@ class PayloadParameters : AbstractParameterBase(), ParameterValidation { var value: ByteString? = null override fun technicalValidation() { - nullCheck(typeUrl) - nullCheck(value) + nullCheck("typeUrl",typeUrl) + nullCheck("value",value) } /** diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/RegistrationCodeRequestParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/RegistrationCodeRequestParameters.kt index 88aa2e9e..e6b45b7c 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/RegistrationCodeRequestParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/RegistrationCodeRequestParameters.kt @@ -11,7 +11,7 @@ class RegistrationCodeRequestParameters : AbstractParameterBase(), ParameterVali lateinit var applicationId: String override fun technicalValidation() { - isBlank(applicationId) + isBlank("applicationId",applicationId) } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/RevokeParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/RevokeParameters.kt index c3565f4c..468e8e62 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/RevokeParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/RevokeParameters.kt @@ -18,14 +18,14 @@ class RevokeParameters : ParameterValidation { var publicKey: String? = null override fun technicalValidation() { - isBlank(applicationId) - isBlank(accountId) - isBlank(privateKey) - isBlank(publicKey) - nullCheck(endpointIds) + isBlank("applicationId",applicationId) + isBlank("accountId",accountId) + isBlank("privateKey",privateKey) + isBlank("publicKey",publicKey) + nullCheck("endpointIds",endpointIds) } override fun businessValidation() { - nullOrEmpty(endpointIds) + nullOrEmpty("endpointIds",endpointIds) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SecuredOnboardingParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SecuredOnboardingParameters.kt index a39a4c01..1c711345 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SecuredOnboardingParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SecuredOnboardingParameters.kt @@ -26,14 +26,14 @@ class SecuredOnboardingParameters : AbstractParameterBase(), ParameterValidation var registrationCode: String? = null override fun technicalValidation() { - isBlank(applicationId) - isBlank(privateKey) - isBlank(publicKey) - isBlank(uuid) - isBlank(certificationVersionId) - isBlank(gatewayId) - isBlank(registrationCode) - nullCheck(certificationType) + isBlank("applicationId",applicationId) + isBlank("privateKey",privateKey) + isBlank("publicKey",publicKey) + isBlank("uuid",uuid) + isBlank("certificationVersionId",certificationVersionId) + isBlank("gatewayId",gatewayId) + isBlank("registrationCode",registrationCode) + nullCheck("certificationType",certificationType) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SendMessageParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SendMessageParameters.kt index 4e1d740e..4d4deeaa 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SendMessageParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SendMessageParameters.kt @@ -14,12 +14,12 @@ open class SendMessageParameters : AbstractParameterBase(), ParameterValidation var encodedMessages: List? = null override fun technicalValidation() { - nullCheck(onboardingResponse) - nullCheck(encodedMessages) + nullCheck("onboardingResponse",onboardingResponse) + nullCheck("encodedMessages",encodedMessages) } override fun businessValidation() { - nullOrEmpty(encodedMessages) + nullOrEmpty("encodedMessages",encodedMessages) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SetCapabilitiesParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SetCapabilitiesParameters.kt index 39827579..d7a7e478 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SetCapabilitiesParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SetCapabilitiesParameters.kt @@ -28,17 +28,17 @@ class SetCapabilitiesParameters : AbstractParameterBase(), ParameterValidation { var direction: Capabilities.CapabilitySpecification.Direction? = null override fun technicalValidation() { - nullCheck(technicalMessageType) - nullCheck(direction) + nullCheck("technicalMessageType",technicalMessageType) + nullCheck("direction",direction) } } override fun technicalValidation() { - nullCheck(onboardingResponse) - isBlank(applicationId) - isBlank(certificationVersionId) - nullCheck(capabilitiesParameters) + nullCheck("onboardingResponse",onboardingResponse) + isBlank("applicationId",applicationId) + isBlank("certificationVersionId",certificationVersionId) + nullCheck("capabilitiesParameters",capabilitiesParameters) capabilitiesParameters?.forEach { c -> c.validate() } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SetSubscriptionParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SetSubscriptionParameters.kt index 2d973a41..6663da44 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SetSubscriptionParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/SetSubscriptionParameters.kt @@ -21,14 +21,14 @@ class SetSubscriptionParameters : AbstractParameterBase(), ParameterValidation { var position: Boolean = false override fun technicalValidation() { - nullCheck(technicalMessageType) + nullCheck("technicalMessageType",technicalMessageType) } } var subscriptions: List = ArrayList() override fun technicalValidation() { - nullCheck(onboardingResponse) + nullCheck("onboardingResponse",onboardingResponse) } } \ No newline at end of file diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index 6f0089f9..8d601145 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -15,6 +15,7 @@ import com.dke.data.agrirouter.impl.messaging.SequenceNumberService; import com.google.common.base.Splitter; import com.google.protobuf.Any; +import com.google.protobuf.ByteString; import org.apache.commons.lang3.StringUtils; import java.io.ByteArrayOutputStream; @@ -70,14 +71,16 @@ public String encode( /** * Encode a number of messages. + * * @param messageParameterTuples - * @return - */ - public List encode(List messageParameterTuples){ + public List encode(List messageParameterTuples) { return messageParameterTuples.stream() .map(messageParameterTuple -> encode(messageParameterTuple.getMessageHeaderParameters(), messageParameterTuple.getPayloadParameters())) .collect(Collectors.toList()); } + /** * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are not longer * in control of the application. @@ -122,6 +125,7 @@ public List chunk( final PayloadParameters payload = new PayloadParameters(); payload.copy(payload); + payload.setValue(ByteString.copyFromUtf8(chunk)); tuples.add(new MessageParameterTuple(header, payload)); diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java new file mode 100644 index 00000000..483cfbd4 --- /dev/null +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -0,0 +1,113 @@ +package com.dke.data.agrirouter.test.messaging.rest; + +import agrirouter.request.Request; +import com.dke.data.agrirouter.api.cancellation.DefaultCancellationToken; +import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; +import com.dke.data.agrirouter.api.dto.messaging.FetchMessageResponse; +import com.dke.data.agrirouter.api.dto.messaging.inner.Message; +import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; +import com.dke.data.agrirouter.api.enums.TechnicalMessageType; +import com.dke.data.agrirouter.api.service.messaging.encoding.DecodeMessageService; +import com.dke.data.agrirouter.api.service.messaging.encoding.EncodeMessageService; +import com.dke.data.agrirouter.api.service.messaging.http.FetchMessageService; +import com.dke.data.agrirouter.api.service.parameters.MessageHeaderParameters; +import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; +import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; +import com.dke.data.agrirouter.api.service.parameters.SendMessageParameters; +import com.dke.data.agrirouter.impl.common.MessageIdService; +import com.dke.data.agrirouter.impl.messaging.SequenceNumberService; +import com.dke.data.agrirouter.impl.messaging.encoding.DecodeMessageServiceImpl; +import com.dke.data.agrirouter.impl.messaging.encoding.EncodeMessageServiceImpl; +import com.dke.data.agrirouter.impl.messaging.rest.FetchMessageServiceImpl; +import com.dke.data.agrirouter.impl.messaging.rest.SendMessageServiceImpl; +import com.dke.data.agrirouter.test.AbstractIntegrationTest; +import com.dke.data.agrirouter.test.Assertions; +import com.dke.data.agrirouter.test.OnboardingResponseRepository; +import com.google.protobuf.ByteString; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; + +/** + * Test case to show the behavior for chunked message sending. + */ +class SendChunkedMessageTest extends AbstractIntegrationTest { + + @Test + void givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks() throws IOException, InterruptedException { + + final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + final SendMessageServiceImpl sendMessageService = new SendMessageServiceImpl(); + final OnboardingResponse onboardingResponse = OnboardingResponseRepository.read(OnboardingResponseRepository.Identifier.FARMING_SOFTWARE); + + + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); + messageHeaderParameters.setApplicationMessageId(MessageIdService.generateMessageId()); + messageHeaderParameters.setApplicationMessageSeqNo(SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); + messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); + + PayloadParameters payloadParameters = new PayloadParameters(); + payloadParameters.setValue(fakeLargeMessageContent()); + payloadParameters.setTypeUrl(TechnicalMessageType.EMPTY.getKey()); + + List tuples = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); + final List messageIdsWaitingForAck = tuples.stream() + .map(messageParameterTuple -> messageParameterTuple.getMessageHeaderParameters().getApplicationMessageId()) + .collect(Collectors.toList()); + + List encodedMessages = encodeMessageService.encode(tuples); + + SendMessageParameters sendMessageParameters = new SendMessageParameters(); + sendMessageParameters.setEncodedMessages(encodedMessages); + sendMessageParameters.setOnboardingResponse(onboardingResponse); + sendMessageService.send(sendMessageParameters); + + Thread.sleep(TimeUnit.SECONDS.toMillis(3)); + + FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); + Optional> fetchMessageResponses = + fetchMessageService.fetch( + onboardingResponse, + new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); + + Assertions.assertTrue(fetchMessageResponses.isPresent()); + Assertions.assertEquals(3, fetchMessageResponses.get().size()); + Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); + Assertions.assertNotNull(fetchMessageResponses.get().get(1).getCommand()); + Assertions.assertNotNull(fetchMessageResponses.get().get(2).getCommand()); + + Message firstAck = fetchMessageResponses.get().get(0).getCommand(); + Message secondAck = fetchMessageResponses.get().get(1).getCommand(); + Message thirdAck = fetchMessageResponses.get().get(2).getCommand(); + + Arrays.stream(new Message[]{firstAck,secondAck,thirdAck}).forEach(message -> { + DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); + DecodeMessageResponse decodeMessageResponse = decodeMessageService.decode(message.getMessage()); + + Assertions.assertMatchesAny( + Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT), + decodeMessageResponse.getResponseEnvelope().getResponseCode()); + }); + } + + /** + * Delivers fake message content for three chunks. + * + * @return - + */ + private ByteString fakeLargeMessageContent() { + return ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000 * 3)); + } + +} From d2c876ed64c3aa08240a2204ecdfbc52bb15083d Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Tue, 6 Apr 2021 16:20:26 +0200 Subject: [PATCH 03/30] Add testcase for sending chunks. --- .../agrirouter/test/messaging/rest/SendChunkedMessageTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index 483cfbd4..dec3f990 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -62,9 +62,6 @@ void givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHel payloadParameters.setTypeUrl(TechnicalMessageType.EMPTY.getKey()); List tuples = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); - final List messageIdsWaitingForAck = tuples.stream() - .map(messageParameterTuple -> messageParameterTuple.getMessageHeaderParameters().getApplicationMessageId()) - .collect(Collectors.toList()); List encodedMessages = encodeMessageService.encode(tuples); From b1b2c1e7651c00d190316e35704efc3e15b64d99 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Tue, 6 Apr 2021 16:26:06 +0200 Subject: [PATCH 04/30] Format. --- .../api/service/ParameterValidation.java | 138 ++++---- .../encoding/EncodeMessageService.java | 60 ++-- .../encoding/EncodeMessageServiceImpl.java | 314 +++++++++--------- .../EncodeMessageServiceImplTest.java | 56 ++-- .../rest/SendChunkedMessageTest.java | 155 ++++----- 5 files changed, 376 insertions(+), 347 deletions(-) diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java index 402d0a93..ee884976 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java @@ -1,90 +1,88 @@ package com.dke.data.agrirouter.api.service; import com.dke.data.agrirouter.api.exception.IllegalParameterDefinitionException; +import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Collection; - -/** - * Parameter validation using bean validation. - */ +/** Parameter validation using bean validation. */ public interface ParameterValidation { - Logger LOGGER = LogManager.getLogger(); + Logger LOGGER = LogManager.getLogger(); - /** - * Validation of the parameters. If there are any constraint violations, there will be a - * exception. - * - * @throws IllegalParameterDefinitionException - - */ - default void validate() { - LOGGER.debug("Validating parameters."); - LOGGER.trace("Technical validation."); - this.technicalValidation(); - LOGGER.trace("Business validation."); - this.businessValidation(); - } + /** + * Validation of the parameters. If there are any constraint violations, there will be a + * exception. + * + * @throws IllegalParameterDefinitionException - + */ + default void validate() { + LOGGER.debug("Validating parameters."); + LOGGER.trace("Technical validation."); + this.technicalValidation(); + LOGGER.trace("Business validation."); + this.businessValidation(); + } - /** - * Technical validation. Empty by default. - */ - default void technicalValidation() { - // - } + /** Technical validation. Empty by default. */ + default void technicalValidation() { + // + } - /** - * Business validation. Empty by default. - */ - default void businessValidation() { - // - } + /** Business validation. Empty by default. */ + default void businessValidation() { + // + } - /** - * Rise an exception if the parameter was not valid. - * - * @param message - - */ - default void rise(String message, String parameterName) { - throw new IllegalParameterDefinitionException( - String.format( - "Parameter '%s' was not defined correctly, please check the values. Error message is '%s'.", - parameterName, message)); - } + /** + * Rise an exception if the parameter was not valid. + * + * @param message - + */ + default void rise(String message, String parameterName) { + throw new IllegalParameterDefinitionException( + String.format( + "Parameter '%s' was not defined correctly, please check the values. Error message is '%s'.", + parameterName, message)); + } - /** - * Check for null values. - * - * @param o - - */ - default void nullCheck(String parameterName, Object o) { - if (null == o) { - this.rise("The parameter '%s' should not have been null, please check your values.", parameterName); - } + /** + * Check for null values. + * + * @param o - + */ + default void nullCheck(String parameterName, Object o) { + if (null == o) { + this.rise( + "The parameter '%s' should not have been null, please check your values.", parameterName); } + } - /** - * Check for null or empty values. - * - * @param s - - */ - default void isBlank(String parameterName, String s) { - if (StringUtils.isBlank(s)) { - this.rise("The parameter '%s' should not have been blank, please check your values.", parameterName); - } + /** + * Check for null or empty values. + * + * @param s - + */ + default void isBlank(String parameterName, String s) { + if (StringUtils.isBlank(s)) { + this.rise( + "The parameter '%s' should not have been blank, please check your values.", + parameterName); } + } - /** - * Check for null or empty values. - * - * @param c - - */ - default void nullOrEmpty(String parameterName, Collection c) { - nullCheck(parameterName, c); - if (c.isEmpty()) { - this.rise("The parameter '%s' should not have been empty, please check your values.", parameterName); - } + /** + * Check for null or empty values. + * + * @param c - + */ + default void nullOrEmpty(String parameterName, Collection c) { + nullCheck(parameterName, c); + if (c.isEmpty()) { + this.rise( + "The parameter '%s' should not have been empty, please check your values.", + parameterName); } + } } diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java index 7d7365fe..b0f2b469 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java @@ -4,41 +4,39 @@ import com.dke.data.agrirouter.api.service.parameters.MessageHeaderParameters; import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; - import java.util.List; -/** - * Encoding of messages. - */ +/** Encoding of messages. */ public interface EncodeMessageService { - /** - * Encode a given message using the internal protobuf encoding mechanism. - * - * @param messageHeaderParameters - - * @param payloadParameters - - * @return - - */ - String encode( - MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters); - - /** - * Encode a number of messages. - * - * @param messageParameterTuples - - * @return - - */ - List encode(List messageParameterTuples); + /** + * Encode a given message using the internal protobuf encoding mechanism. + * + * @param messageHeaderParameters - + * @param payloadParameters - + * @return - + */ + String encode( + MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters); - /** - * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are not longer - * in control of the application. - * - * @param messageHeaderParameters - - * @param payloadParameters - - * @return - - */ - List chunk( - MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters, OnboardingResponse onboardingResponse); + /** + * Encode a number of messages. + * + * @param messageParameterTuples - + * @return - + */ + List encode(List messageParameterTuples); + /** + * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are + * not longer in control of the application. + * + * @param messageHeaderParameters - + * @param payloadParameters - + * @return - + */ + List chunk( + MessageHeaderParameters messageHeaderParameters, + PayloadParameters payloadParameters, + OnboardingResponse onboardingResponse); } diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index 8d601145..20c06514 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -16,8 +16,6 @@ import com.google.common.base.Splitter; import com.google.protobuf.Any; import com.google.protobuf.ByteString; -import org.apache.commons.lang3.StringUtils; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -26,164 +24,180 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; -/** - * Internal service implementation. - */ +/** Internal service implementation. */ @SuppressWarnings("ConstantConditions") public class EncodeMessageServiceImpl extends NonEnvironmentalService - implements EncodeMessageService { - - /** - * Encode a message. In this case chunking will be done by the application and not by the SDK. - * - * @param messageHeaderParameters - - * @param payloadParameters - - * @return Single message, encoded by the SDK. - */ - public String encode( - MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters) { - logMethodBegin(messageHeaderParameters, payloadParameters); - - if (null == messageHeaderParameters || null == payloadParameters) { - throw new IllegalArgumentException("Parameters cannot be NULL"); - } - messageHeaderParameters.validate(); - payloadParameters.validate(); - - try (ByteArrayOutputStream streamedMessage = new ByteArrayOutputStream()) { - - getNativeLogger().trace("Encode header."); - header(messageHeaderParameters).writeDelimitedTo(streamedMessage); - - getNativeLogger().trace("Encode payload."); - payload(payloadParameters).writeDelimitedTo(streamedMessage); - - getNativeLogger().trace("Encoding message."); - String encodedMessage = Base64.getEncoder().encodeToString(streamedMessage.toByteArray()); - - logMethodEnd(encodedMessage); - return encodedMessage; - } catch (IOException e) { - throw new CouldNotEncodeMessageException(e); - } + implements EncodeMessageService { + + /** + * Encode a message. In this case chunking will be done by the application and not by the SDK. + * + * @param messageHeaderParameters - + * @param payloadParameters - + * @return Single message, encoded by the SDK. + */ + public String encode( + MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters) { + logMethodBegin(messageHeaderParameters, payloadParameters); + + if (null == messageHeaderParameters || null == payloadParameters) { + throw new IllegalArgumentException("Parameters cannot be NULL"); } + messageHeaderParameters.validate(); + payloadParameters.validate(); - /** - * Encode a number of messages. - * - * @param messageParameterTuples - - * @return - - */ - public List encode(List messageParameterTuples) { - return messageParameterTuples.stream() - .map(messageParameterTuple -> encode(messageParameterTuple.getMessageHeaderParameters(), messageParameterTuple.getPayloadParameters())) - .collect(Collectors.toList()); - } + try (ByteArrayOutputStream streamedMessage = new ByteArrayOutputStream()) { - /** - * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are not longer - * in control of the application. - * - * @param messageHeaderParameters - - * @param payloadParameters - - * @return - - */ - public List chunk( - MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters, OnboardingResponse onboardingResponse) { - logMethodBegin(messageHeaderParameters, payloadParameters); - - if (null == messageHeaderParameters || null == payloadParameters) { - throw new IllegalArgumentException("Parameters cannot be NULL"); - } - messageHeaderParameters.validate(); - payloadParameters.validate(); - - if (null != messageHeaderParameters.getTechnicalMessageType() - && messageHeaderParameters.getTechnicalMessageType().getNeedsChunking() - && payloadParameters.shouldBeChunked()) { - getNativeLogger().debug("The message should be chunked, current size of the payload ({}) is above the limitation.", payloadParameters.getValue().toStringUtf8().length()); - String wholeMessage = payloadParameters.getValue().toStringUtf8(); - final List messageChunks = Splitter.fixedLength(payloadParameters.maxLengthForMessages()).splitToList(wholeMessage); - List tuples = new ArrayList<>(); - AtomicInteger chunkNr = new AtomicInteger(1); - final String chunkContextId = ChunkContextIdService.generateChunkContextId(); - messageChunks.forEach(chunk -> { - final String messageIdForChunk = MessageIdService.generateMessageId(); - final long sequenceNumberForChunk = SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse); - - final MessageHeaderParameters header = new MessageHeaderParameters(); - header.copy(messageHeaderParameters); - header.setApplicationMessageId(messageIdForChunk); - header.setApplicationMessageSeqNo(sequenceNumberForChunk); - Chunk.ChunkComponent.Builder chunkInfo = Chunk.ChunkComponent.newBuilder(); - chunkInfo.setContextId(chunkContextId); - chunkInfo.setCurrent(chunkNr.get()); - chunkInfo.setTotal(messageChunks.size()); - chunkInfo.setTotalSize(wholeMessage.length()); - header.setChunkInfo(chunkInfo.build()); - - final PayloadParameters payload = new PayloadParameters(); - payload.copy(payload); - payload.setValue(ByteString.copyFromUtf8(chunk)); - - tuples.add(new MessageParameterTuple(header, payload)); - - chunkNr.getAndIncrement(); - }); - return tuples; - } else { - getNativeLogger().debug("The message is not chunked since the current size of the payload ({}) is not above the limitation or the technical message type '{}' does not support chunking.", payloadParameters.getValue().toStringUtf8().length(), messageHeaderParameters.getTechnicalMessageType().getKey()); - return Collections.singletonList(new MessageParameterTuple(messageHeaderParameters, payloadParameters)); - } - } + getNativeLogger().trace("Encode header."); + header(messageHeaderParameters).writeDelimitedTo(streamedMessage); + + getNativeLogger().trace("Encode payload."); + payload(payloadParameters).writeDelimitedTo(streamedMessage); - private Request.RequestEnvelope header(MessageHeaderParameters parameters) { - logMethodBegin(parameters); - - getNativeLogger().trace("Create message header."); - agrirouter.request.Request.RequestEnvelope.Builder messageHeader = - Request.RequestEnvelope.newBuilder(); - messageHeader.setApplicationMessageId(parameters.getApplicationMessageId()); - messageHeader.setApplicationMessageSeqNo(parameters.getApplicationMessageSeqNo()); - messageHeader.setTechnicalMessageType(parameters.getTechnicalMessageType().getKey()); - messageHeader.setMode(parameters.getMode()); - if (null != parameters.getMetadata()) { - messageHeader.setMetadata(parameters.getMetadata()); - } - if (StringUtils.isNotBlank(parameters.getTeamSetContextId())) { - messageHeader.setTeamSetContextId(parameters.getTeamSetContextId()); - } - if (!parameters.getRecipients().isEmpty()) { - messageHeader.addAllRecipients(parameters.getRecipients()); - } - if (parameters.getChunkInfo() != null) { - messageHeader.setChunkInfo(parameters.getChunkInfo()); - } - messageHeader.setTimestamp(new TimestampUtil().current()); - - getNativeLogger().trace("Build message envelope."); - Request.RequestEnvelope requestEnvelope = messageHeader.build(); - - logMethodEnd(requestEnvelope); - return requestEnvelope; + getNativeLogger().trace("Encoding message."); + String encodedMessage = Base64.getEncoder().encodeToString(streamedMessage.toByteArray()); + + logMethodEnd(encodedMessage); + return encodedMessage; + } catch (IOException e) { + throw new CouldNotEncodeMessageException(e); + } + } + + /** + * Encode a number of messages. + * + * @param messageParameterTuples - + * @return - + */ + public List encode(List messageParameterTuples) { + return messageParameterTuples.stream() + .map( + messageParameterTuple -> + encode( + messageParameterTuple.getMessageHeaderParameters(), + messageParameterTuple.getPayloadParameters())) + .collect(Collectors.toList()); + } + + /** + * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are + * not longer in control of the application. + * + * @param messageHeaderParameters - + * @param payloadParameters - + * @return - + */ + public List chunk( + MessageHeaderParameters messageHeaderParameters, + PayloadParameters payloadParameters, + OnboardingResponse onboardingResponse) { + logMethodBegin(messageHeaderParameters, payloadParameters); + + if (null == messageHeaderParameters || null == payloadParameters) { + throw new IllegalArgumentException("Parameters cannot be NULL"); } + messageHeaderParameters.validate(); + payloadParameters.validate(); + + if (null != messageHeaderParameters.getTechnicalMessageType() + && messageHeaderParameters.getTechnicalMessageType().getNeedsChunking() + && payloadParameters.shouldBeChunked()) { + getNativeLogger() + .debug( + "The message should be chunked, current size of the payload ({}) is above the limitation.", + payloadParameters.getValue().toStringUtf8().length()); + String wholeMessage = payloadParameters.getValue().toStringUtf8(); + final List messageChunks = + Splitter.fixedLength(payloadParameters.maxLengthForMessages()).splitToList(wholeMessage); + List tuples = new ArrayList<>(); + AtomicInteger chunkNr = new AtomicInteger(1); + final String chunkContextId = ChunkContextIdService.generateChunkContextId(); + messageChunks.forEach( + chunk -> { + final String messageIdForChunk = MessageIdService.generateMessageId(); + final long sequenceNumberForChunk = + SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse); + + final MessageHeaderParameters header = new MessageHeaderParameters(); + header.copy(messageHeaderParameters); + header.setApplicationMessageId(messageIdForChunk); + header.setApplicationMessageSeqNo(sequenceNumberForChunk); + Chunk.ChunkComponent.Builder chunkInfo = Chunk.ChunkComponent.newBuilder(); + chunkInfo.setContextId(chunkContextId); + chunkInfo.setCurrent(chunkNr.get()); + chunkInfo.setTotal(messageChunks.size()); + chunkInfo.setTotalSize(wholeMessage.length()); + header.setChunkInfo(chunkInfo.build()); + + final PayloadParameters payload = new PayloadParameters(); + payload.copy(payload); + payload.setValue(ByteString.copyFromUtf8(chunk)); + + tuples.add(new MessageParameterTuple(header, payload)); + + chunkNr.getAndIncrement(); + }); + return tuples; + } else { + getNativeLogger() + .debug( + "The message is not chunked since the current size of the payload ({}) is not above the limitation or the technical message type '{}' does not support chunking.", + payloadParameters.getValue().toStringUtf8().length(), + messageHeaderParameters.getTechnicalMessageType().getKey()); + return Collections.singletonList( + new MessageParameterTuple(messageHeaderParameters, payloadParameters)); + } + } + + private Request.RequestEnvelope header(MessageHeaderParameters parameters) { + logMethodBegin(parameters); + + getNativeLogger().trace("Create message header."); + agrirouter.request.Request.RequestEnvelope.Builder messageHeader = + Request.RequestEnvelope.newBuilder(); + messageHeader.setApplicationMessageId(parameters.getApplicationMessageId()); + messageHeader.setApplicationMessageSeqNo(parameters.getApplicationMessageSeqNo()); + messageHeader.setTechnicalMessageType(parameters.getTechnicalMessageType().getKey()); + messageHeader.setMode(parameters.getMode()); + if (null != parameters.getMetadata()) { + messageHeader.setMetadata(parameters.getMetadata()); + } + if (StringUtils.isNotBlank(parameters.getTeamSetContextId())) { + messageHeader.setTeamSetContextId(parameters.getTeamSetContextId()); + } + if (!parameters.getRecipients().isEmpty()) { + messageHeader.addAllRecipients(parameters.getRecipients()); + } + if (parameters.getChunkInfo() != null) { + messageHeader.setChunkInfo(parameters.getChunkInfo()); + } + messageHeader.setTimestamp(new TimestampUtil().current()); - private Request.RequestPayloadWrapper payload(PayloadParameters parameters) { - logMethodBegin(parameters); + getNativeLogger().trace("Build message envelope."); + Request.RequestEnvelope requestEnvelope = messageHeader.build(); - getNativeLogger().trace("Create message payload."); - Request.RequestPayloadWrapper.Builder messagePayload = - Request.RequestPayloadWrapper.newBuilder(); - Any.Builder builder = Any.newBuilder(); - builder.setTypeUrl(parameters.getTypeUrl()); - builder.setValue(parameters.getValue()); - messagePayload.setDetails(builder.build()); + logMethodEnd(requestEnvelope); + return requestEnvelope; + } - getNativeLogger().trace("Message message payload wrapper."); - Request.RequestPayloadWrapper requestPayloadWrapper = messagePayload.build(); + private Request.RequestPayloadWrapper payload(PayloadParameters parameters) { + logMethodBegin(parameters); - logMethodEnd(requestPayloadWrapper); - return requestPayloadWrapper; - } + getNativeLogger().trace("Create message payload."); + Request.RequestPayloadWrapper.Builder messagePayload = + Request.RequestPayloadWrapper.newBuilder(); + Any.Builder builder = Any.newBuilder(); + builder.setTypeUrl(parameters.getTypeUrl()); + builder.setValue(parameters.getValue()); + messagePayload.setDetails(builder.build()); + + getNativeLogger().trace("Message message payload wrapper."); + Request.RequestPayloadWrapper requestPayloadWrapper = messagePayload.build(); + + logMethodEnd(requestPayloadWrapper); + return requestPayloadWrapper; + } } diff --git a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java index ee602346..9bc0e1dd 100644 --- a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java +++ b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java @@ -1,6 +1,5 @@ package com.dke.data.agrirouter.impl.messaging.encoding; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import agrirouter.request.Request; @@ -13,16 +12,15 @@ import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; import com.google.protobuf.ByteString; +import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.List; - class EncodeMessageServiceImplTest { @Test - void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks(){ + void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); ByteString toSendMessage = ByteString.copyFromUtf8(""); @@ -30,12 +28,15 @@ void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumbe messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - final List chunks = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1,chunks.size()); + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); } @Test - void givenSingleChunkMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks(){ + void + givenSingleChunkMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); @@ -43,12 +44,15 @@ void givenSingleChunkMessageWhenChunkingThenTheImplementationShouldReturnTheRigh messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - final List chunks = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1,chunks.size()); + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); } @Test - void givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks(){ + void + givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000)); @@ -56,13 +60,16 @@ void givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldRe messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - final List chunks = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1,chunks.size()); + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); } @Test @SuppressWarnings("ConstantConditions") - void givenMultipleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks(){ + void + givenMultipleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024001)); @@ -70,16 +77,24 @@ void givenMultipleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShould messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - final List chunks = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(2,chunks.size()); + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(2, chunks.size()); - Assertions.assertEquals(2,chunks.get(0).getMessageHeaderParameters().getChunkInfo().getTotal()); - Assertions.assertEquals(2,chunks.get(1).getMessageHeaderParameters().getChunkInfo().getTotal()); + Assertions.assertEquals( + 2, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getTotal()); + Assertions.assertEquals( + 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getTotal()); - Assertions.assertEquals(1,chunks.get(0).getMessageHeaderParameters().getChunkInfo().getCurrent()); - Assertions.assertEquals(2,chunks.get(1).getMessageHeaderParameters().getChunkInfo().getCurrent()); + Assertions.assertEquals( + 1, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getCurrent()); + Assertions.assertEquals( + 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getCurrent()); - Assertions.assertEquals(chunks.get(0).getMessageHeaderParameters().getChunkInfo().getContextId(), chunks.get(1).getMessageHeaderParameters().getChunkInfo().getContextId()); + Assertions.assertEquals( + chunks.get(0).getMessageHeaderParameters().getChunkInfo().getContextId(), + chunks.get(1).getMessageHeaderParameters().getChunkInfo().getContextId()); } @Test @@ -156,5 +171,4 @@ private OnboardingResponse fakeOnboardingResponse() { onboardingResponse.setSensorAlternateId("THIS_IS_FAKE"); return onboardingResponse; } - } diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index dec3f990..88d9c4e2 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -1,5 +1,8 @@ package com.dke.data.agrirouter.test.messaging.rest; +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; + import agrirouter.request.Request; import com.dke.data.agrirouter.api.cancellation.DefaultCancellationToken; import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; @@ -24,87 +27,89 @@ import com.dke.data.agrirouter.test.Assertions; import com.dke.data.agrirouter.test.OnboardingResponseRepository; import com.google.protobuf.ByteString; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; - import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; -/** - * Test case to show the behavior for chunked message sending. - */ +/** Test case to show the behavior for chunked message sending. */ class SendChunkedMessageTest extends AbstractIntegrationTest { - @Test - void givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks() throws IOException, InterruptedException { - - final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - final SendMessageServiceImpl sendMessageService = new SendMessageServiceImpl(); - final OnboardingResponse onboardingResponse = OnboardingResponseRepository.read(OnboardingResponseRepository.Identifier.FARMING_SOFTWARE); - - - MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); - messageHeaderParameters.setApplicationMessageId(MessageIdService.generateMessageId()); - messageHeaderParameters.setApplicationMessageSeqNo(SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); - messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); - - PayloadParameters payloadParameters = new PayloadParameters(); - payloadParameters.setValue(fakeLargeMessageContent()); - payloadParameters.setTypeUrl(TechnicalMessageType.EMPTY.getKey()); - - List tuples = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); - - List encodedMessages = encodeMessageService.encode(tuples); - - SendMessageParameters sendMessageParameters = new SendMessageParameters(); - sendMessageParameters.setEncodedMessages(encodedMessages); - sendMessageParameters.setOnboardingResponse(onboardingResponse); - sendMessageService.send(sendMessageParameters); - - Thread.sleep(TimeUnit.SECONDS.toMillis(3)); - - FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); - Optional> fetchMessageResponses = - fetchMessageService.fetch( - onboardingResponse, - new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); - - Assertions.assertTrue(fetchMessageResponses.isPresent()); - Assertions.assertEquals(3, fetchMessageResponses.get().size()); - Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); - Assertions.assertNotNull(fetchMessageResponses.get().get(1).getCommand()); - Assertions.assertNotNull(fetchMessageResponses.get().get(2).getCommand()); - - Message firstAck = fetchMessageResponses.get().get(0).getCommand(); - Message secondAck = fetchMessageResponses.get().get(1).getCommand(); - Message thirdAck = fetchMessageResponses.get().get(2).getCommand(); - - Arrays.stream(new Message[]{firstAck,secondAck,thirdAck}).forEach(message -> { - DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); - DecodeMessageResponse decodeMessageResponse = decodeMessageService.decode(message.getMessage()); - - Assertions.assertMatchesAny( - Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT), - decodeMessageResponse.getResponseEnvelope().getResponseCode()); - }); - } - - /** - * Delivers fake message content for three chunks. - * - * @return - - */ - private ByteString fakeLargeMessageContent() { - return ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000 * 3)); - } - + public static final int EXPECTED_NUMBER_OF_CHUNKS = 3; + + @Test + void + givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks() + throws IOException, InterruptedException { + + final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + final SendMessageServiceImpl sendMessageService = new SendMessageServiceImpl(); + final OnboardingResponse onboardingResponse = + OnboardingResponseRepository.read(OnboardingResponseRepository.Identifier.FARMING_SOFTWARE); + + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); + messageHeaderParameters.setApplicationMessageId(MessageIdService.generateMessageId()); + messageHeaderParameters.setApplicationMessageSeqNo( + SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); + messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); + + PayloadParameters payloadParameters = new PayloadParameters(); + payloadParameters.setValue(fakeLargeMessageContent()); + payloadParameters.setTypeUrl(TechnicalMessageType.EMPTY.getKey()); + + List tuples = + encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); + + List encodedMessages = encodeMessageService.encode(tuples); + + SendMessageParameters sendMessageParameters = new SendMessageParameters(); + sendMessageParameters.setEncodedMessages(encodedMessages); + sendMessageParameters.setOnboardingResponse(onboardingResponse); + sendMessageService.send(sendMessageParameters); + + Thread.sleep(TimeUnit.SECONDS.toMillis(3)); + + FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); + Optional> fetchMessageResponses = + fetchMessageService.fetch( + onboardingResponse, + new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); + + Assertions.assertTrue(fetchMessageResponses.isPresent()); + Assertions.assertEquals(EXPECTED_NUMBER_OF_CHUNKS, fetchMessageResponses.get().size()); + Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); + Assertions.assertNotNull(fetchMessageResponses.get().get(1).getCommand()); + Assertions.assertNotNull(fetchMessageResponses.get().get(2).getCommand()); + + Message firstAck = fetchMessageResponses.get().get(0).getCommand(); + Message secondAck = fetchMessageResponses.get().get(1).getCommand(); + Message thirdAck = fetchMessageResponses.get().get(2).getCommand(); + + Arrays.stream(new Message[] {firstAck, secondAck, thirdAck}) + .forEach( + message -> { + DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); + DecodeMessageResponse decodeMessageResponse = + decodeMessageService.decode(message.getMessage()); + + Assertions.assertMatchesAny( + Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT), + decodeMessageResponse.getResponseEnvelope().getResponseCode()); + }); + } + + /** + * Delivers fake message content for three chunks. + * + * @return - + */ + private ByteString fakeLargeMessageContent() { + return ByteString.copyFromUtf8( + RandomStringUtils.randomAlphabetic(1024000 * EXPECTED_NUMBER_OF_CHUNKS)); + } } From 2b94e369ebc950d22c2faa58d1885d85dfdd69c4 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Fri, 26 Nov 2021 08:26:30 +0100 Subject: [PATCH 05/30] Merge latest `master` and adapt implementation. --- .../api/enums/ContentMessageType.kt | 32 +- .../agrirouter/api/enums/SystemMessageType.kt | 4 + .../api/enums/TechnicalMessageType.kt | 4 + .../EncodeMessageServiceImplTest.java | 313 +++++++++--------- .../rest/SendChunkedMessageTest.java | 166 +++++----- 5 files changed, 269 insertions(+), 250 deletions(-) diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/ContentMessageType.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/ContentMessageType.kt index bc16d73a..eff8a1f6 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/ContentMessageType.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/ContentMessageType.kt @@ -5,22 +5,22 @@ import agrirouter.technicalmessagetype.Gps /** * Enum containing all the content message types the AR is supporting. */ -enum class ContentMessageType(private val key: String, private val typeUrl: String) : TechnicalMessageType { - ISO_11783_TASKDATA_ZIP("iso:11783:-10:taskdata:zip", ""), +@Suppress("unused") +enum class ContentMessageType(private val key: String, private val typeUrl: String, private val needsChunkinng: Boolean) : TechnicalMessageType { + ISO_11783_TASKDATA_ZIP("iso:11783:-10:taskdata:zip", "", true), + SHP_SHAPE_ZIP("shp:shape:zip", "", true), + DOC_PDF("doc:pdf", "", true), + IMG_JPEG("img:jpeg", "", true), + IMG_PNG("img:png", "", true), + IMG_BMP("img:bmp", "", true), + VID_AVI("vid:avi", "", true), + VID_MP4("vid:mp4", "", true), + VID_WMV("vid:wmv", "", true), + GPS_INFO("gps:info", Gps.GPSList.getDescriptor().fullName, false), //FIXME Since the spec is not public, we can only use those literals. - ISO_11783_DEVICE_DESCRIPTION("iso:11783:-10:device_description:protobuf", "types.agrirouter.com\\efdi.ISO11783_TaskData"), - ISO_11783_TIME_LOG("iso:11783:-10:time_log:protobuf", "types.agrirouter.com\\efdi.TimeLog"), - - SHP_SHAPE_ZIP("shp:shape:zip", ""), - DOC_PDF("doc:pdf", ""), - IMG_JPEG("img:jpeg", ""), - IMG_PNG("img:png", ""), - IMG_BMP("img:bmp", ""), - VID_AVI("vid:avi", ""), - VID_MP4("vid:mp4", ""), - VID_WMV("vid:wmv", ""), - GPS_INFO("gps:info", Gps.GPSList.getDescriptor().fullName); + ISO_11783_DEVICE_DESCRIPTION("iso:11783:-10:device_description:protobuf", "types.agrirouter.com\\efdi.ISO11783_TaskData", false), + ISO_11783_TIME_LOG("iso:11783:-10:time_log:protobuf", "types.agrirouter.com\\efdi.TimeLog", false); override fun getKey(): String { return key @@ -29,4 +29,8 @@ enum class ContentMessageType(private val key: String, private val typeUrl: Stri override fun getTypeUrl(): String { return typeUrl } + + override fun getNeedsChunking(): Boolean { + return needsChunkinng + } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/SystemMessageType.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/SystemMessageType.kt index 953a091e..6521e5b3 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/SystemMessageType.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/SystemMessageType.kt @@ -9,6 +9,7 @@ import agrirouter.request.payload.endpoint.SubscriptionOuterClass /** * Enum containing all the content message types the AR is supporting. */ +@Suppress("unused") enum class SystemMessageType(private val key: String, private val typeUrl: String) : TechnicalMessageType { EMPTY("", ""), DKE_CLOUD_ONBOARD_ENDPOINTS("dke:cloud_onboard_endpoints", CloudVirtualizedAppRegistration.OnboardingRequest.getDescriptor().fullName), @@ -30,4 +31,7 @@ enum class SystemMessageType(private val key: String, private val typeUrl: Strin return typeUrl } + override fun getNeedsChunking(): Boolean { + return false + } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt index b2293265..4648b635 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt @@ -15,4 +15,8 @@ interface TechnicalMessageType { */ fun getTypeUrl(): String + /** + * Indicates whether the technical message type needs chunking or not. + */ + fun getNeedsChunking(): Boolean } diff --git a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java index 933dbb72..39ce17aa 100644 --- a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java +++ b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java @@ -1,173 +1,176 @@ package com.dke.data.agrirouter.impl.messaging.encoding; -import static org.junit.jupiter.api.Assertions.assertThrows; - import agrirouter.request.Request; import agrirouter.request.payload.endpoint.Capabilities; import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; +import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; +import com.dke.data.agrirouter.api.enums.ContentMessageType; import com.dke.data.agrirouter.api.enums.SystemMessageType; import com.dke.data.agrirouter.api.service.messaging.encoding.EncodeMessageService; import com.dke.data.agrirouter.api.service.parameters.MessageHeaderParameters; import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; import com.google.protobuf.ByteString; -import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertThrows; + class EncodeMessageServiceImplTest { - @Test - void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8(""); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - final List chunks = - encodeMessageService.chunk( - messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1, chunks.size()); - } - - @Test - void - givenSingleChunkMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - final List chunks = - encodeMessageService.chunk( - messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1, chunks.size()); - } - - @Test - void - givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000)); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - final List chunks = - encodeMessageService.chunk( - messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1, chunks.size()); - } - - @Test - @SuppressWarnings("ConstantConditions") - void - givenMultipleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024001)); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - final List chunks = - encodeMessageService.chunk( - messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(2, chunks.size()); - - Assertions.assertEquals( - 2, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getTotal()); - Assertions.assertEquals( - 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getTotal()); - - Assertions.assertEquals( - 1, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getCurrent()); - Assertions.assertEquals( - 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getCurrent()); - - Assertions.assertEquals( - chunks.get(0).getMessageHeaderParameters().getChunkInfo().getContextId(), - chunks.get(1).getMessageHeaderParameters().getChunkInfo().getContextId()); - } - - @Test - void givenValidParametersEncodeAndDecodeBackShouldNotFail() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - String encodedMessage = encodeMessageService.encode(messageHeaderParameters, payloadParameters); - DecodeMessageServiceImpl decodeMessageService = new DecodeMessageServiceImpl(); - DecodeMessageResponse response = decodeMessageService.decode(encodedMessage); - Assertions.assertEquals( - "secretMessage", - response.getResponsePayloadWrapper().getDetails().getValue().toStringUtf8()); - } - - @Test - void givenWrongPayloadEncodeAndDecodeBackShouldFail() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8("wrong Message"); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - String encodedMessage = encodeMessageService.encode(messageHeaderParameters, payloadParameters); - DecodeMessageServiceImpl decodeMessageService = new DecodeMessageServiceImpl(); - DecodeMessageResponse response = decodeMessageService.decode(encodedMessage); - Assertions.assertNotEquals( - "secretMessage", - response.getResponsePayloadWrapper().getDetails().getValue().toStringUtf8()); - } - - @Test - void givenNullPayLoadParametersEncodeShouldThrowException() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - assertThrows( - IllegalArgumentException.class, - () -> encodeMessageService.encode(messageHeaderParameters, null)); - } - - @Test - void givenNullMessageHeaderEncodeShouldThrowException() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - PayloadParameters payloadParameters = - getPayloadParameters(ByteString.copyFromUtf8("secretMessage")); - assertThrows( - IllegalArgumentException.class, () -> encodeMessageService.encode(null, payloadParameters)); - } - - private MessageHeaderParameters getMessageHeaderParameters() { - MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); - messageHeaderParameters.setApplicationMessageId("1"); - messageHeaderParameters.setApplicationMessageSeqNo(1); - messageHeaderParameters.setTechnicalMessageType(SystemMessageType.DKE_CAPABILITIES); - messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); - return messageHeaderParameters; - } - - private PayloadParameters getPayloadParameters(ByteString toSendMessage) { - PayloadParameters payloadParameters = new PayloadParameters(); - payloadParameters.setTypeUrl( - Capabilities.CapabilitySpecification.getDescriptor().getFullName()); - payloadParameters.setValue(toSendMessage); - return payloadParameters; - } - - private OnboardingResponse fakeOnboardingResponse() { - OnboardingResponse onboardingResponse = new OnboardingResponse(); - onboardingResponse.setSensorAlternateId("THIS_IS_FAKE"); - return onboardingResponse; - } + @Test + void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(""); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); + } + + @Test + void + givenSingleChunkMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); + } + + @Test + void + givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000)); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); + } + + @Test + @SuppressWarnings("ConstantConditions") + void + givenMultipleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024001)); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(2, chunks.size()); + + Assertions.assertEquals( + 2, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getTotal()); + Assertions.assertEquals( + 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getTotal()); + + Assertions.assertEquals( + 1, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getCurrent()); + Assertions.assertEquals( + 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getCurrent()); + + Assertions.assertEquals( + chunks.get(0).getMessageHeaderParameters().getChunkInfo().getContextId(), + chunks.get(1).getMessageHeaderParameters().getChunkInfo().getContextId()); + } + + @Test + void givenValidParametersEncodeAndDecodeBackShouldNotFail() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + String encodedMessage = encodeMessageService.encode(messageHeaderParameters, payloadParameters); + DecodeMessageServiceImpl decodeMessageService = new DecodeMessageServiceImpl(); + DecodeMessageResponse response = decodeMessageService.decode(encodedMessage); + Assertions.assertEquals( + "secretMessage", + response.getResponsePayloadWrapper().getDetails().getValue().toStringUtf8()); + } + + @Test + void givenWrongPayloadEncodeAndDecodeBackShouldFail() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8("wrong Message"); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + String encodedMessage = encodeMessageService.encode(messageHeaderParameters, payloadParameters); + DecodeMessageServiceImpl decodeMessageService = new DecodeMessageServiceImpl(); + DecodeMessageResponse response = decodeMessageService.decode(encodedMessage); + Assertions.assertNotEquals( + "secretMessage", + response.getResponsePayloadWrapper().getDetails().getValue().toStringUtf8()); + } + + @Test + void givenNullPayLoadParametersEncodeShouldThrowException() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + assertThrows( + IllegalArgumentException.class, + () -> encodeMessageService.encode(messageHeaderParameters, null)); + } + + @Test + void givenNullMessageHeaderEncodeShouldThrowException() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + PayloadParameters payloadParameters = + getPayloadParameters(ByteString.copyFromUtf8("secretMessage")); + assertThrows( + IllegalArgumentException.class, () -> encodeMessageService.encode(null, payloadParameters)); + } + + private MessageHeaderParameters getMessageHeaderParameters() { + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); + messageHeaderParameters.setApplicationMessageId("1"); + messageHeaderParameters.setApplicationMessageSeqNo(1); + messageHeaderParameters.setTechnicalMessageType(SystemMessageType.DKE_CAPABILITIES); + messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); + return messageHeaderParameters; + } + + private PayloadParameters getPayloadParameters(ByteString toSendMessage) { + PayloadParameters payloadParameters = new PayloadParameters(); + payloadParameters.setTypeUrl( + Capabilities.CapabilitySpecification.getDescriptor().getFullName()); + payloadParameters.setValue(toSendMessage); + return payloadParameters; + } + + private OnboardingResponse fakeOnboardingResponse() { + OnboardingResponse onboardingResponse = new OnboardingResponse(); + onboardingResponse.setSensorAlternateId("THIS_IS_FAKE"); + return onboardingResponse; + } } diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index 88d9c4e2..2c3b513b 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -1,15 +1,13 @@ package com.dke.data.agrirouter.test.messaging.rest; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; - import agrirouter.request.Request; import com.dke.data.agrirouter.api.cancellation.DefaultCancellationToken; import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; import com.dke.data.agrirouter.api.dto.messaging.FetchMessageResponse; import com.dke.data.agrirouter.api.dto.messaging.inner.Message; import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; -import com.dke.data.agrirouter.api.enums.TechnicalMessageType; +import com.dke.data.agrirouter.api.enums.ContentMessageType; +import com.dke.data.agrirouter.api.enums.SystemMessageType; import com.dke.data.agrirouter.api.service.messaging.encoding.DecodeMessageService; import com.dke.data.agrirouter.api.service.messaging.encoding.EncodeMessageService; import com.dke.data.agrirouter.api.service.messaging.http.FetchMessageService; @@ -27,89 +25,95 @@ import com.dke.data.agrirouter.test.Assertions; import com.dke.data.agrirouter.test.OnboardingResponseRepository; import com.google.protobuf.ByteString; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; -/** Test case to show the behavior for chunked message sending. */ +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; + +/** + * Test case to show the behavior for chunked message sending. + */ class SendChunkedMessageTest extends AbstractIntegrationTest { - public static final int EXPECTED_NUMBER_OF_CHUNKS = 3; - - @Test - void - givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks() - throws IOException, InterruptedException { - - final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - final SendMessageServiceImpl sendMessageService = new SendMessageServiceImpl(); - final OnboardingResponse onboardingResponse = - OnboardingResponseRepository.read(OnboardingResponseRepository.Identifier.FARMING_SOFTWARE); - - MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.ISO_11783_TASKDATA_ZIP); - messageHeaderParameters.setApplicationMessageId(MessageIdService.generateMessageId()); - messageHeaderParameters.setApplicationMessageSeqNo( - SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); - messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); - - PayloadParameters payloadParameters = new PayloadParameters(); - payloadParameters.setValue(fakeLargeMessageContent()); - payloadParameters.setTypeUrl(TechnicalMessageType.EMPTY.getKey()); - - List tuples = - encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); - - List encodedMessages = encodeMessageService.encode(tuples); - - SendMessageParameters sendMessageParameters = new SendMessageParameters(); - sendMessageParameters.setEncodedMessages(encodedMessages); - sendMessageParameters.setOnboardingResponse(onboardingResponse); - sendMessageService.send(sendMessageParameters); - - Thread.sleep(TimeUnit.SECONDS.toMillis(3)); - - FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); - Optional> fetchMessageResponses = - fetchMessageService.fetch( - onboardingResponse, - new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); - - Assertions.assertTrue(fetchMessageResponses.isPresent()); - Assertions.assertEquals(EXPECTED_NUMBER_OF_CHUNKS, fetchMessageResponses.get().size()); - Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); - Assertions.assertNotNull(fetchMessageResponses.get().get(1).getCommand()); - Assertions.assertNotNull(fetchMessageResponses.get().get(2).getCommand()); - - Message firstAck = fetchMessageResponses.get().get(0).getCommand(); - Message secondAck = fetchMessageResponses.get().get(1).getCommand(); - Message thirdAck = fetchMessageResponses.get().get(2).getCommand(); - - Arrays.stream(new Message[] {firstAck, secondAck, thirdAck}) - .forEach( - message -> { - DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); - DecodeMessageResponse decodeMessageResponse = - decodeMessageService.decode(message.getMessage()); - - Assertions.assertMatchesAny( - Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT), - decodeMessageResponse.getResponseEnvelope().getResponseCode()); - }); - } - - /** - * Delivers fake message content for three chunks. - * - * @return - - */ - private ByteString fakeLargeMessageContent() { - return ByteString.copyFromUtf8( - RandomStringUtils.randomAlphabetic(1024000 * EXPECTED_NUMBER_OF_CHUNKS)); - } + public static final int EXPECTED_NUMBER_OF_CHUNKS = 3; + + @Test + void + givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks() + throws IOException, InterruptedException { + + final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + final SendMessageServiceImpl sendMessageService = new SendMessageServiceImpl(); + final OnboardingResponse onboardingResponse = + OnboardingResponseRepository.read(OnboardingResponseRepository.Identifier.FARMING_SOFTWARE); + + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + messageHeaderParameters.setApplicationMessageId(MessageIdService.generateMessageId()); + messageHeaderParameters.setApplicationMessageSeqNo( + SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); + messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); + + PayloadParameters payloadParameters = new PayloadParameters(); + payloadParameters.setValue(fakeLargeMessageContent()); + payloadParameters.setTypeUrl(SystemMessageType.EMPTY.getKey()); + + List tuples = + encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); + + List encodedMessages = encodeMessageService.encode(tuples); + + SendMessageParameters sendMessageParameters = new SendMessageParameters(); + sendMessageParameters.setEncodedMessages(encodedMessages); + sendMessageParameters.setOnboardingResponse(onboardingResponse); + sendMessageService.send(sendMessageParameters); + + Thread.sleep(TimeUnit.SECONDS.toMillis(3)); + + FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); + Optional> fetchMessageResponses = + fetchMessageService.fetch( + onboardingResponse, + new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); + + Assertions.assertTrue(fetchMessageResponses.isPresent()); + Assertions.assertEquals(EXPECTED_NUMBER_OF_CHUNKS, fetchMessageResponses.get().size()); + Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); + Assertions.assertNotNull(fetchMessageResponses.get().get(1).getCommand()); + Assertions.assertNotNull(fetchMessageResponses.get().get(2).getCommand()); + + Message firstAck = fetchMessageResponses.get().get(0).getCommand(); + Message secondAck = fetchMessageResponses.get().get(1).getCommand(); + Message thirdAck = fetchMessageResponses.get().get(2).getCommand(); + + Arrays.stream(new Message[]{firstAck, secondAck, thirdAck}) + .forEach( + message -> { + DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); + DecodeMessageResponse decodeMessageResponse = + decodeMessageService.decode(message.getMessage()); + + Assertions.assertMatchesAny( + Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT), + decodeMessageResponse.getResponseEnvelope().getResponseCode()); + }); + } + + /** + * Delivers fake message content for three chunks. + * + * @return - + */ + private ByteString fakeLargeMessageContent() { + return ByteString.copyFromUtf8( + RandomStringUtils.randomAlphabetic(1024000 * EXPECTED_NUMBER_OF_CHUNKS)); + } } From 35d4119572679fb05ba1931789ca7777032ad9bb Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Fri, 26 Nov 2021 08:58:09 +0100 Subject: [PATCH 06/30] Adapt implementation for chunking. --- .../api/service/parameters/PayloadParameters.kt | 2 +- .../messaging/encoding/EncodeMessageServiceImpl.java | 2 +- .../encoding/EncodeMessageServiceImplTest.java | 2 ++ .../test/messaging/rest/SendChunkedMessageTest.java | 12 +++++++++++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index 0c2521ef..b23b7cf5 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -42,7 +42,7 @@ class PayloadParameters : AbstractParameterBase(), ParameterValidation { return MAX_LENGTH_FOR_MESSAGES } - fun copy(payload: PayloadParameters) { + fun copyFrom(payload: PayloadParameters) { applicationMessageId = payload.applicationMessageId teamsetContextId = payload.teamsetContextId sequenceNumber = payload.sequenceNumber diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index 20c06514..f222a75c 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -133,7 +133,7 @@ public List chunk( header.setChunkInfo(chunkInfo.build()); final PayloadParameters payload = new PayloadParameters(); - payload.copy(payload); + payload.copyFrom(payloadParameters); payload.setValue(ByteString.copyFromUtf8(chunk)); tuples.add(new MessageParameterTuple(header, payload)); diff --git a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java index 39ce17aa..0af9a216 100644 --- a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java +++ b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java @@ -21,6 +21,8 @@ class EncodeMessageServiceImplTest { + + @Test void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index 2c3b513b..bf5ec309 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -30,7 +30,9 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -107,6 +109,14 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { }); } + @Test + void determineMaxChunkLengthForNonEncodedChunks() { + String base64EncodedString = new String(Base64.getEncoder().encode(RandomStringUtils.randomAlphabetic(767997).getBytes(StandardCharsets.UTF_8))); + Assertions.assertTrue(base64EncodedString.length() < 1024000); + base64EncodedString = new String(Base64.getEncoder().encode(RandomStringUtils.randomAlphabetic(767998).getBytes(StandardCharsets.UTF_8))); + Assertions.assertFalse(base64EncodedString.length() < 1024000); + } + /** * Delivers fake message content for three chunks. * @@ -114,6 +124,6 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { */ private ByteString fakeLargeMessageContent() { return ByteString.copyFromUtf8( - RandomStringUtils.randomAlphabetic(1024000 * EXPECTED_NUMBER_OF_CHUNKS)); + RandomStringUtils.randomAlphabetic(767997 * EXPECTED_NUMBER_OF_CHUNKS)); } } From 81fc0e671bfd0251afae53e6e7f5d7b88ef85c6d Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Fri, 26 Nov 2021 10:15:59 +0100 Subject: [PATCH 07/30] Adapt wait time to handle agrirouter response time. --- .../EncodeMessageServiceImplTest.java | 313 +++++++++--------- .../test/AbstractIntegrationTest.java | 10 + .../rest/CloudOffboardingServiceTest.java | 5 +- .../rest/CloudOnboardingServiceTest.java | 5 +- .../rest/SendChunkedMessageTest.java | 188 ++++++----- .../rest/SetCapabilityServiceTest.java | 5 +- .../rest/SetSubscriptionServiceTest.java | 5 +- 7 files changed, 269 insertions(+), 262 deletions(-) diff --git a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java index 0af9a216..b828c0b5 100644 --- a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java +++ b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java @@ -1,5 +1,7 @@ package com.dke.data.agrirouter.impl.messaging.encoding; +import static org.junit.jupiter.api.Assertions.assertThrows; + import agrirouter.request.Request; import agrirouter.request.payload.endpoint.Capabilities; import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; @@ -11,168 +13,163 @@ import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; import com.google.protobuf.ByteString; +import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertThrows; - class EncodeMessageServiceImplTest { - - - @Test - void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8(""); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - final List chunks = - encodeMessageService.chunk( - messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1, chunks.size()); - } - - @Test - void - givenSingleChunkMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - final List chunks = - encodeMessageService.chunk( - messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1, chunks.size()); - } - - @Test - void - givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000)); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - final List chunks = - encodeMessageService.chunk( - messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(1, chunks.size()); - } - - @Test - @SuppressWarnings("ConstantConditions") - void - givenMultipleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024001)); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - final List chunks = - encodeMessageService.chunk( - messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); - Assertions.assertEquals(2, chunks.size()); - - Assertions.assertEquals( - 2, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getTotal()); - Assertions.assertEquals( - 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getTotal()); - - Assertions.assertEquals( - 1, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getCurrent()); - Assertions.assertEquals( - 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getCurrent()); - - Assertions.assertEquals( - chunks.get(0).getMessageHeaderParameters().getChunkInfo().getContextId(), - chunks.get(1).getMessageHeaderParameters().getChunkInfo().getContextId()); - } - - @Test - void givenValidParametersEncodeAndDecodeBackShouldNotFail() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - String encodedMessage = encodeMessageService.encode(messageHeaderParameters, payloadParameters); - DecodeMessageServiceImpl decodeMessageService = new DecodeMessageServiceImpl(); - DecodeMessageResponse response = decodeMessageService.decode(encodedMessage); - Assertions.assertEquals( - "secretMessage", - response.getResponsePayloadWrapper().getDetails().getValue().toStringUtf8()); - } - - @Test - void givenWrongPayloadEncodeAndDecodeBackShouldFail() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - ByteString toSendMessage = ByteString.copyFromUtf8("wrong Message"); - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); - - String encodedMessage = encodeMessageService.encode(messageHeaderParameters, payloadParameters); - DecodeMessageServiceImpl decodeMessageService = new DecodeMessageServiceImpl(); - DecodeMessageResponse response = decodeMessageService.decode(encodedMessage); - Assertions.assertNotEquals( - "secretMessage", - response.getResponsePayloadWrapper().getDetails().getValue().toStringUtf8()); - } - - @Test - void givenNullPayLoadParametersEncodeShouldThrowException() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); - assertThrows( - IllegalArgumentException.class, - () -> encodeMessageService.encode(messageHeaderParameters, null)); - } - - @Test - void givenNullMessageHeaderEncodeShouldThrowException() { - EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - - PayloadParameters payloadParameters = - getPayloadParameters(ByteString.copyFromUtf8("secretMessage")); - assertThrows( - IllegalArgumentException.class, () -> encodeMessageService.encode(null, payloadParameters)); - } - - private MessageHeaderParameters getMessageHeaderParameters() { - MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); - messageHeaderParameters.setApplicationMessageId("1"); - messageHeaderParameters.setApplicationMessageSeqNo(1); - messageHeaderParameters.setTechnicalMessageType(SystemMessageType.DKE_CAPABILITIES); - messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); - return messageHeaderParameters; - } - - private PayloadParameters getPayloadParameters(ByteString toSendMessage) { - PayloadParameters payloadParameters = new PayloadParameters(); - payloadParameters.setTypeUrl( - Capabilities.CapabilitySpecification.getDescriptor().getFullName()); - payloadParameters.setValue(toSendMessage); - return payloadParameters; - } - - private OnboardingResponse fakeOnboardingResponse() { - OnboardingResponse onboardingResponse = new OnboardingResponse(); - onboardingResponse.setSensorAlternateId("THIS_IS_FAKE"); - return onboardingResponse; - } + @Test + void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(""); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); + } + + @Test + void + givenSingleChunkMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); + } + + @Test + void + givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000)); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(1, chunks.size()); + } + + @Test + @SuppressWarnings("ConstantConditions") + void + givenMultipleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024001)); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + final List chunks = + encodeMessageService.chunk( + messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); + Assertions.assertEquals(2, chunks.size()); + + Assertions.assertEquals( + 2, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getTotal()); + Assertions.assertEquals( + 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getTotal()); + + Assertions.assertEquals( + 1, chunks.get(0).getMessageHeaderParameters().getChunkInfo().getCurrent()); + Assertions.assertEquals( + 2, chunks.get(1).getMessageHeaderParameters().getChunkInfo().getCurrent()); + + Assertions.assertEquals( + chunks.get(0).getMessageHeaderParameters().getChunkInfo().getContextId(), + chunks.get(1).getMessageHeaderParameters().getChunkInfo().getContextId()); + } + + @Test + void givenValidParametersEncodeAndDecodeBackShouldNotFail() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8("secretMessage"); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + String encodedMessage = encodeMessageService.encode(messageHeaderParameters, payloadParameters); + DecodeMessageServiceImpl decodeMessageService = new DecodeMessageServiceImpl(); + DecodeMessageResponse response = decodeMessageService.decode(encodedMessage); + Assertions.assertEquals( + "secretMessage", + response.getResponsePayloadWrapper().getDetails().getValue().toStringUtf8()); + } + + @Test + void givenWrongPayloadEncodeAndDecodeBackShouldFail() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + ByteString toSendMessage = ByteString.copyFromUtf8("wrong Message"); + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); + + String encodedMessage = encodeMessageService.encode(messageHeaderParameters, payloadParameters); + DecodeMessageServiceImpl decodeMessageService = new DecodeMessageServiceImpl(); + DecodeMessageResponse response = decodeMessageService.decode(encodedMessage); + Assertions.assertNotEquals( + "secretMessage", + response.getResponsePayloadWrapper().getDetails().getValue().toStringUtf8()); + } + + @Test + void givenNullPayLoadParametersEncodeShouldThrowException() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); + assertThrows( + IllegalArgumentException.class, + () -> encodeMessageService.encode(messageHeaderParameters, null)); + } + + @Test + void givenNullMessageHeaderEncodeShouldThrowException() { + EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + + PayloadParameters payloadParameters = + getPayloadParameters(ByteString.copyFromUtf8("secretMessage")); + assertThrows( + IllegalArgumentException.class, () -> encodeMessageService.encode(null, payloadParameters)); + } + + private MessageHeaderParameters getMessageHeaderParameters() { + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); + messageHeaderParameters.setApplicationMessageId("1"); + messageHeaderParameters.setApplicationMessageSeqNo(1); + messageHeaderParameters.setTechnicalMessageType(SystemMessageType.DKE_CAPABILITIES); + messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); + return messageHeaderParameters; + } + + private PayloadParameters getPayloadParameters(ByteString toSendMessage) { + PayloadParameters payloadParameters = new PayloadParameters(); + payloadParameters.setTypeUrl( + Capabilities.CapabilitySpecification.getDescriptor().getFullName()); + payloadParameters.setValue(toSendMessage); + return payloadParameters; + } + + private OnboardingResponse fakeOnboardingResponse() { + OnboardingResponse onboardingResponse = new OnboardingResponse(); + onboardingResponse.setSensorAlternateId("THIS_IS_FAKE"); + return onboardingResponse; + } } diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java index 23e9e6b5..b9e94ef5 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java @@ -1,10 +1,20 @@ package com.dke.data.agrirouter.test; +import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.NotImplementedException; /** Abstract implementation for all integration tests. */ public abstract class AbstractIntegrationTest { + /** + * Wait for the AR to process the messages. This can take up to five seconds. + * + * @throws InterruptedException - + */ + protected void waitForTheAgrirouterToProcessTheMessages() throws InterruptedException { + Thread.sleep(TimeUnit.SECONDS.toMillis(5)); + } + /** Farming software for integration testing. */ protected Application farmingSoftware = new Application() { diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOffboardingServiceTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOffboardingServiceTest.java index b44b0db4..7939f82f 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOffboardingServiceTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOffboardingServiceTest.java @@ -27,7 +27,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; import org.apache.http.HttpStatus; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -51,7 +50,7 @@ void onboardVirtualCu() throws Throwable { parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); cloudOnboardingService.send(parameters); - Thread.sleep(TimeUnit.SECONDS.toMillis(5)); + waitForTheAgrirouterToProcessTheMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -101,7 +100,7 @@ void givenValidSensorIdWhenOffboardingVirtualCuThenTheOffbardingShouldBeSuccessf parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); cloudOffboardingService.send(parameters); - Thread.sleep(TimeUnit.SECONDS.toMillis(5)); + waitForTheAgrirouterToProcessTheMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOnboardingServiceTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOnboardingServiceTest.java index 01c70cca..419278b0 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOnboardingServiceTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOnboardingServiceTest.java @@ -27,7 +27,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; import org.apache.http.HttpStatus; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -48,7 +47,7 @@ void offboardVirtualCu() throws Throwable { parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); cloudOffboardingService.send(parameters); - Thread.sleep(TimeUnit.SECONDS.toMillis(5)); + waitForTheAgrirouterToProcessTheMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -84,7 +83,7 @@ void givenValidIdAndNameWhenOnboardingVirtualCuThenTheOnbardingShouldBePossible( parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); cloudOnboardingService.send(parameters); - Thread.sleep(TimeUnit.SECONDS.toMillis(5)); + waitForTheAgrirouterToProcessTheMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index bf5ec309..1a454ff4 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -1,5 +1,8 @@ package com.dke.data.agrirouter.test.messaging.rest; +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; + import agrirouter.request.Request; import com.dke.data.agrirouter.api.cancellation.DefaultCancellationToken; import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; @@ -25,105 +28,106 @@ import com.dke.data.agrirouter.test.Assertions; import com.dke.data.agrirouter.test.OnboardingResponseRepository; import com.google.protobuf.ByteString; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; - import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; -/** - * Test case to show the behavior for chunked message sending. - */ +/** Test case to show the behavior for chunked message sending. */ class SendChunkedMessageTest extends AbstractIntegrationTest { - public static final int EXPECTED_NUMBER_OF_CHUNKS = 3; - - @Test - void - givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks() - throws IOException, InterruptedException { - - final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - final SendMessageServiceImpl sendMessageService = new SendMessageServiceImpl(); - final OnboardingResponse onboardingResponse = - OnboardingResponseRepository.read(OnboardingResponseRepository.Identifier.FARMING_SOFTWARE); - - MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); - messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); - messageHeaderParameters.setApplicationMessageId(MessageIdService.generateMessageId()); - messageHeaderParameters.setApplicationMessageSeqNo( - SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); - messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); - - PayloadParameters payloadParameters = new PayloadParameters(); - payloadParameters.setValue(fakeLargeMessageContent()); - payloadParameters.setTypeUrl(SystemMessageType.EMPTY.getKey()); - - List tuples = - encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); - - List encodedMessages = encodeMessageService.encode(tuples); - - SendMessageParameters sendMessageParameters = new SendMessageParameters(); - sendMessageParameters.setEncodedMessages(encodedMessages); - sendMessageParameters.setOnboardingResponse(onboardingResponse); - sendMessageService.send(sendMessageParameters); - - Thread.sleep(TimeUnit.SECONDS.toMillis(3)); - - FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); - Optional> fetchMessageResponses = - fetchMessageService.fetch( - onboardingResponse, - new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); - - Assertions.assertTrue(fetchMessageResponses.isPresent()); - Assertions.assertEquals(EXPECTED_NUMBER_OF_CHUNKS, fetchMessageResponses.get().size()); - Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); - Assertions.assertNotNull(fetchMessageResponses.get().get(1).getCommand()); - Assertions.assertNotNull(fetchMessageResponses.get().get(2).getCommand()); - - Message firstAck = fetchMessageResponses.get().get(0).getCommand(); - Message secondAck = fetchMessageResponses.get().get(1).getCommand(); - Message thirdAck = fetchMessageResponses.get().get(2).getCommand(); - - Arrays.stream(new Message[]{firstAck, secondAck, thirdAck}) - .forEach( - message -> { - DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); - DecodeMessageResponse decodeMessageResponse = - decodeMessageService.decode(message.getMessage()); - - Assertions.assertMatchesAny( - Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT), - decodeMessageResponse.getResponseEnvelope().getResponseCode()); - }); - } - - @Test - void determineMaxChunkLengthForNonEncodedChunks() { - String base64EncodedString = new String(Base64.getEncoder().encode(RandomStringUtils.randomAlphabetic(767997).getBytes(StandardCharsets.UTF_8))); - Assertions.assertTrue(base64EncodedString.length() < 1024000); - base64EncodedString = new String(Base64.getEncoder().encode(RandomStringUtils.randomAlphabetic(767998).getBytes(StandardCharsets.UTF_8))); - Assertions.assertFalse(base64EncodedString.length() < 1024000); - } - - /** - * Delivers fake message content for three chunks. - * - * @return - - */ - private ByteString fakeLargeMessageContent() { - return ByteString.copyFromUtf8( - RandomStringUtils.randomAlphabetic(767997 * EXPECTED_NUMBER_OF_CHUNKS)); - } + public static final int EXPECTED_NUMBER_OF_CHUNKS = 3; + + @Test + void + givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks() + throws IOException, InterruptedException { + + final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); + final SendMessageServiceImpl sendMessageService = new SendMessageServiceImpl(); + final OnboardingResponse onboardingResponse = + OnboardingResponseRepository.read(OnboardingResponseRepository.Identifier.FARMING_SOFTWARE); + + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); + messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); + messageHeaderParameters.setApplicationMessageId(MessageIdService.generateMessageId()); + messageHeaderParameters.setApplicationMessageSeqNo( + SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); + messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); + + PayloadParameters payloadParameters = new PayloadParameters(); + payloadParameters.setValue(fakeLargeMessageContent()); + payloadParameters.setTypeUrl(SystemMessageType.EMPTY.getKey()); + + List tuples = + encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); + + List encodedMessages = encodeMessageService.encode(tuples); + + SendMessageParameters sendMessageParameters = new SendMessageParameters(); + sendMessageParameters.setEncodedMessages(encodedMessages); + sendMessageParameters.setOnboardingResponse(onboardingResponse); + sendMessageService.send(sendMessageParameters); + + waitForTheAgrirouterToProcessTheMessages(); + + FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); + Optional> fetchMessageResponses = + fetchMessageService.fetch( + onboardingResponse, + new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); + + Assertions.assertTrue(fetchMessageResponses.isPresent()); + Assertions.assertEquals(EXPECTED_NUMBER_OF_CHUNKS, fetchMessageResponses.get().size()); + Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); + Assertions.assertNotNull(fetchMessageResponses.get().get(1).getCommand()); + Assertions.assertNotNull(fetchMessageResponses.get().get(2).getCommand()); + + Message firstAck = fetchMessageResponses.get().get(0).getCommand(); + Message secondAck = fetchMessageResponses.get().get(1).getCommand(); + Message thirdAck = fetchMessageResponses.get().get(2).getCommand(); + + Arrays.stream(new Message[] {firstAck, secondAck, thirdAck}) + .forEach( + message -> { + DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); + DecodeMessageResponse decodeMessageResponse = + decodeMessageService.decode(message.getMessage()); + + Assertions.assertMatchesAny( + Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT), + decodeMessageResponse.getResponseEnvelope().getResponseCode()); + }); + } + + @Test + void determineMaxChunkLengthForNonEncodedChunks() { + String base64EncodedString = + new String( + Base64.getEncoder() + .encode( + RandomStringUtils.randomAlphabetic(767997).getBytes(StandardCharsets.UTF_8))); + Assertions.assertTrue(base64EncodedString.length() < 1024000); + base64EncodedString = + new String( + Base64.getEncoder() + .encode( + RandomStringUtils.randomAlphabetic(767998).getBytes(StandardCharsets.UTF_8))); + Assertions.assertFalse(base64EncodedString.length() < 1024000); + } + + /** + * Delivers fake message content for three chunks. + * + * @return - + */ + private ByteString fakeLargeMessageContent() { + return ByteString.copyFromUtf8( + RandomStringUtils.randomAlphabetic(767997 * EXPECTED_NUMBER_OF_CHUNKS)); + } } diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetCapabilityServiceTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetCapabilityServiceTest.java index 1c8930b3..96886c22 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetCapabilityServiceTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetCapabilityServiceTest.java @@ -27,7 +27,6 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -54,7 +53,7 @@ void givenValidEndpointWhenSendingCapabilitiesTheCapabilityMessageShouldBeAccept parameters.setCapabilitiesParameters(capabilities); setCapabilityService.send(parameters); - Thread.sleep(TimeUnit.SECONDS.toMillis(3)); + waitForTheAgrirouterToProcessTheMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -97,7 +96,7 @@ void givenValidEndpointWhenSendingInvalidCapabilitiesTheCapabilityMessageShouldN parameters.setCapabilitiesParameters(capabilities); setCapabilityService.send(parameters); - Thread.sleep(TimeUnit.SECONDS.toMillis(3)); + waitForTheAgrirouterToProcessTheMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetSubscriptionServiceTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetSubscriptionServiceTest.java index 1dcf5b1f..86647332 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetSubscriptionServiceTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetSubscriptionServiceTest.java @@ -26,7 +26,6 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -48,7 +47,7 @@ void givenValidEndpointWhenSendingSubscriptionsTheSubscriptionMessageShouldBeAcc parameters.setSubscriptions(subscriptions); setSubscriptionService.send(parameters); - Thread.sleep(TimeUnit.SECONDS.toMillis(3)); + waitForTheAgrirouterToProcessTheMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -86,7 +85,7 @@ void givenValidEndpointWhenSendingInvalidSubscriptionsTheSubscriptionMessageShou parameters.setSubscriptions(subscriptions); setSubscriptionService.send(parameters); - Thread.sleep(TimeUnit.SECONDS.toMillis(3)); + waitForTheAgrirouterToProcessTheMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = From 2a8e4ef09436c014c33a582e7a7c6a0e0c41642a Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Fri, 26 Nov 2021 13:45:19 +0100 Subject: [PATCH 08/30] Adapt wait time to handle agrirouter response time. --- .../com/dke/data/agrirouter/test/AbstractIntegrationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java index b9e94ef5..5975367b 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java @@ -7,12 +7,12 @@ public abstract class AbstractIntegrationTest { /** - * Wait for the AR to process the messages. This can take up to five seconds. + * Wait for the AR to process the messages. This can take up to 10 seconds. * * @throws InterruptedException - */ protected void waitForTheAgrirouterToProcessTheMessages() throws InterruptedException { - Thread.sleep(TimeUnit.SECONDS.toMillis(5)); + Thread.sleep(TimeUnit.SECONDS.toMillis(10)); } /** Farming software for integration testing. */ From 9a86e46016ffc2d7089c61bed1eacc8363da27d2 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Wed, 8 Dec 2021 08:08:02 +0100 Subject: [PATCH 09/30] Fix business validation for parameters. --- .../api/service/ParameterValidation.java | 144 +++++++++--------- .../parameters/DeleteMessageParameters.kt | 13 +- .../parameters/MessageQueryParameters.kt | 13 +- 3 files changed, 80 insertions(+), 90 deletions(-) diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java index ee884976..0999c12c 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java @@ -1,88 +1,96 @@ package com.dke.data.agrirouter.api.service; import com.dke.data.agrirouter.api.exception.IllegalParameterDefinitionException; -import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** Parameter validation using bean validation. */ +import java.util.Collection; + +/** + * Parameter validation using bean validation. + */ public interface ParameterValidation { - Logger LOGGER = LogManager.getLogger(); + Logger LOGGER = LogManager.getLogger(); - /** - * Validation of the parameters. If there are any constraint violations, there will be a - * exception. - * - * @throws IllegalParameterDefinitionException - - */ - default void validate() { - LOGGER.debug("Validating parameters."); - LOGGER.trace("Technical validation."); - this.technicalValidation(); - LOGGER.trace("Business validation."); - this.businessValidation(); - } + /** + * Validation of the parameters. If there are any constraint violations, there will be a + * exception. + * + * @throws IllegalParameterDefinitionException - + */ + default void validate() { + LOGGER.debug("Validating parameters."); + LOGGER.trace("Technical validation."); + this.technicalValidation(); + LOGGER.trace("Business validation."); + this.businessValidation(); + } - /** Technical validation. Empty by default. */ - default void technicalValidation() { - // - } + /** + * Technical validation. Empty by default. + */ + default void technicalValidation() { + // + } - /** Business validation. Empty by default. */ - default void businessValidation() { - // - } + /** + * Business validation. Empty by default. + */ + default void businessValidation() { + // + } - /** - * Rise an exception if the parameter was not valid. - * - * @param message - - */ - default void rise(String message, String parameterName) { - throw new IllegalParameterDefinitionException( - String.format( - "Parameter '%s' was not defined correctly, please check the values. Error message is '%s'.", - parameterName, message)); - } + /** + * Rise an exception if there has to be at least one valid parameter. + * + * @param message - + */ + default void rise(String message, String... parameterNames) { + final String joinedParametersNames = String.join(",", parameterNames); + throw new IllegalParameterDefinitionException( + String.format( + "At least one of the following parameters has to be defined [%s] was not defined correctly, please check the values. '%s'.", + joinedParametersNames, message)); + } - /** - * Check for null values. - * - * @param o - - */ - default void nullCheck(String parameterName, Object o) { - if (null == o) { - this.rise( - "The parameter '%s' should not have been null, please check your values.", parameterName); + /** + * Check for null values. + * + * @param o - + */ + default void nullCheck(String parameterName, Object o) { + if (null == o) { + this.rise( + "The parameter '%s' should not have been null, please check your values.", parameterName); + } } - } - /** - * Check for null or empty values. - * - * @param s - - */ - default void isBlank(String parameterName, String s) { - if (StringUtils.isBlank(s)) { - this.rise( - "The parameter '%s' should not have been blank, please check your values.", - parameterName); + /** + * Check for null or empty values. + * + * @param s - + */ + default void isBlank(String parameterName, String s) { + if (StringUtils.isBlank(s)) { + this.rise( + "The parameter '%s' should not have been blank, please check your values.", + parameterName); + } } - } - /** - * Check for null or empty values. - * - * @param c - - */ - default void nullOrEmpty(String parameterName, Collection c) { - nullCheck(parameterName, c); - if (c.isEmpty()) { - this.rise( - "The parameter '%s' should not have been empty, please check your values.", - parameterName); + /** + * Check for null or empty values. + * + * @param c - + */ + default void nullOrEmpty(String parameterName, Collection c) { + nullCheck(parameterName, c); + if (c.isEmpty()) { + this.rise( + "The parameter '%s' should not have been empty, please check your values.", + parameterName); + } } - } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/DeleteMessageParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/DeleteMessageParameters.kt index 255b2e9b..a64483d1 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/DeleteMessageParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/DeleteMessageParameters.kt @@ -24,17 +24,8 @@ class DeleteMessageParameters : AbstractParameterBase(), ParameterValidation { } override fun businessValidation() { - if (null == messageIds) { - rise("messageIds", "There has to be a filter criteria for the query.") - } - if (null == senderIds) { - rise("senderIds", "There has to be a filter criteria for the query.") - } - if (null == sentFromInSeconds) { - rise("sentFromInSeconds", "There has to be a filter criteria for the query.") - } - if (null == sentToInSeconds) { - rise("sentToInSeconds", "There has to be a filter criteria for the query.") + if (null == messageIds && null == senderIds && null == sentFromInSeconds && null == sentToInSeconds) { + rise("There has to be a filter criteria for the query.", "messageIds", "senderIds", "sentFromInSeconds & sendToInSeconds", "") } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageQueryParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageQueryParameters.kt index eed06957..f3777b63 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageQueryParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/MessageQueryParameters.kt @@ -24,17 +24,8 @@ class MessageQueryParameters : AbstractParameterBase(), ParameterValidation { } override fun businessValidation() { - if (null == messageIds) { - rise("messageIds", "There has to be a filter criteria for the query.") - } - if (null == senderIds) { - rise("senderIds", "There has to be a filter criteria for the query.") - } - if (null == sentFromInSeconds) { - rise("sentFromInSeconds", "There has to be a filter criteria for the query.") - } - if (null == sentToInSeconds) { - rise("sentToInSeconds", "There has to be a filter criteria for the query.") + if (null == messageIds && null == senderIds && null == sentFromInSeconds && null == sentToInSeconds) { + rise("There has to be a filter criteria for the query.", "messageIds", "senderIds", "sentFromInSeconds & sendToInSeconds", "") } } From 4e68e74f30484faf1722580e15f9cbf86e8f15a2 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Wed, 8 Dec 2021 08:09:29 +0100 Subject: [PATCH 10/30] Fix comment. --- .../data/agrirouter/api/service/parameters/PayloadParameters.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index b23b7cf5..14eba92a 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -10,7 +10,7 @@ import com.google.protobuf.ByteString * - Total message size is limited to 1468000 characters/signs * - Message that are above this limit will be rejected. * The AR will return an error indicated that the message size is above the limit. - * If the message size is above 5 MB the AR will not return any error. In order to send message with size large that above threshold, the message must be split in chunks with the above limit. + * If the message size is above 5 MB the AR will not return any error. In order to send messages with sizes above threshold, these messages must be split into chunks with the above limit. */ const val MAX_LENGTH_FOR_MESSAGES = 1024000 From 692190c549d787de006f2f4017b2a522221cf95e Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Wed, 8 Dec 2021 08:12:37 +0100 Subject: [PATCH 11/30] Additional `null` value check. --- .../impl/messaging/encoding/EncodeMessageServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index f222a75c..46192441 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -96,7 +96,7 @@ public List chunk( OnboardingResponse onboardingResponse) { logMethodBegin(messageHeaderParameters, payloadParameters); - if (null == messageHeaderParameters || null == payloadParameters) { + if (null == messageHeaderParameters || null == payloadParameters || null==onboardingResponse) { throw new IllegalArgumentException("Parameters cannot be NULL"); } messageHeaderParameters.validate(); From 2c4e643fb22b21e912621268c3bc6db13b9ccdcb Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Wed, 8 Dec 2021 08:14:03 +0100 Subject: [PATCH 12/30] Rename method. --- .../test/messaging/rest/SendChunkedMessageTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index 1a454ff4..c59951a1 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -61,7 +61,7 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); PayloadParameters payloadParameters = new PayloadParameters(); - payloadParameters.setValue(fakeLargeMessageContent()); + payloadParameters.setValue(fakeMessageContentThatHasToBeChunked()); payloadParameters.setTypeUrl(SystemMessageType.EMPTY.getKey()); List tuples = @@ -126,7 +126,7 @@ void determineMaxChunkLengthForNonEncodedChunks() { * * @return - */ - private ByteString fakeLargeMessageContent() { + private ByteString fakeMessageContentThatHasToBeChunked() { return ByteString.copyFromUtf8( RandomStringUtils.randomAlphabetic(767997 * EXPECTED_NUMBER_OF_CHUNKS)); } From 5ed96663ebeb9d4da4fc36254668ff41ad87e42e Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Wed, 8 Dec 2021 08:20:57 +0100 Subject: [PATCH 13/30] Remove test case. Add constant. --- .../data/agrirouter/api/env/Environment.java | 19 ++++------ .../rest/SendChunkedMessageTest.java | 37 ++++++------------- 2 files changed, 18 insertions(+), 38 deletions(-) diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/env/Environment.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/env/Environment.java index a39a264f..a6df1edd 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/env/Environment.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/env/Environment.java @@ -5,9 +5,14 @@ /** Common Environment, holds some default methods pointing to the QA. */ public interface Environment { - String AGRIROUTER_LOGIN_URL = "/app"; - + /** + * Template for MQTT connections. + */ String MQTT_URL_TEMPLATE = "ssl://%s:%s"; + + /** + * Link template for the secured onboarding process. + */ String SECURED_ONBOARDING_AUTHORIZATION_LINK_TEMPLATE = "/application/%s/authorize?response_type=%s&state=%s&redirect_uri=%s"; @@ -68,16 +73,6 @@ default String getRevokeUrl() { return getRegistrationServiceUrl() + getApiPrefix() + "/registration/onboard/revoke"; } - /** - * Returning the URL to login into the AR. This is necessary, because there are services within - * the UI whiche are only avalailable if the user is logged in. - * - * @return - - */ - default String getAgrirouterLoginUrl() { - return this.getEnvironmentBaseUrl() + AGRIROUTER_LOGIN_URL; - } - /** * Returning the authorization URL for secured onboarding. * diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index c59951a1..621a4645 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -1,8 +1,5 @@ package com.dke.data.agrirouter.test.messaging.rest; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; - import agrirouter.request.Request; import com.dke.data.agrirouter.api.cancellation.DefaultCancellationToken; import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; @@ -11,6 +8,7 @@ import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; import com.dke.data.agrirouter.api.enums.ContentMessageType; import com.dke.data.agrirouter.api.enums.SystemMessageType; +import com.dke.data.agrirouter.api.env.Environment; import com.dke.data.agrirouter.api.service.messaging.encoding.DecodeMessageService; import com.dke.data.agrirouter.api.service.messaging.encoding.EncodeMessageService; import com.dke.data.agrirouter.api.service.messaging.http.FetchMessageService; @@ -28,20 +26,23 @@ import com.dke.data.agrirouter.test.Assertions; import com.dke.data.agrirouter.test.OnboardingResponseRepository; import com.google.protobuf.ByteString; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.Base64; import java.util.List; import java.util.Optional; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; + +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; +import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; /** Test case to show the behavior for chunked message sending. */ class SendChunkedMessageTest extends AbstractIntegrationTest { - public static final int EXPECTED_NUMBER_OF_CHUNKS = 3; + private static final int MAX_CHUNK_SIZE = 767997; + private static final int EXPECTED_NUMBER_OF_CHUNKS = 3; @Test void @@ -105,22 +106,6 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { }); } - @Test - void determineMaxChunkLengthForNonEncodedChunks() { - String base64EncodedString = - new String( - Base64.getEncoder() - .encode( - RandomStringUtils.randomAlphabetic(767997).getBytes(StandardCharsets.UTF_8))); - Assertions.assertTrue(base64EncodedString.length() < 1024000); - base64EncodedString = - new String( - Base64.getEncoder() - .encode( - RandomStringUtils.randomAlphabetic(767998).getBytes(StandardCharsets.UTF_8))); - Assertions.assertFalse(base64EncodedString.length() < 1024000); - } - /** * Delivers fake message content for three chunks. * @@ -128,6 +113,6 @@ void determineMaxChunkLengthForNonEncodedChunks() { */ private ByteString fakeMessageContentThatHasToBeChunked() { return ByteString.copyFromUtf8( - RandomStringUtils.randomAlphabetic(767997 * EXPECTED_NUMBER_OF_CHUNKS)); + RandomStringUtils.randomAlphabetic(MAX_CHUNK_SIZE * EXPECTED_NUMBER_OF_CHUNKS)); } } From bd774f9ee70dd12eb47a246b76ae54733d989c43 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Fri, 10 Dec 2021 08:28:18 +0100 Subject: [PATCH 14/30] Adapt the chunking - chunk raw content and then Base64 each chunk of it. Stabilize the test cases by adding more wait time. Check the size of each chunk after chunking them. --- .../data/agrirouter/api/env/Environment.java | 8 +- .../api/service/ParameterValidation.java | 145 +++++++++--------- .../service/parameters/PayloadParameters.kt | 7 +- .../encoding/EncodeMessageServiceImpl.java | 7 +- .../impl/messaging/rest/MessageFetcher.java | 8 + .../test/AbstractIntegrationTest.java | 25 ++- .../rest/CancellationTokenPollingTest.java | 9 +- .../rest/CloudOffboardingServiceTest.java | 35 ++++- .../rest/CloudOnboardingServiceTest.java | 35 ++++- .../rest/SendChunkedMessageTest.java | 29 ++-- .../rest/SetCapabilityServiceTest.java | 6 +- .../rest/SetSubscriptionServiceTest.java | 6 +- 12 files changed, 191 insertions(+), 129 deletions(-) diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/env/Environment.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/env/Environment.java index a6df1edd..997a2ff1 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/env/Environment.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/env/Environment.java @@ -5,14 +5,10 @@ /** Common Environment, holds some default methods pointing to the QA. */ public interface Environment { - /** - * Template for MQTT connections. - */ + /** Template for MQTT connections. */ String MQTT_URL_TEMPLATE = "ssl://%s:%s"; - /** - * Link template for the secured onboarding process. - */ + /** Link template for the secured onboarding process. */ String SECURED_ONBOARDING_AUTHORIZATION_LINK_TEMPLATE = "/application/%s/authorize?response_type=%s&state=%s&redirect_uri=%s"; diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java index 0999c12c..67798f88 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/ParameterValidation.java @@ -1,96 +1,89 @@ package com.dke.data.agrirouter.api.service; import com.dke.data.agrirouter.api.exception.IllegalParameterDefinitionException; +import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Collection; - -/** - * Parameter validation using bean validation. - */ +/** Parameter validation using bean validation. */ public interface ParameterValidation { - Logger LOGGER = LogManager.getLogger(); + Logger LOGGER = LogManager.getLogger(); - /** - * Validation of the parameters. If there are any constraint violations, there will be a - * exception. - * - * @throws IllegalParameterDefinitionException - - */ - default void validate() { - LOGGER.debug("Validating parameters."); - LOGGER.trace("Technical validation."); - this.technicalValidation(); - LOGGER.trace("Business validation."); - this.businessValidation(); - } + /** + * Validation of the parameters. If there are any constraint violations, there will be a + * exception. + * + * @throws IllegalParameterDefinitionException - + */ + default void validate() { + LOGGER.debug("Validating parameters."); + LOGGER.trace("Technical validation."); + this.technicalValidation(); + LOGGER.trace("Business validation."); + this.businessValidation(); + } - /** - * Technical validation. Empty by default. - */ - default void technicalValidation() { - // - } + /** Technical validation. Empty by default. */ + default void technicalValidation() { + // + } - /** - * Business validation. Empty by default. - */ - default void businessValidation() { - // - } + /** Business validation. Empty by default. */ + default void businessValidation() { + // + } - /** - * Rise an exception if there has to be at least one valid parameter. - * - * @param message - - */ - default void rise(String message, String... parameterNames) { - final String joinedParametersNames = String.join(",", parameterNames); - throw new IllegalParameterDefinitionException( - String.format( - "At least one of the following parameters has to be defined [%s] was not defined correctly, please check the values. '%s'.", - joinedParametersNames, message)); - } + /** + * Rise an exception if there has to be at least one valid parameter. + * + * @param message - + */ + default void rise(String message, String... parameterNames) { + final String joinedParametersNames = String.join(",", parameterNames); + throw new IllegalParameterDefinitionException( + String.format( + "At least one of the following parameters has to be defined [%s] was not defined correctly, please check the values. '%s'.", + joinedParametersNames, message)); + } - /** - * Check for null values. - * - * @param o - - */ - default void nullCheck(String parameterName, Object o) { - if (null == o) { - this.rise( - "The parameter '%s' should not have been null, please check your values.", parameterName); - } + /** + * Check for null values. + * + * @param o - + */ + default void nullCheck(String parameterName, Object o) { + if (null == o) { + this.rise( + "The parameter '%s' should not have been null, please check your values.", parameterName); } + } - /** - * Check for null or empty values. - * - * @param s - - */ - default void isBlank(String parameterName, String s) { - if (StringUtils.isBlank(s)) { - this.rise( - "The parameter '%s' should not have been blank, please check your values.", - parameterName); - } + /** + * Check for null or empty values. + * + * @param s - + */ + default void isBlank(String parameterName, String s) { + if (StringUtils.isBlank(s)) { + this.rise( + "The parameter '%s' should not have been blank, please check your values.", + parameterName); } + } - /** - * Check for null or empty values. - * - * @param c - - */ - default void nullOrEmpty(String parameterName, Collection c) { - nullCheck(parameterName, c); - if (c.isEmpty()) { - this.rise( - "The parameter '%s' should not have been empty, please check your values.", - parameterName); - } + /** + * Check for null or empty values. + * + * @param c - + */ + default void nullOrEmpty(String parameterName, Collection c) { + nullCheck(parameterName, c); + if (c.isEmpty()) { + this.rise( + "The parameter '%s' should not have been empty, please check your values.", + parameterName); } + } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index 14eba92a..76c19eee 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -7,12 +7,13 @@ import com.google.protobuf.ByteString /** * Every endpoint can send message based on their capabilities. The size of a message is however limited. A message contains 2 parts: Header and Body. The limitation of a message is defined as follows: * - Body size is equivalent of 1024000 characters/signs + * - Since the chunking is performed on the raw message data this means, that we have to lower the MAX_LENGTH_FOR_MESSAGES to allow Base64 encoding afterwards. * - Total message size is limited to 1468000 characters/signs * - Message that are above this limit will be rejected. * The AR will return an error indicated that the message size is above the limit. * If the message size is above 5 MB the AR will not return any error. In order to send messages with sizes above threshold, these messages must be split into chunks with the above limit. */ -const val MAX_LENGTH_FOR_MESSAGES = 1024000 +const val MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT = 767997 /** * Parameters class. Encapsulation for the services. @@ -32,14 +33,14 @@ class PayloadParameters : AbstractParameterBase(), ParameterValidation { * Determining whether the message should be chunked. */ fun shouldBeChunked(): Boolean { - return value!!.toStringUtf8().length > MAX_LENGTH_FOR_MESSAGES + return value!!.toStringUtf8().length > MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT } /** * The maximum length for messages / the payload. */ fun maxLengthForMessages(): Int { - return MAX_LENGTH_FOR_MESSAGES + return MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT } fun copyFrom(payload: PayloadParameters) { diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index 46192441..eb4a750e 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -18,6 +18,7 @@ import com.google.protobuf.ByteString; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Base64; import java.util.Collections; @@ -96,7 +97,9 @@ public List chunk( OnboardingResponse onboardingResponse) { logMethodBegin(messageHeaderParameters, payloadParameters); - if (null == messageHeaderParameters || null == payloadParameters || null==onboardingResponse) { + if (null == messageHeaderParameters + || null == payloadParameters + || null == onboardingResponse) { throw new IllegalArgumentException("Parameters cannot be NULL"); } messageHeaderParameters.validate(); @@ -134,7 +137,7 @@ public List chunk( final PayloadParameters payload = new PayloadParameters(); payload.copyFrom(payloadParameters); - payload.setValue(ByteString.copyFromUtf8(chunk)); + payload.setValue(ByteString.copyFromUtf8(Base64.getEncoder().encodeToString(chunk.getBytes(StandardCharsets.UTF_8)))); tuples.add(new MessageParameterTuple(header, payload)); diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/rest/MessageFetcher.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/rest/MessageFetcher.java index 14ee435d..bad0eb38 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/rest/MessageFetcher.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/rest/MessageFetcher.java @@ -9,10 +9,14 @@ import java.util.Optional; import javax.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** Interface to fetch messages for the HTTP implementation by polling the outbox. */ public interface MessageFetcher extends ResponseValidator { + Logger LOGGER = LogManager.getLogger(); + int MAX_TRIES_BEFORE_FAILURE = 10; long DEFAULT_INTERVAL = 500; @@ -28,7 +32,11 @@ public interface MessageFetcher extends ResponseValidator { default Optional poll( FetchMessageParameters fetchMessageParameters, CancellationToken cancellationToken) { fetchMessageParameters.validate(); + int nrOfTries = 1; while (cancellationToken.isNotCancelled()) { + LOGGER.debug( + "The cancellation token is not cancelled, we have another try. This is try number {}.", + nrOfTries); Response response = RequestFactory.securedRequest( Objects.requireNonNull(fetchMessageParameters.getOnboardingResponse()) diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java index 5975367b..3b3b39a2 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java @@ -7,12 +7,31 @@ public abstract class AbstractIntegrationTest { /** - * Wait for the AR to process the messages. This can take up to 10 seconds. + * Since there are multiple problems with the stability of the QA env we define dedicated + * constants for message fetching. + */ + protected int MAX_TRIES_BEFORE_FAILURE = 10; + + protected long DEFAULT_INTERVAL = 5000; + + /** + * Wait for the AR to process the messages. Since the QA has some stability problems, + * we will wait up to 30 seconds until the AR has processed the messages. + * + * @throws InterruptedException - + */ + protected void waitForTheAgrirouterToProcessSingleMessage() throws InterruptedException { + Thread.sleep(TimeUnit.SECONDS.toMillis(30)); + } + + /** + * Wait for the AR to process the messages. Since the QA has some stability problems, + * we will wait up to one minute until the AR has processed the messages. * * @throws InterruptedException - */ - protected void waitForTheAgrirouterToProcessTheMessages() throws InterruptedException { - Thread.sleep(TimeUnit.SECONDS.toMillis(10)); + protected void waitForTheAgrirouterToProcessMultipleMessages() throws InterruptedException { + Thread.sleep(TimeUnit.SECONDS.toMillis(60)); } /** Farming software for integration testing. */ diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CancellationTokenPollingTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CancellationTokenPollingTest.java index 96a1da89..353aec8e 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CancellationTokenPollingTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CancellationTokenPollingTest.java @@ -1,8 +1,5 @@ package com.dke.data.agrirouter.test.messaging.rest; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; - import com.dke.data.agrirouter.api.cancellation.CancellationToken; import com.dke.data.agrirouter.api.cancellation.DefaultCancellationToken; import com.dke.data.agrirouter.api.dto.messaging.FetchMessageResponse; @@ -22,7 +19,7 @@ class CancellationTokenPollingTest extends AbstractIntegrationTest { @Test - @Timeout(value = 15) + @Timeout(15) @SuppressWarnings("deprecation") void givenExistingImplementationOfTheCancellationTokenWhenPollingMessagesThenTheDefaultParametersShouldStillInfluenceTheBehavior() @@ -38,7 +35,7 @@ class CancellationTokenPollingTest extends AbstractIntegrationTest { } @Test - @Timeout(value = 1) + @Timeout(1) @SuppressWarnings("deprecation") void givenExistingImplementationOfTheCancellationTokenWhenPollingMessagesThenTheCustomParametersShouldStillInfluenceTheBehavior() @@ -54,7 +51,7 @@ class CancellationTokenPollingTest extends AbstractIntegrationTest { } @Test - @Timeout(15) + @Timeout(55) void givenDefaultImplementationOfTheCancellationTokenWhenPollingMessagesThenTheCancellationTokenShouldInfluenceTheBehavior() throws IOException { diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOffboardingServiceTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOffboardingServiceTest.java index 7939f82f..e8bd978a 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOffboardingServiceTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOffboardingServiceTest.java @@ -1,7 +1,5 @@ package com.dke.data.agrirouter.test.messaging.rest; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; import static com.dke.data.agrirouter.test.OnboardingResponseRepository.Identifier; import static com.dke.data.agrirouter.test.OnboardingResponseRepository.read; @@ -29,6 +27,7 @@ import java.util.Optional; import org.apache.http.HttpStatus; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class CloudOffboardingServiceTest extends AbstractIntegrationTest { @@ -36,10 +35,19 @@ class CloudOffboardingServiceTest extends AbstractIntegrationTest { public static final String EXTERNAL_ID = "8c31e156-3c29-4b46-863c-5e49b405b344"; public static final String ENDPOINT_NAME = "CLOUD-OFFBOARDING-SERVICE-TEST"; + /** + * The endpoint ID for a former test run. If the endpoint is still available, then there could be + * a chance to remove it. This has to be set manually. + */ + public static final String VCU_ENDPOINT_ID_FOR_FORMER_TEST_RUN = + "bbfdcaed-9228-42a9-ab51-9e226cb314d1"; + private OnboardingResponse virtualCommunicationUnit; @BeforeEach void onboardVirtualCu() throws Throwable { + offboardExistingVirtualCUFromFormerTestRun(); + CloudOnboardingService cloudOnboardingService = new CloudOnboardingServiceImpl(); CloudOnboardingParameters parameters = new CloudOnboardingParameters(); CloudOnboardingParameters.EndpointDetailsParameters endpointDetails = @@ -50,7 +58,7 @@ void onboardVirtualCu() throws Throwable { parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); cloudOnboardingService.send(parameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessSingleMessage(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -90,7 +98,26 @@ void onboardVirtualCu() throws Throwable { Assertions.assertNotNull(virtualCommunicationUnit.getConnectionCriteria()); } + private void offboardExistingVirtualCUFromFormerTestRun() throws Throwable { + CloudOffboardingService cloudOffboardingService = new CloudOffboardingServiceImpl(); + CloudOffboardingParameters parameters = new CloudOffboardingParameters(); + parameters.setEndpointIds(Collections.singletonList(VCU_ENDPOINT_ID_FOR_FORMER_TEST_RUN)); + parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); + cloudOffboardingService.send(parameters); + FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); + Optional> fetchMessageResponses = + fetchMessageService.fetch( + read(Identifier.TELEMETRY_PLATFORM), + new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); + waitForTheAgrirouterToProcessSingleMessage(); + Assertions.assertTrue(fetchMessageResponses.isPresent()); + Assertions.assertEquals(1, fetchMessageResponses.get().size()); + Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); + } + @Test + @Disabled( + "Since there are multiple problems with the environment, the test is currently disabled.") void givenValidSensorIdWhenOffboardingVirtualCuThenTheOffbardingShouldBeSuccessful() throws Throwable { CloudOffboardingService cloudOffboardingService = new CloudOffboardingServiceImpl(); @@ -100,7 +127,7 @@ void givenValidSensorIdWhenOffboardingVirtualCuThenTheOffbardingShouldBeSuccessf parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); cloudOffboardingService.send(parameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessSingleMessage(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOnboardingServiceTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOnboardingServiceTest.java index 419278b0..fafe984f 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOnboardingServiceTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CloudOnboardingServiceTest.java @@ -1,7 +1,5 @@ package com.dke.data.agrirouter.test.messaging.rest; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; import static com.dke.data.agrirouter.test.OnboardingResponseRepository.Identifier; import static com.dke.data.agrirouter.test.OnboardingResponseRepository.read; @@ -29,6 +27,7 @@ import java.util.Optional; import org.apache.http.HttpStatus; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class CloudOnboardingServiceTest extends AbstractIntegrationTest { @@ -36,10 +35,19 @@ class CloudOnboardingServiceTest extends AbstractIntegrationTest { public static final String EXTERNAL_ID = "8c31e156-3c29-4b46-863c-5e49b405b343"; public static final String ENDPOINT_NAME = "CLOUD-ONBOARDING-SERVICE-TEST"; + /** + * The endpoint ID for a former test run. If the endpoint is still available, then there could be + * a chance to remove it. This has to be set manually. + */ + public static final String VCU_ENDPOINT_ID_FOR_FORMER_TEST_RUN = + "4e3af0ec-efb1-4109-8d3d-1910094c09d9"; + private OnboardingResponse virtualCommunicationUnit; @AfterEach void offboardVirtualCu() throws Throwable { + offboardExistingVirtualCUFromFormerTestRun(); + CloudOffboardingService cloudOffboardingService = new CloudOffboardingServiceImpl(); CloudOffboardingParameters parameters = new CloudOffboardingParameters(); parameters.setEndpointIds( @@ -47,7 +55,7 @@ void offboardVirtualCu() throws Throwable { parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); cloudOffboardingService.send(parameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessSingleMessage(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -70,7 +78,26 @@ void offboardVirtualCu() throws Throwable { decodeMessageResponse.getResponseEnvelope().getResponseCode()); } + private void offboardExistingVirtualCUFromFormerTestRun() throws Throwable { + CloudOffboardingService cloudOffboardingService = new CloudOffboardingServiceImpl(); + CloudOffboardingParameters parameters = new CloudOffboardingParameters(); + parameters.setEndpointIds(Collections.singletonList(VCU_ENDPOINT_ID_FOR_FORMER_TEST_RUN)); + parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); + cloudOffboardingService.send(parameters); + FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); + Optional> fetchMessageResponses = + fetchMessageService.fetch( + read(Identifier.TELEMETRY_PLATFORM), + new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); + waitForTheAgrirouterToProcessSingleMessage(); + Assertions.assertTrue(fetchMessageResponses.isPresent()); + Assertions.assertEquals(1, fetchMessageResponses.get().size()); + Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); + } + @Test + @Disabled( + "Since there are multiple problems with the environment, the test is currently disabled.") void givenValidIdAndNameWhenOnboardingVirtualCuThenTheOnbardingShouldBePossible() throws Throwable { CloudOnboardingService cloudOnboardingService = new CloudOnboardingServiceImpl(); @@ -83,7 +110,7 @@ void givenValidIdAndNameWhenOnboardingVirtualCuThenTheOnbardingShouldBePossible( parameters.setOnboardingResponse(read(Identifier.TELEMETRY_PLATFORM)); cloudOnboardingService.send(parameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessSingleMessage(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index 621a4645..b17631b3 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -8,14 +8,10 @@ import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; import com.dke.data.agrirouter.api.enums.ContentMessageType; import com.dke.data.agrirouter.api.enums.SystemMessageType; -import com.dke.data.agrirouter.api.env.Environment; import com.dke.data.agrirouter.api.service.messaging.encoding.DecodeMessageService; import com.dke.data.agrirouter.api.service.messaging.encoding.EncodeMessageService; import com.dke.data.agrirouter.api.service.messaging.http.FetchMessageService; -import com.dke.data.agrirouter.api.service.parameters.MessageHeaderParameters; -import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; -import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; -import com.dke.data.agrirouter.api.service.parameters.SendMessageParameters; +import com.dke.data.agrirouter.api.service.parameters.*; import com.dke.data.agrirouter.impl.common.MessageIdService; import com.dke.data.agrirouter.impl.messaging.SequenceNumberService; import com.dke.data.agrirouter.impl.messaging.encoding.DecodeMessageServiceImpl; @@ -26,22 +22,19 @@ import com.dke.data.agrirouter.test.Assertions; import com.dke.data.agrirouter.test.OnboardingResponseRepository; import com.google.protobuf.ByteString; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; - import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Optional; - -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; /** Test case to show the behavior for chunked message sending. */ class SendChunkedMessageTest extends AbstractIntegrationTest { - private static final int MAX_CHUNK_SIZE = 767997; + private static final int MAX_CHUNK_SIZE = 1024000; private static final int EXPECTED_NUMBER_OF_CHUNKS = 3; @Test @@ -62,12 +55,14 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); PayloadParameters payloadParameters = new PayloadParameters(); - payloadParameters.setValue(fakeMessageContentThatHasToBeChunked()); + payloadParameters.setValue(fakeRawMessageContentThatHasToBeChunked()); payloadParameters.setTypeUrl(SystemMessageType.EMPTY.getKey()); List tuples = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); + tuples.forEach(messageParameterTuple -> Assertions.assertTrue(Objects.requireNonNull(messageParameterTuple.getPayloadParameters().getValue()).toStringUtf8().length() encodedMessages = encodeMessageService.encode(tuples); SendMessageParameters sendMessageParameters = new SendMessageParameters(); @@ -75,7 +70,7 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { sendMessageParameters.setOnboardingResponse(onboardingResponse); sendMessageService.send(sendMessageParameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessMultipleMessages(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -111,8 +106,8 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { * * @return - */ - private ByteString fakeMessageContentThatHasToBeChunked() { + private ByteString fakeRawMessageContentThatHasToBeChunked() { return ByteString.copyFromUtf8( - RandomStringUtils.randomAlphabetic(MAX_CHUNK_SIZE * EXPECTED_NUMBER_OF_CHUNKS)); + RandomStringUtils.randomAlphabetic(PayloadParametersKt.MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT * EXPECTED_NUMBER_OF_CHUNKS)); } } diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetCapabilityServiceTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetCapabilityServiceTest.java index 96886c22..befabfee 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetCapabilityServiceTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetCapabilityServiceTest.java @@ -1,7 +1,5 @@ package com.dke.data.agrirouter.test.messaging.rest; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; import static com.dke.data.agrirouter.test.OnboardingResponseRepository.Identifier; import static com.dke.data.agrirouter.test.OnboardingResponseRepository.read; @@ -53,7 +51,7 @@ void givenValidEndpointWhenSendingCapabilitiesTheCapabilityMessageShouldBeAccept parameters.setCapabilitiesParameters(capabilities); setCapabilityService.send(parameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessSingleMessage(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -96,7 +94,7 @@ void givenValidEndpointWhenSendingInvalidCapabilitiesTheCapabilityMessageShouldN parameters.setCapabilitiesParameters(capabilities); setCapabilityService.send(parameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessSingleMessage(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetSubscriptionServiceTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetSubscriptionServiceTest.java index 86647332..bba8e7d0 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetSubscriptionServiceTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SetSubscriptionServiceTest.java @@ -1,7 +1,5 @@ package com.dke.data.agrirouter.test.messaging.rest; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.DEFAULT_INTERVAL; -import static com.dke.data.agrirouter.impl.messaging.rest.MessageFetcher.MAX_TRIES_BEFORE_FAILURE; import static com.dke.data.agrirouter.test.OnboardingResponseRepository.Identifier; import static com.dke.data.agrirouter.test.OnboardingResponseRepository.read; @@ -47,7 +45,7 @@ void givenValidEndpointWhenSendingSubscriptionsTheSubscriptionMessageShouldBeAcc parameters.setSubscriptions(subscriptions); setSubscriptionService.send(parameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessSingleMessage(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = @@ -85,7 +83,7 @@ void givenValidEndpointWhenSendingInvalidSubscriptionsTheSubscriptionMessageShou parameters.setSubscriptions(subscriptions); setSubscriptionService.send(parameters); - waitForTheAgrirouterToProcessTheMessages(); + waitForTheAgrirouterToProcessSingleMessage(); FetchMessageService fetchMessageService = new FetchMessageServiceImpl(); Optional> fetchMessageResponses = From 2c5811f46b6d31aeecf705c5d0c5178d6392c5ac Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Fri, 10 Dec 2021 08:51:41 +0100 Subject: [PATCH 15/30] Format. --- .../messaging/encoding/EncodeMessageServiceImpl.java | 4 +++- .../encoding/EncodeMessageServiceImplTest.java | 6 +++++- .../data/agrirouter/test/AbstractIntegrationTest.java | 8 ++++---- .../test/messaging/rest/SendChunkedMessageTest.java | 11 +++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index eb4a750e..b7a2444b 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -137,7 +137,9 @@ public List chunk( final PayloadParameters payload = new PayloadParameters(); payload.copyFrom(payloadParameters); - payload.setValue(ByteString.copyFromUtf8(Base64.getEncoder().encodeToString(chunk.getBytes(StandardCharsets.UTF_8)))); + payload.setValue( + ByteString.copyFromUtf8( + Base64.getEncoder().encodeToString(chunk.getBytes(StandardCharsets.UTF_8)))); tuples.add(new MessageParameterTuple(header, payload)); diff --git a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java index b828c0b5..a250ca07 100644 --- a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java +++ b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java @@ -12,6 +12,7 @@ import com.dke.data.agrirouter.api.service.parameters.MessageHeaderParameters; import com.dke.data.agrirouter.api.service.parameters.MessageParameterTuple; import com.dke.data.agrirouter.api.service.parameters.PayloadParameters; +import com.dke.data.agrirouter.api.service.parameters.PayloadParametersKt; import com.google.protobuf.ByteString; import java.util.List; import org.apache.commons.lang3.RandomStringUtils; @@ -56,7 +57,10 @@ void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumbe givenSingleChunkMessageWithMaxSizeWhenChunkingThenTheImplementationShouldReturnTheRightNumberOfChunks() { EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); - ByteString toSendMessage = ByteString.copyFromUtf8(RandomStringUtils.randomAlphabetic(1024000)); + ByteString toSendMessage = + ByteString.copyFromUtf8( + RandomStringUtils.randomAlphabetic( + PayloadParametersKt.MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT)); MessageHeaderParameters messageHeaderParameters = getMessageHeaderParameters(); messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP); PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java index 3b3b39a2..deda31e1 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/AbstractIntegrationTest.java @@ -15,8 +15,8 @@ public abstract class AbstractIntegrationTest { protected long DEFAULT_INTERVAL = 5000; /** - * Wait for the AR to process the messages. Since the QA has some stability problems, - * we will wait up to 30 seconds until the AR has processed the messages. + * Wait for the AR to process the messages. Since the QA has some stability problems, we will wait + * up to 30 seconds until the AR has processed the messages. * * @throws InterruptedException - */ @@ -25,8 +25,8 @@ protected void waitForTheAgrirouterToProcessSingleMessage() throws InterruptedEx } /** - * Wait for the AR to process the messages. Since the QA has some stability problems, - * we will wait up to one minute until the AR has processed the messages. + * Wait for the AR to process the messages. Since the QA has some stability problems, we will wait + * up to one minute until the AR has processed the messages. * * @throws InterruptedException - */ diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index b17631b3..bde47ad7 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -61,7 +61,13 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { List tuples = encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); - tuples.forEach(messageParameterTuple -> Assertions.assertTrue(Objects.requireNonNull(messageParameterTuple.getPayloadParameters().getValue()).toStringUtf8().length() + Assertions.assertTrue( + Objects.requireNonNull(messageParameterTuple.getPayloadParameters().getValue()) + .toStringUtf8() + .length() + < MAX_CHUNK_SIZE)); List encodedMessages = encodeMessageService.encode(tuples); @@ -108,6 +114,7 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { */ private ByteString fakeRawMessageContentThatHasToBeChunked() { return ByteString.copyFromUtf8( - RandomStringUtils.randomAlphabetic(PayloadParametersKt.MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT * EXPECTED_NUMBER_OF_CHUNKS)); + RandomStringUtils.randomAlphabetic( + PayloadParametersKt.MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT * EXPECTED_NUMBER_OF_CHUNKS)); } } From 0f7b61a27f978e8cce9cbfe207748bb8ec39757e Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Fri, 10 Dec 2021 08:53:33 +0100 Subject: [PATCH 16/30] Adapt comment. --- .../api/service/messaging/encoding/EncodeMessageService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java index b0f2b469..eb257e19 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java @@ -28,8 +28,8 @@ String encode( List encode(List messageParameterTuples); /** - * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are - * not longer in control of the application. + * Chunk a raw message if necessary. The chunk information and all IDs will be set by the SDK and are + * no longer in control of the application. * * @param messageHeaderParameters - * @param payloadParameters - From 84dfb40aa15e1e3a88043591a0783dbabb207d59 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Tue, 14 Dec 2021 12:26:13 +0100 Subject: [PATCH 17/30] Bump log4j version to fix vulnerability. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c6ee4167..dd317295 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ 2.38.0 1.1.1 1.3.71 - 2.13.3 + 2.16.0 1.2.4 1.14 2.1.1 From 1ebfd5219cacc0dee63e5f7f8c304ea1e0afabbd Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Tue, 14 Dec 2021 17:43:38 +0100 Subject: [PATCH 18/30] Update timeout due to some network problems. --- .../test/messaging/rest/CancellationTokenPollingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CancellationTokenPollingTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CancellationTokenPollingTest.java index 353aec8e..aec60d98 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CancellationTokenPollingTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/CancellationTokenPollingTest.java @@ -51,7 +51,7 @@ class CancellationTokenPollingTest extends AbstractIntegrationTest { } @Test - @Timeout(55) + @Timeout(60) void givenDefaultImplementationOfTheCancellationTokenWhenPollingMessagesThenTheCancellationTokenShouldInfluenceTheBehavior() throws IOException { From ab97a4aecc74b902ccb15bb89a132adcd4a6c167 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Thu, 16 Dec 2021 16:08:08 +0100 Subject: [PATCH 19/30] Fix typo. --- .../data/agrirouter/api/service/parameters/PayloadParameters.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index 76c19eee..553b1b30 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -5,7 +5,7 @@ import com.dke.data.agrirouter.api.service.parameters.base.AbstractParameterBase import com.google.protobuf.ByteString /** - * Every endpoint can send message based on their capabilities. The size of a message is however limited. A message contains 2 parts: Header and Body. The limitation of a message is defined as follows: + * Every endpoint can send message based on its capabilities. The size of a message is however limited. A message contains 2 parts: Header and Body. The limitation of a message is defined as follows: * - Body size is equivalent of 1024000 characters/signs * - Since the chunking is performed on the raw message data this means, that we have to lower the MAX_LENGTH_FOR_MESSAGES to allow Base64 encoding afterwards. * - Total message size is limited to 1468000 characters/signs From 5cc596a3a1fb52f04ac24bf0a457a0dcfcb6a246 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Thu, 16 Dec 2021 16:08:49 +0100 Subject: [PATCH 20/30] Fix typo. --- .../data/agrirouter/api/service/parameters/PayloadParameters.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index 553b1b30..91b9af79 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -6,7 +6,7 @@ import com.google.protobuf.ByteString /** * Every endpoint can send message based on its capabilities. The size of a message is however limited. A message contains 2 parts: Header and Body. The limitation of a message is defined as follows: - * - Body size is equivalent of 1024000 characters/signs + * - The maximum body size is equivalent of 1024000 characters/signs * - Since the chunking is performed on the raw message data this means, that we have to lower the MAX_LENGTH_FOR_MESSAGES to allow Base64 encoding afterwards. * - Total message size is limited to 1468000 characters/signs * - Message that are above this limit will be rejected. From 9e5bb337f87da81ed4bf563d2373f9dbc42a52f1 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Thu, 16 Dec 2021 16:09:16 +0100 Subject: [PATCH 21/30] Fix typo. --- .../data/agrirouter/api/service/parameters/PayloadParameters.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index 91b9af79..6ba24f9d 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -9,7 +9,7 @@ import com.google.protobuf.ByteString * - The maximum body size is equivalent of 1024000 characters/signs * - Since the chunking is performed on the raw message data this means, that we have to lower the MAX_LENGTH_FOR_MESSAGES to allow Base64 encoding afterwards. * - Total message size is limited to 1468000 characters/signs - * - Message that are above this limit will be rejected. + * - Messages that are above this limit will be rejected. * The AR will return an error indicated that the message size is above the limit. * If the message size is above 5 MB the AR will not return any error. In order to send messages with sizes above threshold, these messages must be split into chunks with the above limit. */ From fd22398f41cbad641c41b8b1c5ddcd54747866b3 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Thu, 16 Dec 2021 16:10:11 +0100 Subject: [PATCH 22/30] Fix typo. --- .../data/agrirouter/api/service/parameters/PayloadParameters.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index 6ba24f9d..a6b3ea6f 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -10,7 +10,7 @@ import com.google.protobuf.ByteString * - Since the chunking is performed on the raw message data this means, that we have to lower the MAX_LENGTH_FOR_MESSAGES to allow Base64 encoding afterwards. * - Total message size is limited to 1468000 characters/signs * - Messages that are above this limit will be rejected. - * The AR will return an error indicated that the message size is above the limit. + * The AR will return an error indicating that the message size is above the limit. * If the message size is above 5 MB the AR will not return any error. In order to send messages with sizes above threshold, these messages must be split into chunks with the above limit. */ const val MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT = 767997 From 94be681b795e983f467b8f44e7ac5d8ecb95a0cc Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Thu, 16 Dec 2021 16:11:32 +0100 Subject: [PATCH 23/30] Adapt comment. --- .../impl/messaging/encoding/EncodeMessageServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index b7a2444b..2ddd8e40 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -88,7 +88,7 @@ public List encode(List messageParameterTuples) { * not longer in control of the application. * * @param messageHeaderParameters - - * @param payloadParameters - + * @param payloadParameters Content of the message. It shall not be Base64 encoded before. * @return - */ public List chunk( From 1b30cf68f1e2ac44778f710a9e3e029852b11e7a Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Thu, 16 Dec 2021 17:40:26 +0100 Subject: [PATCH 24/30] Adapt test cases and fix encoding mechanism. --- .../encoding/EncodeMessageService.java | 6 +- .../api/enums/ContentMessageType.kt | 31 ++-- .../agrirouter/api/enums/SystemMessageType.kt | 4 + .../api/enums/TechnicalMessageType.kt | 6 + .../encoding/EncodeMessageServiceImpl.java | 135 +++++++++++------- .../EncodeMessageServiceImplTest.java | 8 +- agrirouter-sdk-java-tests/pom.xml | 4 + .../rest/SendChunkedMessageTest.java | 68 +++++---- pom.xml | 6 + 9 files changed, 171 insertions(+), 97 deletions(-) diff --git a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java index eb257e19..b3c44f34 100644 --- a/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java +++ b/agrirouter-sdk-java-api/src/main/java/com/dke/data/agrirouter/api/service/messaging/encoding/EncodeMessageService.java @@ -28,14 +28,14 @@ String encode( List encode(List messageParameterTuples); /** - * Chunk a raw message if necessary. The chunk information and all IDs will be set by the SDK and are - * no longer in control of the application. + * Chunk a raw message if necessary. The chunk information and all IDs will be set by the SDK and + * are no longer in control of the application. * * @param messageHeaderParameters - * @param payloadParameters - * @return - */ - List chunk( + List chunkAndEncode( MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters, OnboardingResponse onboardingResponse); diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/ContentMessageType.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/ContentMessageType.kt index eff8a1f6..b937f4b8 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/ContentMessageType.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/ContentMessageType.kt @@ -6,21 +6,21 @@ import agrirouter.technicalmessagetype.Gps * Enum containing all the content message types the AR is supporting. */ @Suppress("unused") -enum class ContentMessageType(private val key: String, private val typeUrl: String, private val needsChunkinng: Boolean) : TechnicalMessageType { - ISO_11783_TASKDATA_ZIP("iso:11783:-10:taskdata:zip", "", true), - SHP_SHAPE_ZIP("shp:shape:zip", "", true), - DOC_PDF("doc:pdf", "", true), - IMG_JPEG("img:jpeg", "", true), - IMG_PNG("img:png", "", true), - IMG_BMP("img:bmp", "", true), - VID_AVI("vid:avi", "", true), - VID_MP4("vid:mp4", "", true), - VID_WMV("vid:wmv", "", true), - GPS_INFO("gps:info", Gps.GPSList.getDescriptor().fullName, false), +enum class ContentMessageType(private val key: String, private val typeUrl: String, private val needsChunkinng: Boolean, private val needsBase64Encoding: Boolean) : TechnicalMessageType { + ISO_11783_TASKDATA_ZIP("iso:11783:-10:taskdata:zip", "", true, true), + SHP_SHAPE_ZIP("shp:shape:zip", "", true, true), + DOC_PDF("doc:pdf", "", true, true), + IMG_JPEG("img:jpeg", "", true, true), + IMG_PNG("img:png", "", true, true), + IMG_BMP("img:bmp", "", true, true), + VID_AVI("vid:avi", "", true, true), + VID_MP4("vid:mp4", "", true, true), + VID_WMV("vid:wmv", "", true, true), + GPS_INFO("gps:info", Gps.GPSList.getDescriptor().fullName, false, false), //FIXME Since the spec is not public, we can only use those literals. - ISO_11783_DEVICE_DESCRIPTION("iso:11783:-10:device_description:protobuf", "types.agrirouter.com\\efdi.ISO11783_TaskData", false), - ISO_11783_TIME_LOG("iso:11783:-10:time_log:protobuf", "types.agrirouter.com\\efdi.TimeLog", false); + ISO_11783_DEVICE_DESCRIPTION("iso:11783:-10:device_description:protobuf", "types.agrirouter.com\\efdi.ISO11783_TaskData", false, false), + ISO_11783_TIME_LOG("iso:11783:-10:time_log:protobuf", "types.agrirouter.com\\efdi.TimeLog", false, false); override fun getKey(): String { return key @@ -33,4 +33,9 @@ enum class ContentMessageType(private val key: String, private val typeUrl: Stri override fun getNeedsChunking(): Boolean { return needsChunkinng } + + override fun getNeedsBase64Encoding(): Boolean { + return needsBase64Encoding + } + } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/SystemMessageType.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/SystemMessageType.kt index 6521e5b3..b6abe0a5 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/SystemMessageType.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/SystemMessageType.kt @@ -34,4 +34,8 @@ enum class SystemMessageType(private val key: String, private val typeUrl: Strin override fun getNeedsChunking(): Boolean { return false } + + override fun getNeedsBase64Encoding(): Boolean { + return false + } } diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt index 4648b635..181139b6 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/enums/TechnicalMessageType.kt @@ -19,4 +19,10 @@ interface TechnicalMessageType { * Indicates whether the technical message type needs chunking or not. */ fun getNeedsChunking(): Boolean + + /** + * Indicates whether the technical message type needs base64 encoding or not. + */ + fun getNeedsBase64Encoding(): Boolean; + } diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index 2ddd8e40..a1810ff2 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -84,14 +84,15 @@ public List encode(List messageParameterTuples) { } /** - * Chunk a message if necessary. The chunk information and all IDs will be set by the SDK and are - * not longer in control of the application. + * Chunk and add the Base64 encoding for a message if necessary. If there is only one chunk, the + * this single chunk will be returned as Base64 encoded value. The chunk information and all IDs + * will be set by the SDK and are no longer in control of the application. * * @param messageHeaderParameters - * @param payloadParameters Content of the message. It shall not be Base64 encoded before. * @return - */ - public List chunk( + public List chunkAndEncode( MessageHeaderParameters messageHeaderParameters, PayloadParameters payloadParameters, OnboardingResponse onboardingResponse) { @@ -106,54 +107,92 @@ public List chunk( payloadParameters.validate(); if (null != messageHeaderParameters.getTechnicalMessageType() - && messageHeaderParameters.getTechnicalMessageType().getNeedsChunking() - && payloadParameters.shouldBeChunked()) { - getNativeLogger() - .debug( - "The message should be chunked, current size of the payload ({}) is above the limitation.", - payloadParameters.getValue().toStringUtf8().length()); - String wholeMessage = payloadParameters.getValue().toStringUtf8(); - final List messageChunks = - Splitter.fixedLength(payloadParameters.maxLengthForMessages()).splitToList(wholeMessage); - List tuples = new ArrayList<>(); - AtomicInteger chunkNr = new AtomicInteger(1); - final String chunkContextId = ChunkContextIdService.generateChunkContextId(); - messageChunks.forEach( - chunk -> { - final String messageIdForChunk = MessageIdService.generateMessageId(); - final long sequenceNumberForChunk = - SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse); - - final MessageHeaderParameters header = new MessageHeaderParameters(); - header.copy(messageHeaderParameters); - header.setApplicationMessageId(messageIdForChunk); - header.setApplicationMessageSeqNo(sequenceNumberForChunk); - Chunk.ChunkComponent.Builder chunkInfo = Chunk.ChunkComponent.newBuilder(); - chunkInfo.setContextId(chunkContextId); - chunkInfo.setCurrent(chunkNr.get()); - chunkInfo.setTotal(messageChunks.size()); - chunkInfo.setTotalSize(wholeMessage.length()); - header.setChunkInfo(chunkInfo.build()); - - final PayloadParameters payload = new PayloadParameters(); - payload.copyFrom(payloadParameters); - payload.setValue( - ByteString.copyFromUtf8( - Base64.getEncoder().encodeToString(chunk.getBytes(StandardCharsets.UTF_8)))); - - tuples.add(new MessageParameterTuple(header, payload)); - - chunkNr.getAndIncrement(); - }); - return tuples; + && messageHeaderParameters.getTechnicalMessageType().getNeedsChunking()) { + if (payloadParameters.shouldBeChunked()) { + getNativeLogger() + .debug( + "The message should be chunked, current size of the payload ({}) is above the limitation.", + payloadParameters.getValue().toStringUtf8().length()); + String wholeMessage = payloadParameters.getValue().toStringUtf8(); + final List messageChunks = + Splitter.fixedLength(payloadParameters.maxLengthForMessages()) + .splitToList(wholeMessage); + List tuples = new ArrayList<>(); + AtomicInteger chunkNr = new AtomicInteger(1); + final String chunkContextId = ChunkContextIdService.generateChunkContextId(); + messageChunks.forEach( + chunk -> { + final String messageIdForChunk = MessageIdService.generateMessageId(); + final long sequenceNumberForChunk = + SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse); + + final MessageHeaderParameters header = new MessageHeaderParameters(); + header.copy(messageHeaderParameters); + header.setApplicationMessageId(messageIdForChunk); + header.setApplicationMessageSeqNo(sequenceNumberForChunk); + Chunk.ChunkComponent.Builder chunkInfo = Chunk.ChunkComponent.newBuilder(); + chunkInfo.setContextId(chunkContextId); + chunkInfo.setCurrent(chunkNr.get()); + chunkInfo.setTotal(messageChunks.size()); + chunkInfo.setTotalSize(wholeMessage.length()); + header.setChunkInfo(chunkInfo.build()); + + final PayloadParameters payload = new PayloadParameters(); + payload.copyFrom(payloadParameters); + payload.setValue( + ByteString.copyFromUtf8( + Base64.getEncoder().encodeToString(chunk.getBytes(StandardCharsets.UTF_8)))); + + tuples.add(new MessageParameterTuple(header, payload)); + + chunkNr.getAndIncrement(); + }); + return tuples; + } else { + getNativeLogger() + .debug( + "The message is not chunked since the current size of the payload ({}) is not above the limitation or the technical message type '{}' does not support chunking.", + payloadParameters.getValue().toStringUtf8().length(), + messageHeaderParameters.getTechnicalMessageType().getKey()); + getNativeLogger() + .debug("The content is encoded, since in other cases the content is encoded as well."); + final PayloadParameters payload = new PayloadParameters(); + payload.copyFrom(payloadParameters); + payload.setValue( + ByteString.copyFromUtf8( + Base64.getEncoder() + .encodeToString( + payloadParameters + .getValue() + .toStringUtf8() + .getBytes(StandardCharsets.UTF_8)))); + return Collections.singletonList( + new MessageParameterTuple(messageHeaderParameters, payload)); + } } else { getNativeLogger() .debug( - "The message is not chunked since the current size of the payload ({}) is not above the limitation or the technical message type '{}' does not support chunking.", - payloadParameters.getValue().toStringUtf8().length(), - messageHeaderParameters.getTechnicalMessageType().getKey()); - return Collections.singletonList( - new MessageParameterTuple(messageHeaderParameters, payloadParameters)); + "The message type needs to be base64 encoded, therefore we are encoding the raw value."); + if (messageHeaderParameters.getTechnicalMessageType().getNeedsBase64Encoding()) { + final PayloadParameters payload = new PayloadParameters(); + payload.copyFrom(payloadParameters); + payload.setValue( + ByteString.copyFromUtf8( + Base64.getEncoder() + .encodeToString( + payloadParameters + .getValue() + .toStringUtf8() + .getBytes(StandardCharsets.UTF_8)))); + return Collections.singletonList( + new MessageParameterTuple(messageHeaderParameters, payload)); + } else { + getNativeLogger() + .debug( + "The message type does not need base 64 encoding, we are returning the tuple 'as it is'."); + return Collections.singletonList( + new MessageParameterTuple(messageHeaderParameters, payloadParameters)); + } } } diff --git a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java index a250ca07..3840a6fc 100644 --- a/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java +++ b/agrirouter-sdk-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImplTest.java @@ -31,7 +31,7 @@ void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumbe PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); final List chunks = - encodeMessageService.chunk( + encodeMessageService.chunkAndEncode( messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); Assertions.assertEquals(1, chunks.size()); } @@ -47,7 +47,7 @@ void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumbe PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); final List chunks = - encodeMessageService.chunk( + encodeMessageService.chunkAndEncode( messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); Assertions.assertEquals(1, chunks.size()); } @@ -66,7 +66,7 @@ void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumbe PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); final List chunks = - encodeMessageService.chunk( + encodeMessageService.chunkAndEncode( messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); Assertions.assertEquals(1, chunks.size()); } @@ -83,7 +83,7 @@ void givenEmptyMessageWhenChunkingThenTheImplementationShouldReturnTheRightNumbe PayloadParameters payloadParameters = getPayloadParameters(toSendMessage); final List chunks = - encodeMessageService.chunk( + encodeMessageService.chunkAndEncode( messageHeaderParameters, payloadParameters, fakeOnboardingResponse()); Assertions.assertEquals(2, chunks.size()); diff --git a/agrirouter-sdk-java-tests/pom.xml b/agrirouter-sdk-java-tests/pom.xml index 37a38234..bbcad8ed 100644 --- a/agrirouter-sdk-java-tests/pom.xml +++ b/agrirouter-sdk-java-tests/pom.xml @@ -36,6 +36,10 @@ org.junit.jupiter junit-jupiter-api + + org.junit.jupiter + junit-jupiter-params + org.glassfish.jersey.inject jersey-hk2 diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index bde47ad7..7768581a 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -4,7 +4,6 @@ import com.dke.data.agrirouter.api.cancellation.DefaultCancellationToken; import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse; import com.dke.data.agrirouter.api.dto.messaging.FetchMessageResponse; -import com.dke.data.agrirouter.api.dto.messaging.inner.Message; import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; import com.dke.data.agrirouter.api.enums.ContentMessageType; import com.dke.data.agrirouter.api.enums.SystemMessageType; @@ -23,23 +22,25 @@ import com.dke.data.agrirouter.test.OnboardingResponseRepository; import com.google.protobuf.ByteString; import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; import org.apache.commons.lang3.RandomStringUtils; import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** Test case to show the behavior for chunked message sending. */ class SendChunkedMessageTest extends AbstractIntegrationTest { private static final int MAX_CHUNK_SIZE = 1024000; - private static final int EXPECTED_NUMBER_OF_CHUNKS = 3; - @Test + @ParameterizedTest + @MethodSource("fakeRawMessageContentThatHasToBeChunked") void - givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks() + givenLargeContentMessageWhenSendingTheMessageToTheAgrirouterTheSdkShouldHelpToSendTheFileInMultipleChunks( + ByteString messageContent, int expectedNrOfChunks) throws IOException, InterruptedException { final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl(); @@ -55,11 +56,12 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.PUBLISH); PayloadParameters payloadParameters = new PayloadParameters(); - payloadParameters.setValue(fakeRawMessageContentThatHasToBeChunked()); + payloadParameters.setValue(messageContent); payloadParameters.setTypeUrl(SystemMessageType.EMPTY.getKey()); List tuples = - encodeMessageService.chunk(messageHeaderParameters, payloadParameters, onboardingResponse); + encodeMessageService.chunkAndEncode( + messageHeaderParameters, payloadParameters, onboardingResponse); tuples.forEach( messageParameterTuple -> @@ -85,36 +87,44 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL)); Assertions.assertTrue(fetchMessageResponses.isPresent()); - Assertions.assertEquals(EXPECTED_NUMBER_OF_CHUNKS, fetchMessageResponses.get().size()); - Assertions.assertNotNull(fetchMessageResponses.get().get(0).getCommand()); - Assertions.assertNotNull(fetchMessageResponses.get().get(1).getCommand()); - Assertions.assertNotNull(fetchMessageResponses.get().get(2).getCommand()); + Assertions.assertEquals(expectedNrOfChunks, fetchMessageResponses.get().size()); - Message firstAck = fetchMessageResponses.get().get(0).getCommand(); - Message secondAck = fetchMessageResponses.get().get(1).getCommand(); - Message thirdAck = fetchMessageResponses.get().get(2).getCommand(); - - Arrays.stream(new Message[] {firstAck, secondAck, thirdAck}) + DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); + AtomicReference decodeMessageResponse = new AtomicReference<>(); + fetchMessageResponses.get().stream() + .map(FetchMessageResponse::getCommand) .forEach( message -> { - DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl(); - DecodeMessageResponse decodeMessageResponse = - decodeMessageService.decode(message.getMessage()); + Assertions.assertNotNull(message); + decodeMessageResponse.set(decodeMessageService.decode(message.getMessage())); Assertions.assertMatchesAny( Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT), - decodeMessageResponse.getResponseEnvelope().getResponseCode()); + decodeMessageResponse.get().getResponseEnvelope().getResponseCode()); }); } /** - * Delivers fake message content for three chunks. + * Delivers fake message content for multiple test cases. * * @return - */ - private ByteString fakeRawMessageContentThatHasToBeChunked() { - return ByteString.copyFromUtf8( - RandomStringUtils.randomAlphabetic( - PayloadParametersKt.MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT * EXPECTED_NUMBER_OF_CHUNKS)); + private static Stream fakeRawMessageContentThatHasToBeChunked() { + return Stream.of( + Arguments.of( + ByteString.copyFromUtf8( + RandomStringUtils.randomAlphabetic( + PayloadParametersKt.MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT * 3)), + 3), + Arguments.of( + ByteString.copyFromUtf8( + RandomStringUtils.randomAlphabetic( + PayloadParametersKt.MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT * 2)), + 2), + Arguments.of( + ByteString.copyFromUtf8( + RandomStringUtils.randomAlphabetic( + PayloadParametersKt.MAX_LENGTH_FOR_RAW_MESSAGE_CONTENT)), + 1)); } } diff --git a/pom.xml b/pom.xml index dd317295..19d5e145 100644 --- a/pom.xml +++ b/pom.xml @@ -152,6 +152,12 @@ ${junit-jupiter.version} test + + org.junit.jupiter + junit-jupiter-params + ${junit-jupiter.version} + test + org.junit.jupiter junit-jupiter-engine From 2999dbf85e5e8567fc4d1a87810f700c3fe140fa Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Mon, 20 Dec 2021 23:53:04 +0100 Subject: [PATCH 25/30] Fix typo. --- .../impl/messaging/encoding/EncodeMessageServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index a1810ff2..092d3d71 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -85,7 +85,7 @@ public List encode(List messageParameterTuples) { /** * Chunk and add the Base64 encoding for a message if necessary. If there is only one chunk, the - * this single chunk will be returned as Base64 encoded value. The chunk information and all IDs + * single chunk will be returned as Base64 encoded value. The chunk information and all IDs * will be set by the SDK and are no longer in control of the application. * * @param messageHeaderParameters - From 18a6e53639682833844832acd5d6d9bf4af615db Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Mon, 20 Dec 2021 23:54:16 +0100 Subject: [PATCH 26/30] Fix typo. --- .../data/agrirouter/api/service/parameters/PayloadParameters.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt index a6b3ea6f..093cac6d 100644 --- a/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt +++ b/agrirouter-sdk-java-api/src/main/kotlin/com/dke/data/agrirouter/api/service/parameters/PayloadParameters.kt @@ -5,7 +5,7 @@ import com.dke.data.agrirouter.api.service.parameters.base.AbstractParameterBase import com.google.protobuf.ByteString /** - * Every endpoint can send message based on its capabilities. The size of a message is however limited. A message contains 2 parts: Header and Body. The limitation of a message is defined as follows: + * Every endpoint can send messages based on its capabilities. The size of a message is however limited. A message contains 2 parts: Header and Body. The limitation of a message is defined as follows: * - The maximum body size is equivalent of 1024000 characters/signs * - Since the chunking is performed on the raw message data this means, that we have to lower the MAX_LENGTH_FOR_MESSAGES to allow Base64 encoding afterwards. * - Total message size is limited to 1468000 characters/signs From 728f8601887f3287211081c8d72b299e617441d1 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Mon, 20 Dec 2021 23:57:43 +0100 Subject: [PATCH 27/30] Changed the log message to a more meaningful one. --- .../impl/messaging/encoding/EncodeMessageServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index 092d3d71..e7f651e6 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -151,7 +151,7 @@ public List chunkAndEncode( } else { getNativeLogger() .debug( - "The message is not chunked since the current size of the payload ({}) is not above the limitation or the technical message type '{}' does not support chunking.", + "The message is not chunked since the current size of the payload ({}) is not above the limitation and the technical message type '{}' does support chunking.", payloadParameters.getValue().toStringUtf8().length(), messageHeaderParameters.getTechnicalMessageType().getKey()); getNativeLogger() From a42d92d2b579622dfe923277008c02006d3d062c Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Mon, 20 Dec 2021 23:58:42 +0100 Subject: [PATCH 28/30] Adapt position of the log message. --- .../impl/messaging/encoding/EncodeMessageServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index e7f651e6..9c98d8f4 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -170,10 +170,10 @@ public List chunkAndEncode( new MessageParameterTuple(messageHeaderParameters, payload)); } } else { - getNativeLogger() - .debug( - "The message type needs to be base64 encoded, therefore we are encoding the raw value."); if (messageHeaderParameters.getTechnicalMessageType().getNeedsBase64Encoding()) { + getNativeLogger() + .debug( + "The message type needs to be base64 encoded, therefore we are encoding the raw value."); final PayloadParameters payload = new PayloadParameters(); payload.copyFrom(payloadParameters); payload.setValue( From 7bb8311fb5276a1a566739a2dd6dff6837ea5266 Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Tue, 21 Dec 2021 00:02:55 +0100 Subject: [PATCH 29/30] Remove null check that was not necessary. --- .../impl/messaging/encoding/EncodeMessageServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java index 9c98d8f4..9c4abf35 100644 --- a/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java +++ b/agrirouter-sdk-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/encoding/EncodeMessageServiceImpl.java @@ -106,8 +106,7 @@ public List chunkAndEncode( messageHeaderParameters.validate(); payloadParameters.validate(); - if (null != messageHeaderParameters.getTechnicalMessageType() - && messageHeaderParameters.getTechnicalMessageType().getNeedsChunking()) { + if (messageHeaderParameters.getTechnicalMessageType().getNeedsChunking()) { if (payloadParameters.shouldBeChunked()) { getNativeLogger() .debug( From c90f9c4d43f9a541c2f45742c7c93d18cb956b7b Mon Sep 17 00:00:00 2001 From: Sascha Doemer Date: Tue, 21 Dec 2021 00:09:51 +0100 Subject: [PATCH 30/30] Adapt test case. --- .../agrirouter/test/messaging/rest/SendChunkedMessageTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java index 7768581a..0253ed02 100644 --- a/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java +++ b/agrirouter-sdk-java-tests/src/test/java/com/dke/data/agrirouter/test/messaging/rest/SendChunkedMessageTest.java @@ -69,7 +69,7 @@ class SendChunkedMessageTest extends AbstractIntegrationTest { Objects.requireNonNull(messageParameterTuple.getPayloadParameters().getValue()) .toStringUtf8() .length() - < MAX_CHUNK_SIZE)); + <= MAX_CHUNK_SIZE)); List encodedMessages = encodeMessageService.encode(tuples);