diff options
author | Christof Rabensteiner <christof.rabensteiner@iaik.tugraz.at> | 2019-07-12 15:40:05 +0200 |
---|---|---|
committer | Christof Rabensteiner <christof.rabensteiner@iaik.tugraz.at> | 2019-07-12 15:40:05 +0200 |
commit | 25d68c8900c2cc791f03ea3db173955ca237fd55 (patch) | |
tree | f4d28b97845d10a709590dce480195b322ba019b /src/main/java/at | |
parent | 9dc0e72571a895e34a55c11d015c5d359b485aff (diff) | |
download | moa-zs-25d68c8900c2cc791f03ea3db173955ca237fd55.tar.gz moa-zs-25d68c8900c2cc791f03ea3db173955ca237fd55.tar.bz2 moa-zs-25d68c8900c2cc791f03ea3db173955ca237fd55.zip |
Allow App To Choose Between MsgResponse Sinks
- MZS Schema Change: Add "MsgResponseSinks" element to
mzs:DeliveryRequest/Config that allows sender to configure how
MsgResponses should be archived.
- ConfigUtil: Interpret MsgResponseSink parameters from Spring
Environment and merge with ConfigType.
- MsgResponseBackend: Send responses to sinks according to
MsgResponseSinks in Config
- application.yaml: Add MsgResponseSinks parameter to configuration.
- Uncouple Sink implementations from java.util.function.Function,
because the sink interfaces are going to differ and there is no need
to unite them under one interface.
- Add and test LogResponseSink, which logs responses to it's logger.
- MsgResponse: Add JAXB getter for response. Reason: Can be passed to
marshaller.
Diffstat (limited to 'src/main/java/at')
7 files changed, 166 insertions, 21 deletions
diff --git a/src/main/java/at/gv/egiz/moazs/backend/LogResponseSink.java b/src/main/java/at/gv/egiz/moazs/backend/LogResponseSink.java new file mode 100644 index 0000000..d419944 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/backend/LogResponseSink.java @@ -0,0 +1,44 @@ +package at.gv.egiz.moazs.backend; + +import at.gv.egiz.moazs.scheme.Marshaller; +import at.gv.egiz.moazs.scheme.MsgResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.concurrent.CompletableFuture; + +import static java.util.concurrent.CompletableFuture.completedFuture; + +@Component +public class LogResponseSink { + + private static final Logger log = LoggerFactory.getLogger(LogResponseSink.class); + private static final String LOG_LEVEL_WARN_MESSAGE = "Will not log response because INFO level is disabled."; + + private final Marshaller msgMarshaller; + + public LogResponseSink(Marshaller msgMarshaller) { + this.msgMarshaller = msgMarshaller; + } + + /** + * Log response to class logger at level=INFO. + * + * @param msgResponse + * @return + */ + public CompletableFuture<Void> log(MsgResponse msgResponse) { + + if(log.isInfoEnabled()) { + var builder = new StringBuilder("Received the following Message: \n"); + builder.append(msgMarshaller.marshallXml(msgResponse.getResponseAsJAXBElement())); + log.info(builder.toString()); + } else { + log.warn(LOG_LEVEL_WARN_MESSAGE); + } + + return completedFuture(null); + + } +} 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 414c2dc..1b17f34 100644 --- a/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java +++ b/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java @@ -4,14 +4,13 @@ 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; import org.springframework.stereotype.Component; -import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.function.Function; import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder; import static java.lang.String.format; @@ -27,29 +26,38 @@ public class MsgResponseBackend implements Consumer<String> { private final DeliveryRepository repository; private final Consumer<byte[]> signatureVerifier; - private final Function<MsgResponse, CompletableFuture<Void>> sink; + private final SaveResponseToFileSink saveResponseToFileSink; + private final LogResponseSink logResponseSink; @Autowired public MsgResponseBackend(DeliveryRepository repository, Consumer<byte[]> signatureVerifier, - Function<MsgResponse, CompletableFuture<Void>> sink) { + SaveResponseToFileSink saveResponseToFileSink, + LogResponseSink logResponseSink) { this.repository = repository; this.signatureVerifier = signatureVerifier; - this.sink = sink; + this.saveResponseToFileSink = saveResponseToFileSink; + this.logResponseSink = logResponseSink; } /** * Performs all {@code MsgResponse}'s Back-End Tasks, such as verifying * its signature and archiving the response. * + * Note: When the signature verification fails, this method will not archive + * the original response (the one that was received from msg) to the sink, + * but an error message. + * * @param responseID refers to MsgResponse Object. */ @Override public void accept(String responseID) { - supplyAsync(() -> verify(responseID)).thenAcceptAsync(sink::apply); + + supplyAsync(() -> verify(responseID)) + .thenAcceptAsync(msgResponse -> applySinks(msgResponse)); } - public MsgResponse verify(String responseID) { + private MsgResponse verify(String responseID) { var response = repository.retrieveResponse(responseID).get(); var builder = moaZSExceptionBuilder().withAllParametersInAnswer(response.getAnswer()); @@ -72,4 +80,23 @@ public class MsgResponseBackend implements Consumer<String> { } + private void applySinks(MsgResponse msgResponse) { + + var sinkParams = getSinkParams(msgResponse); + + if (sinkParams.isSafeResponseToFile()) { + supplyAsync(() -> saveResponseToFileSink.save(msgResponse)); + } + + if (sinkParams.isLogResponse()) { + supplyAsync(() -> logResponseSink.log(msgResponse)); + } + } + + private MsgResponseSinksType getSinkParams(MsgResponse msgResponse) { + var appDeliveryID = msgResponse.getAppDeliveryID(); + var request = repository.retrieveDeliveryRequest(appDeliveryID).get(); + return request.getConfig().getMsgResponseSinks(); + } + } diff --git a/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java b/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java index 02771a9..7da76ef 100644 --- a/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java +++ b/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java @@ -15,7 +15,6 @@ import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.CompletableFuture; -import java.util.function.Function; import static at.gv.egiz.moazs.MoaZSException.moaZSException; import static java.lang.String.format; @@ -23,10 +22,11 @@ import static java.util.concurrent.CompletableFuture.allOf; import static java.util.concurrent.CompletableFuture.supplyAsync; @Component -public class SaveResponseToFileSink implements Function<MsgResponse, CompletableFuture<Void>> { +public class SaveResponseToFileSink { private static final Logger log = LoggerFactory.getLogger(SaveResponseToFileSink.class); private static final String SAVING_FAILED_MSG = "Could not save response with AppDeliveryId=%s."; + private static final String SAVE_RESPONSE_MSG = "Saving response to {}..." ; private final SimpleDateFormat isoFormatter; private final Marshaller msgMarshaller; @@ -41,8 +41,13 @@ public class SaveResponseToFileSink implements Function<MsgResponse, Completable this.isoFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); } - @Override - public CompletableFuture<Void> apply(MsgResponse response) { + /** + * Save response and it's binary version to the file system. + * + * @param response + * @return Future that completes when both responses have been written to the file system. + */ + public CompletableFuture<Void> save(MsgResponse response) { var responseID = response.getResponseID(); @@ -79,6 +84,7 @@ public class SaveResponseToFileSink implements Function<MsgResponse, Completable } private void storeToFile(String path, byte[] content) { + log.trace(SAVE_RESPONSE_MSG, path); try { FileUtils.writeByteArrayToFile(new File(path), content); } catch (IOException e) { 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 68f833e..9cdc07a 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java @@ -1,10 +1,7 @@ package at.gv.egiz.moazs.preprocess; import at.gv.egiz.moazs.util.StringUtils; -import at.gv.zustellung.app2mzs.xsd.ClientType; -import at.gv.zustellung.app2mzs.xsd.ConfigType; -import at.gv.zustellung.app2mzs.xsd.KeyStoreType; -import at.gv.zustellung.app2mzs.xsd.SSLType; +import at.gv.zustellung.app2mzs.xsd.*; import org.springframework.stereotype.Component; import java.math.BigInteger; @@ -13,6 +10,7 @@ import java.util.Map; import static at.gv.zustellung.app2mzs.xsd.ClientType.clientTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.ConfigType.configTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder; +import static at.gv.zustellung.app2mzs.xsd.MsgResponseSinksType.msgResponseSinksTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.SSLType.SSLTypeBuilder; import static java.util.stream.Collectors.toMap; @@ -33,7 +31,9 @@ public class ConfigUtil { public static final String PASSWORD_KEY = "password"; public static final String RECEIVE_TIMEOUT = "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"; + public static final String SAVE_RESPONSE_TO_FILE_KEY = "save-response-to-file"; /** * Convert a map into a Config object. @@ -53,10 +53,15 @@ public class ConfigUtil { ClientType tnvzClient = tnvzClientParams.isEmpty() ? null : buildClient(tnvzClientParams); + var msgResponseSinksParams = filterMapByPrefix(values, MSG_RESPONSE_SINKS_KEY); + MsgResponseSinksType sinks = msgResponseSinksParams.isEmpty() + ? null : buildMsgResponseSinks(msgResponseSinksParams); + return ConfigType.configTypeBuilder() .withPerformQueryPersonRequest(performQueryPersonRequest) .withMSGClient(msgClient) .withTNVZClient(tnvzClient) + .withMsgResponseSinks(sinks) .build(); } @@ -102,11 +107,8 @@ public class ConfigUtil { KeyStoreType trustStore = trustStoreParams.isEmpty() ? null : buildKeyStore(trustStoreParams); - var trustAll = sslParams.get(TRUST_ALL_KEY) == null - ? null : Boolean.getBoolean(sslParams.get(TRUST_ALL_KEY)); - - var laxHostNameVerification = sslParams.get(LAX_HOSTNAME_VERIFICATION_KEY) == null - ? null : Boolean.getBoolean(sslParams.get(LAX_HOSTNAME_VERIFICATION_KEY)); + var trustAll = booleanOrNull(sslParams.get(TRUST_ALL_KEY)); + var laxHostNameVerification = booleanOrNull(sslParams.get(LAX_HOSTNAME_VERIFICATION_KEY)); return SSLTypeBuilder() .withKeyStore(keyStore) @@ -126,6 +128,22 @@ public class ConfigUtil { .build(); } + private MsgResponseSinksType buildMsgResponseSinks(Map<String, String> params) { + + var logResponse = booleanOrNull(params.get(LOG_RESPONSE_KEY)); + var saveResponse = booleanOrNull(params.get(SAVE_RESPONSE_TO_FILE_KEY)); + + return msgResponseSinksTypeBuilder() + .withLogResponse(logResponse) + .withLogResponse(saveResponse) + .build(); + } + + private Boolean booleanOrNull(String value) { + return value == null ? null : Boolean.getBoolean(value); + } + + /** * Combine properties of two Configs; {@code primary} overrides {@code fallback}. * @@ -149,6 +167,10 @@ public class ConfigUtil { builder.withMSGClient(merge(primary.getTNVZClient(), fallback.getTNVZClient())); } + if (primary.getMsgResponseSinks() != null) { + builder.withMsgResponseSinks(merge(primary.getMsgResponseSinks(), fallback.getMsgResponseSinks())); + } + return builder.build(); } @@ -214,4 +236,24 @@ public class ConfigUtil { } + private MsgResponseSinksType merge(MsgResponseSinksType primary, MsgResponseSinksType fallback) { + + if (fallback == null) { + return primary; + } + + var builder = msgResponseSinksTypeBuilder(fallback); + + if (primary.isLogResponse() != null) { + builder.withLogResponse(primary.isLogResponse()); + } + + if (primary.isSafeResponseToFile() != null) { + builder.withLogResponse(primary.isSafeResponseToFile()); + } + + return builder.build(); + } + + } diff --git a/src/main/java/at/gv/egiz/moazs/scheme/MsgResponse.java b/src/main/java/at/gv/egiz/moazs/scheme/MsgResponse.java index 80e2059..5370448 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/MsgResponse.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/MsgResponse.java @@ -3,6 +3,12 @@ package at.gv.egiz.moazs.scheme; import at.gv.egiz.moazs.MoaZSException; import at.gv.zustellung.msg.xsd.DeliveryAnswerType; +import javax.xml.bind.JAXBElement; + +/** + * Represents responses to DeliveryRequests that were received from the msg service. + * @param <T> The type of the response. + */ public abstract class MsgResponse <T> { protected String id; @@ -20,6 +26,7 @@ public abstract class MsgResponse <T> { } public abstract T getResponse(); + public abstract JAXBElement<T> getResponseAsJAXBElement(); public abstract String getAppDeliveryID(); public abstract String getZSDeliveryID(); public abstract DeliveryAnswerType getAnswer(); diff --git a/src/main/java/at/gv/egiz/moazs/scheme/NotificationResponse.java b/src/main/java/at/gv/egiz/moazs/scheme/NotificationResponse.java index de1fd38..784d000 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/NotificationResponse.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/NotificationResponse.java @@ -3,6 +3,9 @@ package at.gv.egiz.moazs.scheme; import at.gv.egiz.moazs.MoaZSException; import at.gv.zustellung.msg.xsd.DeliveryAnswerType; import at.gv.zustellung.msg.xsd.DeliveryNotificationType; +import at.gv.zustellung.msg.xsd.ObjectFactory; + +import javax.xml.bind.JAXBElement; import static at.gv.zustellung.msg.xsd.DeliveryNotificationType.deliveryNotificationTypeBuilder; @@ -10,6 +13,7 @@ public class NotificationResponse extends MsgResponse<DeliveryNotificationType> private final DeliveryNotificationType notification; private static final String ID_SUFFIX = ".NO"; + private static final ObjectFactory factory = new ObjectFactory(); public NotificationResponse(DeliveryNotificationType notification) { super.id = createResponseId(notification.getAppDeliveryID(), ID_SUFFIX); @@ -26,6 +30,11 @@ public class NotificationResponse extends MsgResponse<DeliveryNotificationType> } @Override + public JAXBElement<DeliveryNotificationType> getResponseAsJAXBElement() { + return factory.createDeliveryNotification(notification); + } + + @Override public String getAppDeliveryID() { return notification.getAppDeliveryID(); } diff --git a/src/main/java/at/gv/egiz/moazs/scheme/RequestStatusResponse.java b/src/main/java/at/gv/egiz/moazs/scheme/RequestStatusResponse.java index 0705698..3b4710b 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/RequestStatusResponse.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/RequestStatusResponse.java @@ -4,6 +4,9 @@ import at.gv.egiz.moazs.MoaZSException; import at.gv.zustellung.msg.xsd.DeliveryAnswerType; import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType; import at.gv.zustellung.msg.xsd.ErrorInfoType; +import at.gv.zustellung.msg.xsd.ObjectFactory; + +import javax.xml.bind.JAXBElement; import static at.gv.egiz.moazs.util.NullCoalesce.coalesce; import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.Error.errorBuilder; @@ -15,6 +18,8 @@ public class RequestStatusResponse extends MsgResponse<DeliveryRequestStatusType private final DeliveryRequestStatusType status; private final DeliveryAnswerType answer; private static final String ID_SUFFIX = ".RS"; + private static final ObjectFactory factory = new ObjectFactory(); + public RequestStatusResponse(DeliveryRequestStatusType status) { this.status = status; @@ -32,6 +37,11 @@ public class RequestStatusResponse extends MsgResponse<DeliveryRequestStatusType } @Override + public JAXBElement<DeliveryRequestStatusType> getResponseAsJAXBElement() { + return factory.createDeliveryRequestStatus(status); + } + + @Override public String getAppDeliveryID() { return answer.getAppDeliveryID(); } |