diff --git a/agrirouter-api-java-api/src/main/java/com/dke/data/agrirouter/api/exception/OnboardingException.java b/agrirouter-api-java-api/src/main/java/com/dke/data/agrirouter/api/exception/OnboardingException.java index 13e868d7..c879b8b4 100644 --- a/agrirouter-api-java-api/src/main/java/com/dke/data/agrirouter/api/exception/OnboardingException.java +++ b/agrirouter-api-java-api/src/main/java/com/dke/data/agrirouter/api/exception/OnboardingException.java @@ -23,5 +23,4 @@ public String getMessage() { onboardingError.getError().getCode(), onboardingError.getError().message) : "There was an error during the onboarding process."; } - } diff --git a/agrirouter-api-java-api/src/main/java/com/dke/data/agrirouter/api/exception/RevokingException.java b/agrirouter-api-java-api/src/main/java/com/dke/data/agrirouter/api/exception/RevokingException.java index 80125363..2ea74318 100644 --- a/agrirouter-api-java-api/src/main/java/com/dke/data/agrirouter/api/exception/RevokingException.java +++ b/agrirouter-api-java-api/src/main/java/com/dke/data/agrirouter/api/exception/RevokingException.java @@ -1,27 +1,26 @@ package com.dke.data.agrirouter.api.exception; import com.dke.data.agrirouter.api.dto.revoke.RevokingError; - import java.util.Optional; public class RevokingException extends RuntimeException { - private final RevokingError revokingError; + private final RevokingError revokingError; - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public RevokingException(Optional lastError) { - this.revokingError = lastError.orElse(null); - } + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + public RevokingException(Optional lastError) { + this.revokingError = lastError.orElse(null); + } - public RevokingError getRevokingError() { - return revokingError; - } + public RevokingError getRevokingError() { + return revokingError; + } - @Override - public String getMessage() { - return null != revokingError - ? String.format( - "There was an error '%s' during the revoking, details were '%s'", - revokingError.getError().getCode(), revokingError.getError().message) - : "There was an error during the revoking process."; - } + @Override + public String getMessage() { + return null != revokingError + ? String.format( + "There was an error '%s' during the revoking, details were '%s'", + revokingError.getError().getCode(), revokingError.getError().message) + : "There was an error during the revoking process."; + } } diff --git a/agrirouter-api-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/MessageEncoder.java b/agrirouter-api-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/MessageEncoder.java index f82bb84b..f47693c3 100644 --- a/agrirouter-api-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/MessageEncoder.java +++ b/agrirouter-api-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/MessageEncoder.java @@ -25,6 +25,8 @@ public interface MessageEncoder extends LoggingEnabledService { * @return - */ default EncodedMessage encode(DeleteMessageParameters parameters) { + assert parameters.getOnboardingResponse() != null; + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); final String applicationMessageID = @@ -37,7 +39,11 @@ default EncodedMessage encode(DeleteMessageParameters parameters) { parameters.getTeamsetContextId() == null ? "" : parameters.getTeamsetContextId(); messageHeaderParameters.setTeamSetContextId(Objects.requireNonNull(teamsetContextId)); - messageHeaderParameters.setApplicationMessageSeqNo(parameters.getSequenceNumber()); + messageHeaderParameters.setApplicationMessageSeqNo( + parameters.getSequenceNumber() != 0 + ? parameters.getSequenceNumber() + : SequenceNumberService.generateSequenceNumberForEndpoint( + parameters.getOnboardingResponse())); messageHeaderParameters.setMetadata(MessageOuterClass.Metadata.newBuilder().build()); messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.DKE_FEED_DELETE); @@ -77,6 +83,7 @@ default EncodedMessage encode(DeleteMessageParameters parameters) { * @return - */ default EncodedMessage encode(ListEndpointsParameters parameters) { + assert parameters.getOnboardingResponse() != null; MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); @@ -91,7 +98,11 @@ default EncodedMessage encode(ListEndpointsParameters parameters) { parameters.getTeamsetContextId() == null ? "" : parameters.getTeamsetContextId(); messageHeaderParameters.setTeamSetContextId(Objects.requireNonNull(teamsetContextId)); - messageHeaderParameters.setApplicationMessageSeqNo(parameters.getSequenceNumber()); + messageHeaderParameters.setApplicationMessageSeqNo( + parameters.getSequenceNumber() != 0 + ? parameters.getSequenceNumber() + : SequenceNumberService.generateSequenceNumberForEndpoint( + parameters.getOnboardingResponse())); if (parameters.getUnfilteredList()) { messageHeaderParameters.setTechnicalMessageType( @@ -123,6 +134,8 @@ default EncodedMessage encode(ListEndpointsParameters parameters) { * @return - */ default EncodedMessage encode(MessageConfirmationParameters parameters) { + assert parameters.getOnboardingResponse() != null; + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); final String applicationMessageID = @@ -137,7 +150,11 @@ default EncodedMessage encode(MessageConfirmationParameters parameters) { parameters.getTeamsetContextId() == null ? "" : parameters.getTeamsetContextId(); messageHeaderParameters.setTeamSetContextId(Objects.requireNonNull(teamsetContextId)); - messageHeaderParameters.setApplicationMessageSeqNo(parameters.getSequenceNumber()); + messageHeaderParameters.setApplicationMessageSeqNo( + parameters.getSequenceNumber() != 0 + ? parameters.getSequenceNumber() + : SequenceNumberService.generateSequenceNumberForEndpoint( + parameters.getOnboardingResponse())); messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.DKE_FEED_CONFIRM); messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); @@ -160,6 +177,8 @@ default EncodedMessage encode(MessageConfirmationParameters parameters) { * @return - */ default EncodedMessage encode(SetCapabilitiesParameters parameters) { + assert parameters.getOnboardingResponse() != null; + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); final String applicationMessageID = @@ -174,7 +193,11 @@ default EncodedMessage encode(SetCapabilitiesParameters parameters) { parameters.getTeamsetContextId() == null ? "" : parameters.getTeamsetContextId(); messageHeaderParameters.setTeamSetContextId(Objects.requireNonNull(teamsetContextId)); - messageHeaderParameters.setApplicationMessageSeqNo(parameters.getSequenceNumber()); + messageHeaderParameters.setApplicationMessageSeqNo( + parameters.getSequenceNumber() != 0 + ? parameters.getSequenceNumber() + : SequenceNumberService.generateSequenceNumberForEndpoint( + parameters.getOnboardingResponse())); messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.DKE_CAPABILITIES); messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); @@ -216,6 +239,8 @@ default EncodedMessage encode(SetCapabilitiesParameters parameters) { * @return - */ default EncodedMessage encodeMessage(SetSubscriptionParameters parameters) { + assert parameters.getOnboardingResponse() != null; + MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); final String applicationMessageID = @@ -230,7 +255,11 @@ default EncodedMessage encodeMessage(SetSubscriptionParameters parameters) { parameters.getTeamsetContextId() == null ? "" : parameters.getTeamsetContextId(); messageHeaderParameters.setTeamSetContextId(Objects.requireNonNull(teamsetContextId)); - messageHeaderParameters.setApplicationMessageSeqNo(parameters.getSequenceNumber()); + messageHeaderParameters.setApplicationMessageSeqNo( + parameters.getSequenceNumber() != 0 + ? parameters.getSequenceNumber() + : SequenceNumberService.generateSequenceNumberForEndpoint( + parameters.getOnboardingResponse())); messageHeaderParameters.setTechnicalMessageType(TechnicalMessageType.DKE_SUBSCRIPTION); messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); @@ -267,6 +296,8 @@ default EncodedMessage encodeMessage(SetSubscriptionParameters parameters) { */ default EncodedMessage encode( TechnicalMessageType technicalMessageType, MessageQueryParameters parameters) { + assert parameters.getOnboardingResponse() != null; + this.logMethodBegin(parameters); this.getNativeLogger().trace("Build message header parameters."); @@ -283,7 +314,12 @@ default EncodedMessage encode( final String teamsetContextId = parameters.getTeamsetContextId() == null ? "" : parameters.getTeamsetContextId(); messageHeaderParameters.setTeamSetContextId(Objects.requireNonNull(teamsetContextId)); - messageHeaderParameters.setApplicationMessageSeqNo(parameters.getSequenceNumber()); + messageHeaderParameters.setApplicationMessageSeqNo( + parameters.getSequenceNumber() != 0 + ? parameters.getSequenceNumber() + : SequenceNumberService.generateSequenceNumberForEndpoint( + parameters.getOnboardingResponse())); + messageHeaderParameters.setTechnicalMessageType(technicalMessageType); messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); @@ -326,6 +362,8 @@ default EncodedMessage encode( * @return - */ default EncodedMessage encode(CloudOnboardingParameters parameters) { + assert parameters.getOnboardingResponse() != null; + final String applicationMessageID = parameters.getApplicationMessageId() == null ? MessageIdService.generateMessageId() @@ -334,12 +372,14 @@ default EncodedMessage encode(CloudOnboardingParameters parameters) { final String teamsetContextId = parameters.getTeamsetContextId() == null ? "" : parameters.getTeamsetContextId(); - int sequenceNumber = parameters.getSequenceNumber(); - MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters(); messageHeaderParameters.setApplicationMessageId(applicationMessageID); messageHeaderParameters.setTeamSetContextId(teamsetContextId); - messageHeaderParameters.setApplicationMessageSeqNo(sequenceNumber); + messageHeaderParameters.setApplicationMessageSeqNo( + parameters.getSequenceNumber() != 0 + ? parameters.getSequenceNumber() + : SequenceNumberService.generateSequenceNumberForEndpoint( + parameters.getOnboardingResponse())); messageHeaderParameters.setMetadata(MessageOuterClass.Metadata.newBuilder().build()); messageHeaderParameters.setTechnicalMessageType( TechnicalMessageType.DKE_CLOUD_ONBOARD_ENDPOINTS); @@ -376,6 +416,8 @@ default EncodedMessage encode(CloudOnboardingParameters parameters) { * @return - */ default EncodedMessage encode(CloudOffboardingParameters parameters) { + assert parameters.getOnboardingResponse() != null; + final String applicationMessageID = parameters.getApplicationMessageId() == null ? MessageIdService.generateMessageId() @@ -391,6 +433,11 @@ default EncodedMessage encode(CloudOffboardingParameters parameters) { TechnicalMessageType.DKE_CLOUD_OFFBOARD_ENDPOINTS); messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT); messageHeaderParameters.setMetadata(MessageOuterClass.Metadata.newBuilder().build()); + messageHeaderParameters.setApplicationMessageSeqNo( + parameters.getSequenceNumber() != 0 + ? parameters.getSequenceNumber() + : SequenceNumberService.generateSequenceNumberForEndpoint( + parameters.getOnboardingResponse())); PayloadParameters payloadParameters = new PayloadParameters(); payloadParameters.setTypeUrl( diff --git a/agrirouter-api-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/SequenceNumberService.java b/agrirouter-api-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/SequenceNumberService.java new file mode 100644 index 00000000..cfc8f974 --- /dev/null +++ b/agrirouter-api-java-impl/src/main/java/com/dke/data/agrirouter/impl/messaging/SequenceNumberService.java @@ -0,0 +1,31 @@ +package com.dke.data.agrirouter.impl.messaging; + +import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Service to generate sequence numbers while sending messages to the agrirouter. The sequence + * number generation is based on the ID of the endpoint, therefore a sequence number can be used + * multiple times for different endpoints. + */ +public class SequenceNumberService { + + private static final ConcurrentHashMap sequenceNumbersForEndpoints = + new ConcurrentHashMap<>(); + + /** + * Generate the sequence number for the onboarding response. + * + * @param onboardingResponse - + * @return 1 if this was the first call, 1+n for the n-th call. + */ + public static synchronized long generateSequenceNumberForEndpoint( + OnboardingResponse onboardingResponse) { + sequenceNumbersForEndpoints.putIfAbsent(onboardingResponse.getSensorAlternateId(), 1L); + Long currentSequenceNumber = + sequenceNumbersForEndpoints.get(onboardingResponse.getSensorAlternateId()); + sequenceNumbersForEndpoints.put( + onboardingResponse.getSensorAlternateId(), currentSequenceNumber + 1); + return currentSequenceNumber; + } +} diff --git a/agrirouter-api-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/SequenceNumberServiceTest.java b/agrirouter-api-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/SequenceNumberServiceTest.java new file mode 100644 index 00000000..f755ebea --- /dev/null +++ b/agrirouter-api-java-impl/src/test/java/com/dke/data/agrirouter/impl/messaging/SequenceNumberServiceTest.java @@ -0,0 +1,67 @@ +package com.dke.data.agrirouter.impl.messaging; + +import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class SequenceNumberServiceTest { + + @Test + void givenOnboardingResponseWhenGeneratingSequenceNumberForTheFirstTimeThenTheResultShouldBe1() { + OnboardingResponse onboardingResponse = new OnboardingResponse(); + onboardingResponse.setSensorAlternateId("this-is-my-id"); + Assertions.assertEquals( + 1, SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); + } + + @Test + void + givenOnboardingResponseWhenGeneratingSequenceNumberForTwoTimesInARowThenTheResultShouldBe1And2() { + OnboardingResponse onboardingResponse = new OnboardingResponse(); + onboardingResponse.setSensorAlternateId("this-is-another-id"); + Assertions.assertEquals( + 1, SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); + Assertions.assertEquals( + 2, SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse)); + } + + @Test + void + givenTwoOnboardingResponsesWhenGeneratingSequenceNumberForTheFirstTimeThenTheResultShouldBe1ForBoth() { + OnboardingResponse firstOnboardingResponse = new OnboardingResponse(); + firstOnboardingResponse.setSensorAlternateId("this-is-the-first-id"); + OnboardingResponse secondOnboardingResponse = new OnboardingResponse(); + secondOnboardingResponse.setSensorAlternateId("this-is-the-second-id"); + Assertions.assertEquals( + 1, SequenceNumberService.generateSequenceNumberForEndpoint(firstOnboardingResponse)); + Assertions.assertEquals( + 1, SequenceNumberService.generateSequenceNumberForEndpoint(secondOnboardingResponse)); + } + + @Test + void + givenTwoOnboardingResponsesWhenGeneratingSequenceNumberForMultipleTimesThenTheResultShouldBeMatchingTheTimesTheMethodWasCalled() { + OnboardingResponse firstOnboardingResponse = new OnboardingResponse(); + firstOnboardingResponse.setSensorAlternateId("this-is-the-first-id-for-multiple-calls"); + OnboardingResponse secondOnboardingResponse = new OnboardingResponse(); + secondOnboardingResponse.setSensorAlternateId("this-is-the-second-id-multiple-calls"); + Assertions.assertEquals( + 1, SequenceNumberService.generateSequenceNumberForEndpoint(firstOnboardingResponse)); + Assertions.assertEquals( + 2, SequenceNumberService.generateSequenceNumberForEndpoint(firstOnboardingResponse)); + Assertions.assertEquals( + 3, SequenceNumberService.generateSequenceNumberForEndpoint(firstOnboardingResponse)); + Assertions.assertEquals( + 4, SequenceNumberService.generateSequenceNumberForEndpoint(firstOnboardingResponse)); + + Assertions.assertEquals( + 1, SequenceNumberService.generateSequenceNumberForEndpoint(secondOnboardingResponse)); + Assertions.assertEquals( + 2, SequenceNumberService.generateSequenceNumberForEndpoint(secondOnboardingResponse)); + Assertions.assertEquals( + 3, SequenceNumberService.generateSequenceNumberForEndpoint(secondOnboardingResponse)); + + Assertions.assertEquals( + 5, SequenceNumberService.generateSequenceNumberForEndpoint(firstOnboardingResponse)); + } +}