From cb9a76eaafd37f921006822bcfe043655288bc63 Mon Sep 17 00:00:00 2001 From: Christof Rabensteiner Date: Mon, 22 Jul 2019 13:02:19 +0200 Subject: Test Flow of DeliveryRequest from "End-To-End" & Fix Bugs Schema Changes: - Remove mzs:DeliveryRequest/TnvzMetaData because all metadata fields can be collected from DeliveryRequest and redundancy is not needed. Fixes and Refactoring in preprocess: - MzsDeliveryRequestValidator: Instead of returning false, throw an exception when a condition is not met, and explain which condition is not met / why it is not met in the exception's message. - Integrate interface change in ConfigProfileGenerator and DeliveryRequestAugmenter. - Rewrite and simplify DeliveryRequestAugmenter's augmentation. - ConfigUtil Fixes: Ensure that we do not override the wrong parameters while merging. This error appeared in tnvz / msg client, connection / receive timeout, key / trust store, and lax hostname verification / trust all. Fix Bugs in Interceptor / SoapUtils: - Problem: DOM access and information extraction was implemented somewhat sloppy. - SolutioN: Change DOM access interface to access DOM more efficiently. Add boundary checks and handle edge cases while extracting information from SOAP Messages. - Test those changes properly. Testing: - Implement Delivery Request Flow in ITEndToEndTest. - Start application on random port instead of fixed port when running integration tests. - Add assertions to tests in ITMzsServiceTest suite. Others Bug Fixes: - ServicesConfig: Ensure that mzs service and msg service run on different endpoint addresses (/msg and /mzs). - DeliveryRequestBackend: Throw exception when binary message is missing. Don't wrap the exception. - SaveResponseToFileSink: Wrap Response in JAXB Element (otherwise, marshaller does not recognize it) --- .../egiz/moazs/backend/DeliveryRequestBackend.java | 13 +- .../egiz/moazs/backend/SaveResponseToFileSink.java | 2 +- .../java/at/gv/egiz/moazs/client/TnvzHelper.java | 8 +- .../at/gv/egiz/moazs/config/ServicesConfig.java | 7 +- .../moazs/preprocess/ConfigProfileGenerator.java | 8 +- .../at/gv/egiz/moazs/preprocess/ConfigUtil.java | 9 +- .../moazs/preprocess/DeliveryRequestAugmenter.java | 70 ++++----- .../preprocess/MzsDeliveryRequestValidator.java | 160 +++++++++++++++------ .../repository/InMemoryDeliveryRepository.java | 15 +- .../java/at/gv/egiz/moazs/scheme/Marshaller.java | 4 + .../java/at/gv/egiz/moazs/scheme/NameSpace.java | 5 + .../java/at/gv/egiz/moazs/scheme/SOAPUtils.java | 64 +++++++-- .../at/gv/egiz/moazs/util/EndpointFactory.java | 8 +- ...StoreSOAPBodyBinaryInRepositoryInterceptor.java | 20 ++- 14 files changed, 267 insertions(+), 126 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java b/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java index 6a1e0fd..72f8ba0 100644 --- a/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java +++ b/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java @@ -18,10 +18,10 @@ import org.springframework.stereotype.Component; import java.util.function.Consumer; -import static at.gv.egiz.moazs.scheme.RequestStatusResponse.*; +import static at.gv.egiz.moazs.MoaZSException.moaZSException; import static at.gv.egiz.moazs.scheme.RequestStatusResponse.generateError; +import static at.gv.egiz.moazs.scheme.RequestStatusResponse.getAnswer; import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.Error.errorBuilder; -import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.deliveryRequestStatusTypeBuilder; import static java.lang.String.format; @Component @@ -72,7 +72,7 @@ public class DeliveryRequestBackend implements Consumer { try { var mzsRequest = repository.retrieveDeliveryRequest(appDeliveryID).orElseThrow( - () -> MoaZSException.moaZSException(format(DELIVERY_REQUEST_MISSING_ERROR_MSG, appDeliveryID))); + () -> moaZSException(format(DELIVERY_REQUEST_MISSING_ERROR_MSG, appDeliveryID))); fallbackAnswerBuilder.withDeliverySystem(mzsRequest.getConfig().getMSGClient().getURL()); var msgRequest = buildMsgRequest(mzsRequest); @@ -98,14 +98,15 @@ public class DeliveryRequestBackend implements Consumer { } private void verifySignedStatus(String responseID, String appDeliveryID) throws MoaZSException { + var signedStatus = repository.retrieveBinaryResponse(responseID).orElseThrow(() -> moaZSException( + format(BINARY_RESPONSE_MISSING_ERROR_MSG, responseID), + MoaZSException.ERROR_MZS_BINARY_RESPONSE_MISSING)); try { - var signedStatus = repository.retrieveBinaryResponse(responseID).orElseThrow( - () -> MoaZSException.moaZSException(format(BINARY_RESPONSE_MISSING_ERROR_MSG, responseID))); signatureVerifier.accept(signedStatus); } catch (MoaZSException ex) { var message = format(MsgResponseBackend.MOASP_SIGNATURE_INVALID_ERROR_MSG, appDeliveryID); var code = MoaZSException.ERROR_MOASP_SIGNATURE_INVALID; - throw MoaZSException.moaZSException(message, code, ex); + throw moaZSException(message, code, ex); } } 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 2468ca9..c58e1a5 100644 --- a/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java +++ b/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java @@ -50,7 +50,7 @@ public class SaveResponseToFileSink { var responseID = response.getResponseID(); var responsePath = generatePath(rootPath, responseID, "xml"); - var storeResponseToFileSystemFuture = supplyAsync(() -> msgMarshaller.marshallXml(response.getResponse())) + var storeResponseToFileSystemFuture = supplyAsync(() -> msgMarshaller.marshallXml(response.getResponseAsJAXBElement())) .thenApply(responseString -> responseString.getBytes(StandardCharsets.UTF_8)) .thenAccept(responseByteArray -> storeToFile(responsePath, responseByteArray)) .exceptionally(ex -> logException(ex, responseID)); diff --git a/src/main/java/at/gv/egiz/moazs/client/TnvzHelper.java b/src/main/java/at/gv/egiz/moazs/client/TnvzHelper.java index de22805..a304bdf 100644 --- a/src/main/java/at/gv/egiz/moazs/client/TnvzHelper.java +++ b/src/main/java/at/gv/egiz/moazs/client/TnvzHelper.java @@ -96,18 +96,16 @@ public class TnvzHelper { var builder = metaDataBuilder(); - var meta = request.getTnvzMetaData(); - + var meta = request.getMetaData(); if (meta.getDeliveryQuality() != null) { builder.withDeliveryQuality(meta.getDeliveryQuality()); } else { builder.withPrivateMessageQuality(meta.getPrivateMessageQuality()); } - return builder - .withOrigin(meta.getOrigin()) - .withPreAdviceNote(request.getReceiver().getPreAdviceNote()) + return builder.withOrigin(meta.getOrigin()) .withIgnorePostRedirectionOrder(meta.getIgnorePostRedirectionOrder()) + .withPreAdviceNote(request.getReceiver().getPreAdviceNote()) .build(); } diff --git a/src/main/java/at/gv/egiz/moazs/config/ServicesConfig.java b/src/main/java/at/gv/egiz/moazs/config/ServicesConfig.java index c16dfd2..6f71e50 100644 --- a/src/main/java/at/gv/egiz/moazs/config/ServicesConfig.java +++ b/src/main/java/at/gv/egiz/moazs/config/ServicesConfig.java @@ -23,14 +23,15 @@ public class ServicesConfig { @Autowired public Endpoint msgEndpoint(MsgService msgService, Zuse2AppPortService zuse2app, - Interceptor msgInterceptor) { - return endpointFactory.create(msgService, zuse2app, msgInterceptor); + Interceptor msgInterceptor + ) { + return endpointFactory.create(msgService, zuse2app, "/msg", msgInterceptor); } @Bean @Autowired public Endpoint mzsEndpoint(MzsService mzsService, App2Mzs app2mzs) { - return endpointFactory.create(mzsService, app2mzs); + return endpointFactory.create(mzsService, app2mzs, "/mzs"); } @Bean diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java index 5e81f0d..0637f98 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java @@ -11,6 +11,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import static at.gv.egiz.moazs.MoaZSException.moaZSException; +import static java.lang.String.format; import static java.util.stream.Collectors.*; public class ConfigProfileGenerator { @@ -70,9 +72,11 @@ public class ConfigProfileGenerator { var defaultProfile = profiles.get(defaultConfigKey); - if (!validator.isConfigProfileComplete(defaultProfile)) { + try { + validator.isConfigProfileComplete(defaultProfile); + } catch (MoaZSException ex) { if (verifyCompletenessOfDefaultConfiguration) - throw MoaZSException.moaZSException(PROFILE_NOT_COMPLETE_ERROR_MESSAGE); + throw moaZSException(format("%s Reason: %s", PROFILE_NOT_COMPLETE_ERROR_MESSAGE, ex.getMessage())); else { LOGGER.warn(PROFILE_NOT_COMPLETE_WARNING_MESSAGE); } 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 056f6dc..f49132f 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java @@ -205,7 +205,7 @@ public class ConfigUtil { } if (primary.getTNVZClient() != null) { - builder.withMSGClient(merge(primary.getTNVZClient(), fallback.getTNVZClient())); + builder.withTNVZClient(merge(primary.getTNVZClient(), fallback.getTNVZClient())); } if (primary.getMsgResponseSinks() != null) { @@ -240,7 +240,7 @@ public class ConfigUtil { } if (primary.getReceiveTimeout() != null) { - builder.withConnectionTimeout(primary.getReceiveTimeout()); + builder.withReceiveTimeout(primary.getReceiveTimeout()); } return builder.build(); @@ -259,7 +259,7 @@ public class ConfigUtil { } if (primary.getTrustStore() != null) { - builder.withKeyStore(merge(primary.getTrustStore(), fallback.getTrustStore())); + builder.withTrustStore(merge(primary.getTrustStore(), fallback.getTrustStore())); } if (primary.isLaxHostNameVerification() != null) { @@ -267,8 +267,9 @@ public class ConfigUtil { } if (primary.isTrustAll() != null) { - builder.withLaxHostNameVerification(primary.isTrustAll()); + builder.withTrustAll(primary.isTrustAll()); } + return builder.build(); } diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java b/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java index e7ee357..240a677 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java @@ -1,36 +1,41 @@ package at.gv.egiz.moazs.preprocess; +import at.gv.egiz.moazs.scheme.Marshaller; import at.gv.zustellung.app2mzs.xsd.ConfigType; import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType; +import at.gv.zustellung.app2mzs.xsd.ObjectFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import java.util.Map; import static at.gv.egiz.moazs.MoaZSException.moaZSException; +import static at.gv.egiz.moazs.util.NullCoalesce.coalesce; import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.deliveryRequestTypeBuilder; -import static java.lang.String.format; @Component public class DeliveryRequestAugmenter { + private static final Logger log = LoggerFactory.getLogger(DeliveryRequestAugmenter.class); + private static final ObjectFactory FACTORY = new ObjectFactory(); + private final ConfigUtil util; private final Map configs; private final MzsDeliveryRequestValidator validator; + private final Marshaller mzsMarshaller; - private static final String INCOMPLETE_TNVZ_ERROR_MESSAGE = "mzs:DeliveryRequest is incomplete because mandatory " + - "fields for sending a tnvz:QueryPersonRequest are missing."; - private static final String INCOMPLETE_CONFIG_ERROR_MESSAGE = "Could not find a profile for " + - "the delivery request configuration, and the configuration attached to mzs:DeliveryRequest is incomplete."; - private static final String INCOMPLETE_MERGED_CONFIG_ERROR_MESSAGE = "I merged parameters from " + - "mzs:DeliveryRequest/Config with parameters from config profile with ProfileId='%s', but the " + - "configuration is incomplete."; + private static final String CONFIG_MISSING_ERROR_MSG = "Delivery request configuration is missing."; @Autowired - public DeliveryRequestAugmenter(Map deliveryRequestConfigs, ConfigUtil util, MzsDeliveryRequestValidator validator) { + public DeliveryRequestAugmenter(Map deliveryRequestConfigs, ConfigUtil util, + MzsDeliveryRequestValidator validator, Marshaller mzsMarshaller) { this.configs = deliveryRequestConfigs; this.util = util; this.validator = validator; + this.mzsMarshaller = mzsMarshaller; } /** @@ -46,41 +51,29 @@ public class DeliveryRequestAugmenter { var fallbackProfileId = determineProfileIdFrom(requestConfig); var fallbackConfig = configs.get(fallbackProfileId); - if (fallbackConfig == null) { - - if (!validator.isConfigProfileComplete(request.getConfig())) { - throw moaZSException(INCOMPLETE_CONFIG_ERROR_MESSAGE); - } else if (!validator.isTnvzComplete(request)) { - throw moaZSException(INCOMPLETE_TNVZ_ERROR_MESSAGE); - } else { - return request; - } + trace("request config", requestConfig); + trace("fallback config", fallbackConfig); - } else { + var augmentedConfig = (requestConfig != null && fallbackConfig != null) + ? util.merge(requestConfig, fallbackConfig) + : coalesce(requestConfig, fallbackConfig) + .orElseThrow(()-> moaZSException(CONFIG_MISSING_ERROR_MSG)); - var mergedConfig = (requestConfig == null) - ? fallbackConfig - : util.merge(requestConfig, fallbackConfig); + trace("augmented config", augmentedConfig); - if (!validator.isConfigProfileComplete(mergedConfig)) { - var message = format(INCOMPLETE_MERGED_CONFIG_ERROR_MESSAGE, fallbackProfileId); - throw moaZSException(message); - } + validator.isConfigProfileComplete(augmentedConfig); - var mergedRequest = deliveryRequestTypeBuilder(request) - .withConfig(mergedConfig) - .build(); + var augmentedRequest = deliveryRequestTypeBuilder(request) + .withConfig(augmentedConfig) + .build(); - if (!validator.isTnvzComplete(mergedRequest)) { - throw moaZSException(INCOMPLETE_TNVZ_ERROR_MESSAGE); - } + validator.isTnvzComplete(augmentedRequest); - return mergedRequest; + return augmentedRequest; - } } - private String determineProfileIdFrom(ConfigType requestConfig) { + private String determineProfileIdFrom(@Nullable ConfigType requestConfig) { return (requestConfig == null || requestConfig.getProfileID() == null || isProfileMissing(requestConfig.getProfileID())) @@ -92,4 +85,11 @@ public class DeliveryRequestAugmenter { return !configs.containsKey(id); } + private void trace(String description, ConfigType config) { + if (log.isTraceEnabled()) { + log.trace("{} : {}", description, mzsMarshaller.marshallXml(FACTORY.createConfig(config))); + } + } + + } diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/MzsDeliveryRequestValidator.java b/src/main/java/at/gv/egiz/moazs/preprocess/MzsDeliveryRequestValidator.java index 2c2fc36..67086a2 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/MzsDeliveryRequestValidator.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/MzsDeliveryRequestValidator.java @@ -1,90 +1,168 @@ package at.gv.egiz.moazs.preprocess; +import at.gv.egiz.moazs.MoaZSException; import at.gv.zustellung.app2mzs.xsd.*; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; +import static at.gv.egiz.moazs.MoaZSException.moaZSException; +import static at.gv.egiz.moazs.scheme.NameSpace.*; +import static java.lang.String.format; + @Component public class MzsDeliveryRequestValidator { /** * Checks if the mandatory fields that are needed to send a tnvz:QueryPersonRequest are present. * @param request - * @return true if mandatory fields are present. + * @throws MoaZSException if a field is missing. */ - public boolean isTnvzComplete(DeliveryRequestType request) { - return !request.getConfig().isPerformQueryPersonRequest() || - (request.getTnvzMetaData() != null - && request.getSender().getCorporateBody() != null); + public void isTnvzComplete(DeliveryRequestType request) { + if (request.getConfig().isPerformQueryPersonRequest()) { + + if (request.getMetaData().getOrigin() == null) + throw mzse(MZS_DELIVERY_REQUEST, MZS_DELIVERY_REQUEST + "/MetaData/Origin is missing."); + + if (request.getMetaData().getDeliveryQuality() == null) + throw mzse(MZS_DELIVERY_REQUEST, MZS_DELIVERY_REQUEST + "/MetaData/DeliveryQuality missing."); + + if (request.getSender().getCorporateBody() == null) + throw mzse(MZS_DELIVERY_REQUEST, MZS_DELIVERY_REQUEST + "/Sender/CorporateBody is missing."); + } } /** * Check if all mandatory fields of configuration are present. * * @param profile - * @return true if all mandatory fields are present. + * @throws MoaZSException if a field is missing. */ - public boolean isConfigProfileComplete(@Nullable ConfigType profile) { - return profile != null - && profile.isPerformQueryPersonRequest() != null - && isTVNZClientConfigured(profile.getTNVZClient(), profile.isPerformQueryPersonRequest()) - && isClientConfigured(profile.getMSGClient()) - && areSinksConfigured(profile.getMsgResponseSinks()); + public void isConfigProfileComplete(@Nullable ConfigType profile) { + + if (profile == null) + throw mzse(MZS_DELIVERY_REQUEST + "/Config"); + + if (profile.isPerformQueryPersonRequest() == null) + throw mzse(MZS_DELIVERY_REQUEST + "/Config", "PerformQueryPersonRequest is missing."); + + isTNVZClientConfigured(profile.getTNVZClient(), profile.isPerformQueryPersonRequest()); + areSinksConfigured(profile.getMsgResponseSinks()); + + try { + isClientConfigured(profile.getMSGClient()); + } catch (MoaZSException ex) { + throw mzse(MZS_MSGCLIENT, ex.getMessage()); + } + } - private boolean isTVNZClientConfigured(ClientType tnvzClient, Boolean isPerformQueryPersonRequest) { - return !isPerformQueryPersonRequest || (tnvzClient != null + private void isTNVZClientConfigured(@Nullable ClientType tnvzClient, Boolean isPerformQueryPersonRequest) { + if (!isPerformQueryPersonRequest) return; + + var isConfigured = tnvzClient != null && tnvzClient.getURL() != null && tnvzClient.getReceiveTimeout() != null - && tnvzClient.getConnectionTimeout() != null - && isSSLConfigured(tnvzClient)); + && tnvzClient.getConnectionTimeout() != null; + + if (!isConfigured) { + if(tnvzClient == null) throw mzse(MZS_TNVZCLIENT); + + var reasons = new StringBuilder("The following elements in " + MZS_TNVZCLIENT + " are missing: "); + if(tnvzClient.getURL() == null) reasons.append("URL;"); + if(tnvzClient.getReceiveTimeout() == null) reasons.append("ReceiveTimeout;"); + if(tnvzClient.getConnectionTimeout() == null) reasons.append("ConnectionTimeout;"); + throw mzse(MZS_TNVZCLIENT, reasons.toString()); + } + + try { + isSSLConfigured(tnvzClient); + } catch (MoaZSException ex) { + throw mzse(MZS_TNVZCLIENT, ex.getMessage()); + } } - private boolean isClientConfigured(ClientType clientParams) { - return clientParams != null + private void isClientConfigured(@Nullable ClientType clientParams) { + var isConfigured = clientParams != null && clientParams.getURL() != null - && isSSLConfigured(clientParams) && clientParams.getReceiveTimeout() != null && clientParams.getConnectionTimeout() != null; + + if (!isConfigured) throw mzse("Client"); + + isSSLConfigured(clientParams); + } - private boolean isSSLConfigured(ClientType clientParams) { - return !clientParams.getURL().startsWith("https") || (clientParams.getSSL() != null - && clientParams.getSSL().isTrustAll() != null - && clientParams.getSSL().isLaxHostNameVerification() != null - && isKeyStoreConfigured(clientParams.getSSL().getKeyStore()) - && isTrustStoreConfigured(clientParams.getSSL().getTrustStore())); + private void isSSLConfigured(ClientType clientParams) { + if (!clientParams.getURL().startsWith("https")) return; + + var isConfigured = (clientParams.getSSL() != null + && clientParams.getSSL().isTrustAll() != null + && clientParams.getSSL().isLaxHostNameVerification() != null); + if (!isConfigured) throw mzse("SSL"); + + try { + isKeyStoreConfigured(clientParams.getSSL().getKeyStore()); + isTrustStoreConfigured(clientParams.getSSL().getTrustStore()); + } catch (MoaZSException ex) { + throw mzse("SSL", ex.getMessage()); + } } - private boolean isKeyStoreConfigured(KeyStoreType keyStore) { - return keyStore == null || (keyStore.getPassword() != null + private void isKeyStoreConfigured(@Nullable KeyStoreType keyStore) { + if (keyStore == null) return; + + var isConfigured = keyStore.getPassword() != null && keyStore.getFileType() != null - && keyStore.getFileName() != null); + && keyStore.getFileName() != null; + if (!isConfigured) throw mzse("KeyStore"); } - private boolean isTrustStoreConfigured(KeyStoreType trustStore) { - return trustStore == null || (trustStore.getPassword() != null + private void isTrustStoreConfigured(@Nullable KeyStoreType trustStore) { + if (trustStore == null) return; + + var isConfigured = trustStore.getPassword() != null && "JKS".equals(trustStore.getFileType()) - && trustStore.getFileName() != null); + && trustStore.getFileName() != null; + if (!isConfigured) throw mzse("TrustStore"); } - private boolean areSinksConfigured(MsgResponseSinksType sinks) { - return sinks != null - && sinks.isLogResponse() != null - && isSaveResponseToFileConfigured(sinks.getSaveResponseToFile()) - && isForwardResponseToServiceConfigured(sinks.getForwardResponseToService()); + private void areSinksConfigured(@Nullable MsgResponseSinksType sinks) { + var isConfigured = sinks != null && sinks.isLogResponse() != null; + if (!isConfigured) throw mzse("MsgResponseSinks"); + + isSaveResponseToFileConfigured(sinks.getSaveResponseToFile()); + isForwardResponseToServiceConfigured(sinks.getForwardResponseToService()); } - private boolean isSaveResponseToFileConfigured(SaveResponseToFileType fileSink) { - return fileSink != null + private void isSaveResponseToFileConfigured(@Nullable SaveResponseToFileType fileSink) { + var isConfigured = fileSink != null && (!fileSink.isActive() || fileSink.getPath() != null); + + if (!isConfigured) throw mzse("SaveResponseToFile"); + } + + private void isForwardResponseToServiceConfigured(@Nullable ForwardResponseToServiceType forwardSink) { + if (forwardSink == null) throw mzse("ForwardResponseToService"); + + if (forwardSink.isActive()) { + try { + isClientConfigured(forwardSink.getMzsClient()); + } catch (MoaZSException e) { + throw mzse("ForwardResponseToService", e.getMessage()); + } + } } - private boolean isForwardResponseToServiceConfigured(ForwardResponseToServiceType forwardSink) { - return forwardSink != null - && (!forwardSink.isActive() || isClientConfigured(forwardSink.getMzsClient())); + private MoaZSException mzse(String missing) { + return moaZSException(format("%s is not configured.", missing)); } + private MoaZSException mzse(String missing, String reason) { + return moaZSException(format("%s is not configured. Reason: %s", missing, reason)); + } + + } 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 41b742b..b8a6d78 100644 --- a/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java +++ b/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java @@ -4,6 +4,8 @@ package at.gv.egiz.moazs.repository; import at.gv.egiz.moazs.scheme.MsgResponse; import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType; import com.google.common.cache.Cache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; @@ -17,6 +19,8 @@ import static java.util.Optional.ofNullable; @Profile("!cluster") public class InMemoryDeliveryRepository implements DeliveryRepository { + private static final Logger log = LoggerFactory.getLogger(InMemoryDeliveryRepository.class); + private final Cache requestRepository; private final Cache responseRepository; private final Cache binaryResponseRepository; @@ -31,33 +35,38 @@ public class InMemoryDeliveryRepository implements DeliveryRepository { @Override public void store(DeliveryRequestType request) { var key = request.getMetaData().getAppDeliveryID(); + log.trace("Store DeliveryRequest by key={}", key); requestRepository.put(key, request); } @Override public Optional retrieveDeliveryRequest(String appDeliveryID) { + log.trace("Retrieve DeliveryRequest by key={}", appDeliveryID); return ofNullable(requestRepository.getIfPresent(appDeliveryID)); } @Override public void store(MsgResponse response) { String key = response.getResponseID(); + log.trace("Store MsgResponse by key={}", key); responseRepository.put(key, response); } @Override public Optional retrieveResponse(String responseID) { + log.trace("Retrieve MsgResponse by key={}", responseID); return ofNullable(responseRepository.getIfPresent(responseID)); } @Override - public void store(String id, byte[] data) { - binaryResponseRepository.put(id, data); - + public void store(String responseID, byte[] data) { + log.trace("Store Binary MsgResponse by key={}", responseID); + binaryResponseRepository.put(responseID, data); } @Override public Optional retrieveBinaryResponse(String responseID) { + log.trace("Retrieve Binary MsgResponse by key={}", responseID); return ofNullable(binaryResponseRepository.getIfPresent(responseID)); } diff --git a/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java b/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java index 83ace5c..1a86079 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java @@ -32,6 +32,10 @@ public class Marshaller { } public String marshallXml(final T obj) { + if (obj == null) { + return "null"; + } + StringWriter sw = new StringWriter(); Result result = new StreamResult(sw); jaxbMarshaller.marshal(obj, result); diff --git a/src/main/java/at/gv/egiz/moazs/scheme/NameSpace.java b/src/main/java/at/gv/egiz/moazs/scheme/NameSpace.java index 6b6f34a..386a7a6 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/NameSpace.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/NameSpace.java @@ -5,6 +5,7 @@ public class NameSpace { private NameSpace() {} private static final at.gv.zustellung.msg.xsd.ObjectFactory MSG_FACTORY = new at.gv.zustellung.msg.xsd.ObjectFactory(); + private static final at.gv.zustellung.app2mzs.xsd.ObjectFactory MZS_FACTORY = new at.gv.zustellung.app2mzs.xsd.ObjectFactory(); public static final String MSG_VERSION = "2.2.0"; public static final String MSG = MSG_FACTORY.createDeliveryRequest(null).getName().getNamespaceURI(); @@ -18,4 +19,8 @@ public class NameSpace { public static final String MSG_DELIVERY_NOTIFICATION = MSG_FACTORY.createDeliveryNotification(null).getName().getLocalPart(); public static final String MSG_APP_DELIVERY_ID = MSG_FACTORY.createAppDeliveryID("").getName().getLocalPart(); + public static final String MZS_DELIVERY_REQUEST = MZS_FACTORY.createDeliveryRequest(null).getName().getLocalPart(); + public static final String MZS_TNVZCLIENT = MZS_FACTORY.createTNVZClient(null).getName().getLocalPart(); + public static final String MZS_MSGCLIENT = MZS_FACTORY.createTNVZClient(null).getName().getLocalPart(); + } diff --git a/src/main/java/at/gv/egiz/moazs/scheme/SOAPUtils.java b/src/main/java/at/gv/egiz/moazs/scheme/SOAPUtils.java index 6e96a6b..461d8fe 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/SOAPUtils.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/SOAPUtils.java @@ -3,8 +3,12 @@ package at.gv.egiz.moazs.scheme; import at.gv.egiz.eaaf.core.impl.utils.DOMUtils; import at.gv.egiz.moazs.MoaZSException; import org.apache.cxf.binding.soap.Soap11; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -12,34 +16,72 @@ import javax.xml.transform.TransformerException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; + +import static at.gv.egiz.moazs.MoaZSException.moaZSException; @Component public class SOAPUtils { + private static final Logger log = LoggerFactory.getLogger(SOAPUtils.class); + private static final String SOAP_BODY_MISSING_ERROR_MSG = " is missing."; + private static final String SOAP_BODY_CHILDREN_MISSING_ERROR_MSG = " has no child elements."; + private static final String APP_DELIVERY_ID_MISSING_ERROR_MSG = " is missing."; + private static final String APP_DELIVERY_ID_EMPTY_ERROR_MSG = " is empty."; + private static final String MULTIPLE_MSG = "Found multiple {} elements. Will choose first element."; + private static final String APP_DELIVERY_ID_FULL_TAG_NAME = ""; + public Element toDOM(byte[] bytes) throws IOException, SAXException, ParserConfigurationException { var stream = new ByteArrayInputStream(bytes); return DOMUtils.parseXmlNonValidating(stream); } - public byte[] unwrapSoapEnvelope(Element document) { - try { - var body = document.getElementsByTagNameNS(Soap11.SOAP_NAMESPACE, "Body"); - var item = body.item(0).getFirstChild(); + public Node getChildElementOfSoapBody(Element document) throws MoaZSException { + + var bodyList = document.getElementsByTagNameNS(Soap11.SOAP_NAMESPACE, "Body"); + if (bodyList.getLength() == 0) throw moaZSException(SOAP_BODY_MISSING_ERROR_MSG); + if (bodyList.getLength() > 1) log.warn(MULTIPLE_MSG, ""); + + var body = bodyList.item(0); + var children = body.getChildNodes(); + var candidates = filterNodeByType(children, Node.ELEMENT_NODE); + + if (candidates.isEmpty()) throw moaZSException(SOAP_BODY_CHILDREN_MISSING_ERROR_MSG); + if (candidates.size() > 1) log.warn(MULTIPLE_MSG, " child"); - return DOMUtils.serializeNode(item, true) - .getBytes(StandardCharsets.UTF_8); + return candidates.get(0); + } - } catch (IOException | TransformerException e) { - throw MoaZSException.moaZSException("Error while parsing message. ", e); + private List filterNodeByType(NodeList children, short nodeType) { + var candidates = new LinkedList(); + for (int i = 0; i < children.getLength(); i++) { + var child = children.item(i); + if (child.getNodeType() == nodeType) { + candidates.add(child); + } } + + return candidates; + } + + public byte[] toBytes(Node node) throws TransformerException, IOException { + return DOMUtils.serializeNode(node, true).getBytes(StandardCharsets.UTF_8); } public String getAppDeliveryIDFrom(Element document) { - var elements = document.getElementsByTagNameNS(NameSpace.MSG, NameSpace.MSG_APP_DELIVERY_ID); + var elementList = document.getElementsByTagNameNS(NameSpace.MSG, NameSpace.MSG_APP_DELIVERY_ID); + + if (elementList.getLength() == 0) throw moaZSException(APP_DELIVERY_ID_MISSING_ERROR_MSG); + if (elementList.getLength() > 1) log.warn(MULTIPLE_MSG, APP_DELIVERY_ID_FULL_TAG_NAME); - var appDeliveryIdElement = elements.item(0).getFirstChild(); + var children = elementList.item(0).getChildNodes(); + var candidates = filterNodeByType(children, Node.TEXT_NODE); + if (candidates.isEmpty() || candidates.get(0).getNodeValue().isBlank()) + throw moaZSException(APP_DELIVERY_ID_EMPTY_ERROR_MSG); + if (candidates.size() > 1) log.warn(MULTIPLE_MSG, APP_DELIVERY_ID_FULL_TAG_NAME); - return appDeliveryIdElement.getNodeValue(); + return candidates.get(0).getNodeValue(); } } diff --git a/src/main/java/at/gv/egiz/moazs/util/EndpointFactory.java b/src/main/java/at/gv/egiz/moazs/util/EndpointFactory.java index 24321e1..9d31596 100644 --- a/src/main/java/at/gv/egiz/moazs/util/EndpointFactory.java +++ b/src/main/java/at/gv/egiz/moazs/util/EndpointFactory.java @@ -20,13 +20,13 @@ public class EndpointFactory { this.bus = bus; } - public Endpoint create(Object implementor, Service service) { - return create(implementor, service, null); + public Endpoint create(Object implementor, Service service, String route) { + return create(implementor, service, route, null); } - public Endpoint create(Object implementor, Service service, Interceptor interceptor) { + public Endpoint create(Object implementor, Service service, String route, Interceptor interceptor) { EndpointImpl endpoint = new EndpointImpl(bus, implementor); - endpoint.setAddress("/"); + endpoint.setAddress(route); endpoint.setServiceName(service.getServiceName()); endpoint.setWsdlLocation(service.getWSDLDocumentLocation().toString()); endpoint.publish(); diff --git a/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java b/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java index 88ab7e0..d4aa75a 100644 --- a/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java +++ b/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java @@ -13,6 +13,7 @@ import org.w3c.dom.Element; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -46,25 +47,22 @@ public class StoreSOAPBodyBinaryInRepositoryInterceptor extends AbstractPhaseInt try { byte[] content = messageUtils.copyContent(message); - if(log.isTraceEnabled()) { - log.trace("Interceptor received this SOAP message: {}. ", new String(content, StandardCharsets.UTF_8)); - } - if (content.length <= 0) { return; } Element document = soapUtils.toDOM(content); - byte[] response = soapUtils.unwrapSoapEnvelope(document); - String appDeliveryID = soapUtils.getAppDeliveryIDFrom(document); - String rootTag = document.getTagName(); + var rootTagNode = soapUtils.getChildElementOfSoapBody(document); + var rootTagLocalName = rootTagNode.getLocalName(); - if (!idGenerators.containsKey(rootTag)) { - log.trace("Will not store message of type {}. ", rootTag); + if (!idGenerators.containsKey(rootTagLocalName)) { + log.trace("Child element {} of is unknown. Will not store message.", rootTagLocalName); return; } - var id = idGenerators.get(rootTag).apply(appDeliveryID); + String appDeliveryID = soapUtils.getAppDeliveryIDFrom(document); + var id = idGenerators.get(rootTagLocalName).apply(appDeliveryID); + byte[] response = soapUtils.toBytes(rootTagNode); repository.store(id, response); if(log.isTraceEnabled()) { @@ -72,7 +70,7 @@ public class StoreSOAPBodyBinaryInRepositoryInterceptor extends AbstractPhaseInt appDeliveryID, new String(response, StandardCharsets.UTF_8)); } - } catch (ParserConfigurationException | SAXException | IOException | NullPointerException e) { + } catch (ParserConfigurationException | SAXException | IOException | NullPointerException | TransformerException e) { throw moaZSException("Could not extract signed data from message.", e); } } -- cgit v1.2.3