From 6d30f261c618a3b69a8f1be092056383e6dea424 Mon Sep 17 00:00:00 2001 From: Christof Rabensteiner Date: Thu, 18 Jul 2019 09:23:00 +0200 Subject: Integrate Mzs Service Timeout and Handle Async Responses - Service Timeout : Add service timeout to mzs schema (DeliveryRequest / Config), application.yaml, convert service timeout from spring environment to Config, and merge service timeouts in Configs. - Handling of Asynchronous DeliveryRequestStatus: Move "Applying Response Sinks" from backend into dedicated component "MsgResponseSinkHub" and integrate SinkHub into MzsService (apply sinks to asynchronous responses). - Remove line breaks in SignatureVerifier's log statements. - Revise documentation of parameters in application.yaml. --- src/main/java/at/gv/egiz/moazs/MoaZSException.java | 1 - .../gv/egiz/moazs/backend/MsgResponseBackend.java | 46 +++++------------- .../gv/egiz/moazs/backend/MsgResponseSinkHub.java | 44 ++++++++++++++++++ .../gv/egiz/moazs/backend/SignatureVerifier.java | 28 +++++------ .../at/gv/egiz/moazs/preprocess/ConfigUtil.java | 23 ++++++--- .../java/at/gv/egiz/moazs/service/MzsService.java | 54 +++++++++++++--------- 6 files changed, 115 insertions(+), 81 deletions(-) create mode 100644 src/main/java/at/gv/egiz/moazs/backend/MsgResponseSinkHub.java (limited to 'src/main/java/at/gv/egiz/moazs') diff --git a/src/main/java/at/gv/egiz/moazs/MoaZSException.java b/src/main/java/at/gv/egiz/moazs/MoaZSException.java index 499dc14..1ffc1ef 100644 --- a/src/main/java/at/gv/egiz/moazs/MoaZSException.java +++ b/src/main/java/at/gv/egiz/moazs/MoaZSException.java @@ -8,7 +8,6 @@ public class MoaZSException extends RuntimeException { public static final String ERROR_MZS_MIMETYPE_MISSMATCH = "8001"; public static final String ERROR_MZS_NO_TNVZ_PERSON_QUERY_RESULTS = "8002"; public static final String ERROR_MZS_BINARY_RESPONSE_MISSING = "8003"; - public static final String ERROR_MZS_RESPONSE_MISSING = "8004"; public static final String ERROR_MOASP_SIGNATURE_INVALID = "7001"; @Nullable private final String code; diff --git a/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java b/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java index 8649a32..df0f83e 100644 --- a/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java +++ b/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java @@ -4,7 +4,6 @@ import at.gv.egiz.moazs.MoaZSException; import at.gv.egiz.moazs.repository.DeliveryRepository; import at.gv.egiz.moazs.scheme.MsgResponse; import at.gv.egiz.moazs.service.MsgService; -import at.gv.zustellung.app2mzs.xsd.MsgResponseSinksType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -25,25 +24,19 @@ public class MsgResponseBackend implements Consumer { "with AppDeliveryID=%s is not valid."; public static final String BINARY_RESPONSE_MISSING_ERROR_MSG = "Binary Response is not in repository."; public static final String RESPONSE_MISSING_ERROR_MSG = "Response with ResponseID=%s is not in repository."; + public static final String REQUEST_MISSING_ERROR_MSG = "Request with AppDeliveryID=%s is not in repository."; private final DeliveryRepository repository; private final Consumer signatureVerifier; - private final SaveResponseToFileSink saveResponseSink; - private final LogResponseSink logResponseSink; - private final ForwardResponseToServiceSink forwardResponseSink; + private final MsgResponseSinkHub hub; @Autowired public MsgResponseBackend(DeliveryRepository repository, Consumer signatureVerifier, - SaveResponseToFileSink saveResponseToFileSink, - LogResponseSink logResponseSink, - ForwardResponseToServiceSink forwardResponseSink) { + MsgResponseSinkHub hub) { this.repository = repository; this.signatureVerifier = signatureVerifier; - this.saveResponseSink = saveResponseToFileSink; - this.logResponseSink = logResponseSink; - this.forwardResponseSink = forwardResponseSink; - + this.hub = hub; } /** @@ -58,8 +51,7 @@ public class MsgResponseBackend implements Consumer { */ @Override public void accept(String responseID) { - - supplyAsync(() -> verify(responseID)) + supplyAsync(() -> verifySignature(responseID)) .thenAcceptAsync(msgResponse -> applySinks(msgResponse)) .exceptionally(ex -> { log.error(ex.getMessage(), ex); @@ -67,7 +59,7 @@ public class MsgResponseBackend implements Consumer { }); } - private MsgResponse verify(String responseID) { + private MsgResponse verifySignature(String responseID) { var response = repository.retrieveResponse(responseID).orElseThrow( ()-> moaZSException(format(RESPONSE_MISSING_ERROR_MSG, responseID))); @@ -87,28 +79,12 @@ public class MsgResponseBackend implements Consumer { } private void applySinks(MsgResponse msgResponse) { - - var sinkParams = getSinkParams(msgResponse); - - if (sinkParams.getSaveResponseToFile().isActive()) { - supplyAsync(() -> saveResponseSink.save(msgResponse, sinkParams.getSaveResponseToFile().getPath())); - } - - if (sinkParams.isLogResponse()) { - supplyAsync(() -> logResponseSink.log(msgResponse)); - } - - if (sinkParams.getForwardResponseToService().isActive()) { - supplyAsync(() -> forwardResponseSink.send( - msgResponse, sinkParams.getForwardResponseToService().getMzsClient())); - } - - } - - private MsgResponseSinksType getSinkParams(MsgResponse msgResponse) { var appDeliveryID = msgResponse.getAppDeliveryID(); - var request = repository.retrieveDeliveryRequest(appDeliveryID).get(); - return request.getConfig().getMsgResponseSinks(); + var request = repository.retrieveDeliveryRequest(appDeliveryID).orElseThrow( + ()-> moaZSException(format(REQUEST_MISSING_ERROR_MSG, appDeliveryID))); + + var sinkParams = request.getConfig().getMsgResponseSinks(); + hub.applySinks(msgResponse, sinkParams); } } diff --git a/src/main/java/at/gv/egiz/moazs/backend/MsgResponseSinkHub.java b/src/main/java/at/gv/egiz/moazs/backend/MsgResponseSinkHub.java new file mode 100644 index 0000000..0df5c68 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/backend/MsgResponseSinkHub.java @@ -0,0 +1,44 @@ +package at.gv.egiz.moazs.backend; + +import at.gv.egiz.moazs.scheme.MsgResponse; +import at.gv.zustellung.app2mzs.xsd.MsgResponseSinksType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import static java.util.concurrent.CompletableFuture.supplyAsync; + +@Component +public class MsgResponseSinkHub { + + private final SaveResponseToFileSink saveResponseSink; + private final LogResponseSink logResponseSink; + private final ForwardResponseToServiceSink forwardResponseSink; + + @Autowired + public MsgResponseSinkHub(SaveResponseToFileSink saveResponseSink, LogResponseSink logResponseSink, + ForwardResponseToServiceSink forwardResponseSink) { + this.saveResponseSink = saveResponseSink; + this.logResponseSink = logResponseSink; + this.forwardResponseSink = forwardResponseSink; + } + + /** + * Apply all sinks that are configured in {@code sinkParams} to {@code msgResponse}. + */ + public void applySinks(MsgResponse msgResponse, MsgResponseSinksType sinkParams) { + + if (sinkParams.getSaveResponseToFile().isActive()) { + supplyAsync(() -> saveResponseSink.save(msgResponse, sinkParams.getSaveResponseToFile().getPath())); + } + + if (sinkParams.isLogResponse()) { + supplyAsync(() -> logResponseSink.log(msgResponse)); + } + + if (sinkParams.getForwardResponseToService().isActive()) { + supplyAsync(() -> forwardResponseSink.send( + msgResponse, sinkParams.getForwardResponseToService().getMzsClient())); + } + + } +} diff --git a/src/main/java/at/gv/egiz/moazs/backend/SignatureVerifier.java b/src/main/java/at/gv/egiz/moazs/backend/SignatureVerifier.java index 874e4f4..a19c06a 100644 --- a/src/main/java/at/gv/egiz/moazs/backend/SignatureVerifier.java +++ b/src/main/java/at/gv/egiz/moazs/backend/SignatureVerifier.java @@ -3,7 +3,6 @@ package at.gv.egiz.moazs.backend; import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.ISignatureVerificationService; import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.data.IXMLSignatureVerificationResponse; import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.exceptions.MOASigServiceException; -import at.gv.egiz.moazs.MoaZSException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,19 +20,17 @@ public class SignatureVerifier implements Consumer { private static final String CERT_CODE_ERROR_MSG = "Certificate chain is not valid: Check code was %d. "; private static final String MANIFEST_CODE_ERROR_MSG = "Signature Manifest is not valid: Check code was %d. "; private static final String XMLMANIFEST_CODE_ERROR_MSG = "XmlDSIGManifest is not valid: Check code was %d. "; - - //TODO: Dont make this multiline! private static final String XML_SIGNATURE_RESPONSE_TEMPLATE = - " XmlDsigSubjectName: %s\n" + - " SignatureManifestCheckCode: %s\n" + - " XmlDSIGManifestCheckCode: %s\n" + - " CertificateCheckCode: %s\n" + - " SignatureCheckCode: %s\n" + - " SigningDateTime: %s\n" + - " isXmlDSIGManigest: %s\n" + - " isPublicAuthority: %s\n" + - " isQualifiedCertificate: %s\n" + - " getPublicAuthorityCode: %s\n"; + "XmlDsigSubjectName: %s; " + + "SignatureManifestCheckCode: %s; " + + "XmlDSIGManifestCheckCode: %s; " + + "CertificateCheckCode: %s; " + + "SignatureCheckCode: %s; " + + "SigningDateTime: %s; " + + "isXmlDSIGManigest: %s; " + + "isPublicAuthority: %s; " + + "isQualifiedCertificate: %s; " + + "getPublicAuthorityCode: %s; "; private static final String MOASIG_SERVICE_ERROR_MSG = "MOA SPSS could not accept the XML signature. "; private final ISignatureVerificationService service; @@ -48,8 +45,7 @@ public class SignatureVerifier implements Consumer { } /** - * Verifies the signature of a signed XML document. Throws a at.gv.egiz.moazs.MoaZSException exception - * if the validation fails. + * Verifies the signature of a signed XML document. If the validation fails, it will throw an exception. * @param signedXMLdocument * @throws at.gv.egiz.moazs.MoaZSException */ @@ -107,7 +103,7 @@ public class SignatureVerifier implements Consumer { public static void debug(IXMLSignatureVerificationResponse response) { if (log.isDebugEnabled()) { - var builder = new StringBuilder("Response: \n"); + var builder = new StringBuilder("Response: "); if (response == null) { builder.append("null"); } else { diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java index a3329cd..056f6dc 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java @@ -31,7 +31,7 @@ public class ConfigUtil { public static final String FILENAME_KEY = "filename"; public static final String FILETYPE_KEY = "filetype"; public static final String PASSWORD_KEY = "password"; - public static final String RECEIVE_TIMEOUT = "receive-timeout"; + public static final String RECEIVE_TIMEOUT_KEY = "receive-timeout"; public static final String CONNECTION_TIMEOUT_KEY = "connection-timeout"; public static final String MSG_RESPONSE_SINKS_KEY = "msg-response-sinks"; public static final String LOG_RESPONSE_KEY = "log-response"; @@ -40,6 +40,7 @@ public class ConfigUtil { public static final String SAVE_RESPONSE_TO_FILE_PATH_KEY = "path"; public static final String FORWARD_RESPONSE_TO_SERVICE_KEY = "forward-response-to-service"; public static final String MZS_CLIENT_KEY = "mzs-client"; + public static final String SERVICE_TIMEOUT_KEY = "service-timeout"; /** @@ -64,11 +65,14 @@ public class ConfigUtil { MsgResponseSinksType sinks = msgResponseSinksParams.isEmpty() ? null : buildMsgResponseSinks(msgResponseSinksParams); + var serviceTimeout = bigIntOrNull(values.get(SERVICE_TIMEOUT_KEY)); + return ConfigType.configTypeBuilder() .withPerformQueryPersonRequest(performQueryPersonRequest) .withMSGClient(msgClient) .withTNVZClient(tnvzClient) .withMsgResponseSinks(sinks) + .withServiceTimeout(serviceTimeout) .build(); } @@ -83,13 +87,9 @@ public class ConfigUtil { var url = clientParams.get(URL_KEY); - BigInteger connectionTimeout = clientParams.containsKey(CONNECTION_TIMEOUT_KEY) - ? new BigInteger(clientParams.get(CONNECTION_TIMEOUT_KEY)) - : null; + var connectionTimeout = bigIntOrNull(clientParams.get(CONNECTION_TIMEOUT_KEY)); - BigInteger receiveTimeout = clientParams.containsKey(RECEIVE_TIMEOUT) - ? new BigInteger(clientParams.get(RECEIVE_TIMEOUT)) - : null; + var receiveTimeout = bigIntOrNull(clientParams.get(RECEIVE_TIMEOUT_KEY)); var sslParams = filterMapByPrefix(clientParams, SSL_KEY); SSLType ssl = sslParams.isEmpty() @@ -179,6 +179,11 @@ public class ConfigUtil { return value == null ? null : Boolean.getBoolean(value); } + private BigInteger bigIntOrNull(String value) { + return value == null + ? null + : new BigInteger(value); + } /** * Combine properties of two Configs; {@code primary} overrides {@code fallback}. @@ -207,6 +212,10 @@ public class ConfigUtil { builder.withMsgResponseSinks(merge(primary.getMsgResponseSinks(), fallback.getMsgResponseSinks())); } + if (primary.getServiceTimeout() != null) { + builder.withServiceTimeout(primary.getServiceTimeout()); + } + return builder.build(); } diff --git a/src/main/java/at/gv/egiz/moazs/service/MzsService.java b/src/main/java/at/gv/egiz/moazs/service/MzsService.java index 8f0ef86..c6871da 100644 --- a/src/main/java/at/gv/egiz/moazs/service/MzsService.java +++ b/src/main/java/at/gv/egiz/moazs/service/MzsService.java @@ -1,5 +1,6 @@ package at.gv.egiz.moazs.service; +import at.gv.egiz.moazs.backend.MsgResponseSinkHub; import at.gv.egiz.moazs.preprocess.DeliveryRequestAugmenter; import at.gv.egiz.moazs.repository.DeliveryRepository; import at.gv.egiz.moazs.scheme.Msg2MzsConverter; @@ -7,7 +8,6 @@ import at.gv.egiz.moazs.scheme.RequestStatusResponse; import at.gv.zustellung.app2mzs.xsd.App2MzsPortType; import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType; import at.gv.zustellung.app2mzs.xsd.DeliveryResponseType; -import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType; import org.apache.cxf.annotations.SchemaValidation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,46 +31,57 @@ public class MzsService implements App2MzsPortType { private static final Logger logger = LoggerFactory.getLogger(MzsService.class); - //TODO move timeout and namespaces to config - private static final int TIMEOUT_FOR_ANWSER = 10; private static final String MZS_SERVICE_ERROR_MSG = "An error occurred while processing DeliveryRequest " + "with AppDeliveryID=%s."; private static final String RESPONSE_MISSING_ERROR_MSG = "Could not get a response for AppDeliveryID=%s."; + private static final String SERVICE_TIME_OUT_REACHED_MSG = "Backend processing for DeliveryRequest with " + + "AppDeliveryID=%s timed out. "; private final DeliveryRepository repository; private final Consumer backend; private final DeliveryRequestAugmenter augmenter; private final Msg2MzsConverter converter; + private final MsgResponseSinkHub hub; @Autowired public MzsService(DeliveryRepository repository, Consumer deliveryRequestBackend, - DeliveryRequestAugmenter augmenter, Msg2MzsConverter converter) { + DeliveryRequestAugmenter augmenter, Msg2MzsConverter converter, + MsgResponseSinkHub hub) { this.repository = repository; this.backend = deliveryRequestBackend; this.augmenter = augmenter; this.converter = converter; + this.hub = hub; } @Override public DeliveryResponseType app2Mzs( - @WebParam(partName = "DeliveryRequest", - name = "DeliveryRequest") - DeliveryRequestType deliveryRequest) { + @WebParam(partName = "DeliveryRequest",name = "DeliveryRequest") DeliveryRequestType deliveryRequest) { var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID(); - var responseID = RequestStatusResponse.getResponseID(appDeliveryID); - var future = supplyAsync(() -> augmenter.augment(deliveryRequest)) - .thenApply(this::process) - .thenApply(status -> converter.convert(status, repository.retrieveBinaryResponse(responseID))); + var completeRequest = augmenter.augment(deliveryRequest); + + var requestProcessed = supplyAsync(() -> process(completeRequest)); try { - return future.get(TIMEOUT_FOR_ANWSER, TimeUnit.SECONDS); - } catch (TimeoutException e) { - logger.info("Answer Timed Out", e); + var serviceTimeout = completeRequest.getConfig().getServiceTimeout(); + RequestStatusResponse response; + + if (serviceTimeout == null) { + response = requestProcessed.get(); + } else { + response = requestProcessed.get(serviceTimeout.longValue(), TimeUnit.SECONDS); + } + + var status = response.getResponse(); + var binaryStatus = repository.retrieveBinaryResponse(response.getResponseID()); + return converter.convert(status, binaryStatus); - //TODO: revise how notification should be sent - //future.thenAccept(appClient::sendNotification); + } catch (TimeoutException e) { + logger.info(format(SERVICE_TIME_OUT_REACHED_MSG, appDeliveryID), e); + var sinkParams = completeRequest.getConfig().getMsgResponseSinks(); + requestProcessed.thenAcceptAsync(response -> hub.applySinks(response, sinkParams)); return generatePartialSuccessResponse(appDeliveryID); } catch (Exception e) { @@ -80,19 +91,18 @@ public class MzsService implements App2MzsPortType { } - private DeliveryRequestStatusType process(DeliveryRequestType deliveryRequest) { + private RequestStatusResponse process(DeliveryRequestType deliveryRequest) { var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID(); - //TODO: fix too. - logger.info("Receive request with appDeliveryID = {}.", appDeliveryID); repository.store(deliveryRequest); backend.accept(appDeliveryID); - var statusId = RequestStatusResponse.getResponseID(appDeliveryID); - var response = repository.retrieveResponse(statusId) + var responseID = RequestStatusResponse.getResponseID(appDeliveryID); + + return (RequestStatusResponse) repository.retrieveResponse(responseID) .orElseThrow(() -> moaZSException(format(RESPONSE_MISSING_ERROR_MSG, appDeliveryID))); - return (DeliveryRequestStatusType) response.getResponse(); + } private DeliveryResponseType generatePartialSuccessResponse(String appDeliveryId) { -- cgit v1.2.3