From aed1250912476f47fd772b0cedd20c850cdcff6e Mon Sep 17 00:00:00 2001 From: Christof Rabensteiner Date: Thu, 18 Apr 2019 11:05:31 +0200 Subject: Change App2mzs Interface and Init Delivery Pipeline - Change app2mzs interface: output message from app2mzs:DeliveryRequest was app2mzs:DeliveryResponse, now its msg:DeliveryRequestStatus. Reason: ZD returns msg:DeliveryRequestStatus which is signed. Moazs does not convert msg:DeliveryRequestStatus into app2mzs:DeliveryResponse because the conversion woudl break the signature. - App2MzsService: Make beans final and inject them with constructor; Refactor DeliveryRequestHandler into App2MZSService. - DeliveryPipeline: Add Interface and a "SameThreadImplementation" that executes the pipeline in the same threat and can be used in single mode. - DeliveryRepository: Augment interface for storing and retrieving request status objects; Add in-memory-implementation for status objects. - Utils: Add helper function for coalescing multiple values --- src/main/java/at/gv/egiz/moazs/App2MzsService.java | 89 +++++++++++++++------- .../at/gv/egiz/moazs/DeliveryRequestHandler.java | 38 --------- src/main/java/at/gv/egiz/moazs/Mzs2AppClient.java | 4 +- .../gv/egiz/moazs/pipeline/DeliveryPipeline.java | 12 +++ .../moazs/pipeline/SameThreadDeliveryPipeline.java | 49 ++++++++++++ .../egiz/moazs/repository/DeliveryRepository.java | 6 ++ .../repository/InMemoryDeliveryRepository.java | 34 ++++++--- .../moazs/repository/RedisDeliveryRepository.java | 10 +++ .../java/at/gv/egiz/moazs/util/NullCoalesce.java | 33 ++++++++ src/main/resources/mzs/app2mzs.xsd | 14 +--- .../java/at/gv/egiz/moazs/App2MzsServiceTest.java | 2 +- 11 files changed, 200 insertions(+), 91 deletions(-) delete mode 100644 src/main/java/at/gv/egiz/moazs/DeliveryRequestHandler.java create mode 100644 src/main/java/at/gv/egiz/moazs/pipeline/DeliveryPipeline.java create mode 100644 src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java create mode 100644 src/main/java/at/gv/egiz/moazs/util/NullCoalesce.java diff --git a/src/main/java/at/gv/egiz/moazs/App2MzsService.java b/src/main/java/at/gv/egiz/moazs/App2MzsService.java index f8c00fc..8626c24 100644 --- a/src/main/java/at/gv/egiz/moazs/App2MzsService.java +++ b/src/main/java/at/gv/egiz/moazs/App2MzsService.java @@ -1,18 +1,21 @@ package at.gv.egiz.moazs; +import at.gv.e_government.reference.namespace.zustellung.msg.phase2._20181206_.DeliveryRequestStatusType; +import at.gv.e_government.reference.namespace.zustellung.msg.phase2._20181206_.ObjectFactory; import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs.App2MzsPortType; -import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.*; +import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.DeliveryRequestType; +import at.gv.egiz.moazs.pipeline.DeliveryPipeline; +import at.gv.egiz.moazs.repository.DeliveryRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.jws.WebParam; -import java.math.BigInteger; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static java.text.MessageFormat.format; import static java.util.concurrent.CompletableFuture.supplyAsync; @Service @@ -20,63 +23,93 @@ public class App2MzsService implements App2MzsPortType { private static final Logger logger = LoggerFactory.getLogger(App2MzsService.class); - //TODO move timeout to config - private final int timeoutForAnwser = 10; - private final String MZSTNS = "http://reference.e-government.gv.at/namespace/zustellung/mzs/app2mzs#"; + //TODO move timeout and namespaces to config + private static final int TIMEOUT_FOR_ANWSER = 10; + private static final String MZSTNS = "http://reference.e-government.gv.at/namespace/zustellung/mzs/app2mzs#"; - @Autowired - private DeliveryRequestHandler deliveryRequestHandler; + private final DeliveryRepository repository; + private final DeliveryPipeline pipeline; + private final Mzs2AppClient appClient; + private final ObjectFactory objectFactory = new ObjectFactory(); - @Autowired - private Mzs2AppClient appClient; - - @Autowired - private ObjectFactory objectFactory; + public App2MzsService(@Autowired DeliveryRepository repository, @Autowired DeliveryPipeline pipeline, @Autowired Mzs2AppClient appClient) { + this.repository = repository; + this.pipeline = pipeline; + this.appClient = appClient; + } @Override - public DeliveryResponseType app2Mzs( + public DeliveryRequestStatusType app2Mzs( @WebParam(partName = "DeliveryRequest", name = "DeliveryRequest", targetNamespace = MZSTNS) DeliveryRequestType deliveryRequest) { - var future = supplyAsync(() -> deliveryRequestHandler.apply(deliveryRequest)); + final var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID(); + + var future = supplyAsync(() -> process(deliveryRequest)); try { - return future.get(timeoutForAnwser, TimeUnit.SECONDS); + return future.get(TIMEOUT_FOR_ANWSER, TimeUnit.SECONDS); } catch (TimeoutException e) { future.thenAccept(appClient::sendNotification); - logger.warn("Answer Timed Out", e); - return generatePartialSuccessResponse(deliveryRequest); + logger.info("Answer Timed Out", e); + return generatePartialSuccessResponse(appDeliveryID, "Your request timed out; Wait until X for receiving a notification."); } catch (Exception e ) { logger.error("Could not deliver request.", e); - return generateErrorResponse(); + final var message = format("An error occured while delivering request: {}", e.getMessage()); + return generateErrorResponse(appDeliveryID, message); } } - private DeliveryResponseType generatePartialSuccessResponse(DeliveryRequestType deliveryRequest) { + private DeliveryRequestStatusType generatePartialSuccessResponse(String appDeliveryId, String message) { - var partialSuccess = objectFactory.createPartialSuccessType(); - partialSuccess.setAppDeliveryID(deliveryRequest.getMetaData().getAppDeliveryID()); + var partialSuccess = objectFactory.createDeliveryAnswerType(); + partialSuccess.setAppDeliveryID(appDeliveryId); - var response = objectFactory.createDeliveryResponseType(); + var response = objectFactory.createDeliveryRequestStatusType(); response.setPartialSuccess(partialSuccess); + response.setSignature(null); + return response; } - private DeliveryResponseType generateErrorResponse() { + private DeliveryRequestStatusType generateErrorResponse(String appDeliveryId, String message) { - var error = objectFactory.createErrorType(); - error.setText("Error Occurred"); - error.setCode(BigInteger.valueOf(500)); + var error = objectFactory.createDeliveryRequestStatusTypeError(); + error.setAppDeliveryID(appDeliveryId); - var response = objectFactory.createDeliveryResponseType(); + var info = objectFactory.createErrorInfoType(); + info.setCode("500"); + info.setText(message); + error.setErrorInfo(info); + + var response = objectFactory.createDeliveryRequestStatusType(); response.setError(error); + response.setSignature(null); return response; } + private DeliveryRequestStatusType process(DeliveryRequestType deliveryRequest) throws RuntimeException { + + var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID(); + logger.info("Receive request with appDeliveryID = {}.", appDeliveryID); + + repository.add(deliveryRequest); + pipeline.processRequest(appDeliveryID); + + var status = repository.getDeliveryRequestStatus(appDeliveryID); + + if (status.isEmpty()) { + throw new RuntimeException(format("Could not get a response for request with appDeliveryId = {}", appDeliveryID)); + } + + return status.get(); + + } + } \ No newline at end of file diff --git a/src/main/java/at/gv/egiz/moazs/DeliveryRequestHandler.java b/src/main/java/at/gv/egiz/moazs/DeliveryRequestHandler.java deleted file mode 100644 index 0dc0a15..0000000 --- a/src/main/java/at/gv/egiz/moazs/DeliveryRequestHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -package at.gv.egiz.moazs; - -import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.DeliveryRequestType; -import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.DeliveryResponseType; -import at.gv.egiz.moazs.repository.DeliveryRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.function.Function; - -@Component -public class DeliveryRequestHandler implements Function { - - private static final Logger logger = LoggerFactory.getLogger(DeliveryRequestHandler.class); - - @Autowired - private DeliveryRepository repository; - - /** - * Handles the Delivery Request - */ - @Override - public DeliveryResponseType apply(DeliveryRequestType deliveryRequest) { - - var deliveryId = deliveryRequest.getMetaData().getAppDeliveryID(); - logger.info(String.format("Received Request with AppDeliveryID = %s", deliveryId)); - - //check if DR is complete - - - //store delivery request - repository.add(deliveryRequest); - - return null; - } -} diff --git a/src/main/java/at/gv/egiz/moazs/Mzs2AppClient.java b/src/main/java/at/gv/egiz/moazs/Mzs2AppClient.java index 01f2787..d0d7936 100644 --- a/src/main/java/at/gv/egiz/moazs/Mzs2AppClient.java +++ b/src/main/java/at/gv/egiz/moazs/Mzs2AppClient.java @@ -1,12 +1,12 @@ package at.gv.egiz.moazs; -import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.DeliveryResponseType; +import at.gv.e_government.reference.namespace.zustellung.msg.phase2._20181206_.DeliveryRequestStatusType; import org.springframework.stereotype.Component; @Component public class Mzs2AppClient { - public void sendNotification(DeliveryResponseType response) { + public void sendNotification(DeliveryRequestStatusType status) { } diff --git a/src/main/java/at/gv/egiz/moazs/pipeline/DeliveryPipeline.java b/src/main/java/at/gv/egiz/moazs/pipeline/DeliveryPipeline.java new file mode 100644 index 0000000..1d3edb8 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/pipeline/DeliveryPipeline.java @@ -0,0 +1,12 @@ +package at.gv.egiz.moazs.pipeline; + + +public interface DeliveryPipeline { + + /** + * Gets request that is referred by appDeliveryId from cache, + * @param appDeliveryId + */ + void processRequest(String appDeliveryId); + +} diff --git a/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java b/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java new file mode 100644 index 0000000..3652662 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java @@ -0,0 +1,49 @@ +package at.gv.egiz.moazs.pipeline; + + +import at.gv.e_government.reference.namespace.zustellung.msg.phase2._20181206_.DeliveryRequestStatusType; +import at.gv.e_government.reference.namespace.zustellung.msg.phase2._20181206_.ObjectFactory; +import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.DeliveryRequestType; +import at.gv.egiz.moazs.repository.DeliveryRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +import static java.text.MessageFormat.format; + +@Component +@Profile("!cluster") +public class SameThreadDeliveryPipeline implements DeliveryPipeline { + + private final DeliveryRepository repository; + + public SameThreadDeliveryPipeline(@Autowired DeliveryRepository repository) { + this.repository = repository; + } + + @Override + public void processRequest(String appDeliveryId) { + + Optional request = repository.getDeliveryRequest(appDeliveryId); + if (request.isEmpty()) { + throw new RuntimeException(format( + "Request with appDeliveryId = {} does not exist.", appDeliveryId)); + } + + //do some logic. + + //store response to repository + repository.add(generateBogusStatus(appDeliveryId)); + } + + private DeliveryRequestStatusType generateBogusStatus(String appDeliveryId) { + final var factory = new ObjectFactory(); + final var status = factory.createDeliveryRequestStatusType(); + final var success = factory.createDeliveryRequestStatusTypeSuccess(); + success.setAppDeliveryID(appDeliveryId); + status.setSuccess(success); + return status; + } +} diff --git a/src/main/java/at/gv/egiz/moazs/repository/DeliveryRepository.java b/src/main/java/at/gv/egiz/moazs/repository/DeliveryRepository.java index d5766b6..f736160 100644 --- a/src/main/java/at/gv/egiz/moazs/repository/DeliveryRepository.java +++ b/src/main/java/at/gv/egiz/moazs/repository/DeliveryRepository.java @@ -1,5 +1,6 @@ package at.gv.egiz.moazs.repository; +import at.gv.e_government.reference.namespace.zustellung.msg.phase2._20181206_.DeliveryRequestStatusType; import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.DeliveryRequestType; import org.springframework.stereotype.Repository; @@ -12,4 +13,9 @@ public interface DeliveryRepository { Optional getDeliveryRequest(String appDeliveryID); + void add(DeliveryRequestStatusType status); + + Optional getDeliveryRequestStatus(String appDeliveryID); + + } diff --git a/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java b/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java index e5486d7..5712c2d 100644 --- a/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java +++ b/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java @@ -1,5 +1,6 @@ package at.gv.egiz.moazs.repository; +import at.gv.e_government.reference.namespace.zustellung.msg.phase2._20181206_.DeliveryRequestStatusType; import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.DeliveryRequestType; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -11,30 +12,45 @@ import org.springframework.stereotype.Repository; import java.util.Optional; import java.util.concurrent.TimeUnit; +import static at.gv.egiz.moazs.util.NullCoalesce.coalesce; +import static java.util.Optional.ofNullable; + @Repository @Profile("!cluster") public class InMemoryDeliveryRepository implements DeliveryRepository { private static final Logger logger = LoggerFactory.getLogger(InMemoryDeliveryRepository.class); - private static final Cache rep = CacheBuilder.newBuilder() + private static final Cache requestRepository = CacheBuilder.newBuilder() .expireAfterWrite(30, TimeUnit.MINUTES) .build(); - public InMemoryDeliveryRepository() { - logger.info("InMemoryDeliveryRepository()"); - } + private static final Cache statusRepository = CacheBuilder.newBuilder() + .expireAfterWrite(30, TimeUnit.MINUTES) + .build(); @Override public void add(DeliveryRequestType request) { - String key = request.getMetaData().getAppDeliveryID(); - logger.info("Save DeliveryRequest with AppDeliveryId = {} to Cache.", key); - rep.put(key, request); + var key = request.getMetaData().getAppDeliveryID(); + requestRepository.put(key, request); } @Override public Optional getDeliveryRequest(String appDeliveryID) { - DeliveryRequestType deliveryRequestType = rep.getIfPresent(appDeliveryID); - return Optional.ofNullable(deliveryRequestType); + var deliveryRequestType = requestRepository.getIfPresent(appDeliveryID); + return ofNullable(deliveryRequestType); + } + + @Override + public void add(DeliveryRequestStatusType status) { + var key = coalesce(status.getSuccess(), status.getPartialSuccess(), status.getError()) + .get().getAppDeliveryID(); + statusRepository.put(key, status); + } + + @Override + public Optional getDeliveryRequestStatus(String appDeliveryID) { + var deliveryRequestType = statusRepository.getIfPresent(appDeliveryID); + return ofNullable(deliveryRequestType); } } diff --git a/src/main/java/at/gv/egiz/moazs/repository/RedisDeliveryRepository.java b/src/main/java/at/gv/egiz/moazs/repository/RedisDeliveryRepository.java index 4f39f31..cd02dac 100644 --- a/src/main/java/at/gv/egiz/moazs/repository/RedisDeliveryRepository.java +++ b/src/main/java/at/gv/egiz/moazs/repository/RedisDeliveryRepository.java @@ -1,5 +1,6 @@ package at.gv.egiz.moazs.repository; +import at.gv.e_government.reference.namespace.zustellung.msg.phase2._20181206_.DeliveryRequestStatusType; import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.DeliveryRequestType; import at.gv.e_government.reference.namespace.zustellung.mzs.app2mzs_.ObjectFactory; import at.gv.egiz.moazs.Marshaller; @@ -59,4 +60,13 @@ public class RedisDeliveryRepository implements DeliveryRepository { return Optional.of(request.getValue()); } + @Override + public void add(DeliveryRequestStatusType status) { + throw new RuntimeException("Not Implemented Yet"); + } + + @Override + public Optional getDeliveryRequestStatus(String appDeliveryID) { + throw new RuntimeException("Not Implemented Yet"); + } } diff --git a/src/main/java/at/gv/egiz/moazs/util/NullCoalesce.java b/src/main/java/at/gv/egiz/moazs/util/NullCoalesce.java new file mode 100644 index 0000000..4377140 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/util/NullCoalesce.java @@ -0,0 +1,33 @@ +package at.gv.egiz.moazs.util; + +import java.util.Optional; + +import static java.util.Optional.ofNullable; + +/** + * Helper function for coalescing nullables value without cluttering code with if-else cascades + * @author les2 + * @source https://stackoverflow.com/questions/2768054/how-to-get-the-first-non-null-value-in-java + */ +public class NullCoalesce { + + /** + * @return the first not-null value + * @param a + * @param b + * @param + * @return a if a != null, otherwise b. + */ + public static Optional coalesce(T a, T b) { + return a != null ? ofNullable(a) : ofNullable(b); + } + + public static Optional coalesce(T a, T b, T c) { + return a != null ? ofNullable(a) : coalesce(b,c); + } + + public static Optional coalesce(T a, T b, T c, T d) { + return a != null ? ofNullable(a) : coalesce(b,c,d); + } + +} diff --git a/src/main/resources/mzs/app2mzs.xsd b/src/main/resources/mzs/app2mzs.xsd index 1e0a175..434db23 100644 --- a/src/main/resources/mzs/app2mzs.xsd +++ b/src/main/resources/mzs/app2mzs.xsd @@ -80,19 +80,7 @@ - - - - - - - - DeliveryConfirmation Base64 encoded - - - - - + asynchronous communication moazs2app diff --git a/src/test/java/at/gv/egiz/moazs/App2MzsServiceTest.java b/src/test/java/at/gv/egiz/moazs/App2MzsServiceTest.java index 6874c75..78dafdc 100644 --- a/src/test/java/at/gv/egiz/moazs/App2MzsServiceTest.java +++ b/src/test/java/at/gv/egiz/moazs/App2MzsServiceTest.java @@ -15,7 +15,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.file.Paths; -import static java.net.http.HttpClient.*; +import static java.net.http.HttpClient.Version; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) -- cgit v1.2.3