From 78f0715d86a055aed11138df5f66b0794e72326a Mon Sep 17 00:00:00 2001
From: Christof Rabensteiner <christof.rabensteiner@iaik.tugraz.at>
Date: Wed, 3 Jul 2019 15:27:14 +0200
Subject: Refactor: Restructure Project

- Move components that depend on the service contracts into scheme
  package.
- Move cxf related components into util package.
- Rename SameThread to SingleThreaded.
---
 .../at/gv/egiz/moazs/config/MzsServiceConfig.java  |   2 +-
 .../at/gv/egiz/moazs/config/PreprocessConfig.java  |   4 +-
 .../java/at/gv/egiz/moazs/msg/ClientFactory.java   | 113 ----------
 ...StoreSOAPBodyBinaryInRepositoryInterceptor.java |  65 ------
 src/main/java/at/gv/egiz/moazs/mzs/MzsClient.java  |  13 --
 src/main/java/at/gv/egiz/moazs/mzs/MzsService.java | 105 ----------
 .../java/at/gv/egiz/moazs/mzs/MzsValidator.java    |  74 -------
 .../moazs/pipeline/SameThreadDeliveryPipeline.java | 146 -------------
 .../pipeline/SingleThreadedDeliveryPipeline.java   | 146 +++++++++++++
 .../moazs/preprocess/ConfigProfileGenerator.java   |  10 +-
 .../moazs/preprocess/DeliveryRequestAugmenter.java |   6 +-
 .../java/at/gv/egiz/moazs/scheme/MzsClient.java    |  13 ++
 .../moazs/scheme/MzsDeliveryRequestValidator.java  |  74 +++++++
 .../java/at/gv/egiz/moazs/scheme/MzsService.java   | 104 +++++++++
 .../java/at/gv/egiz/moazs/scheme/TnvzHelper.java   | 232 +++++++++++++++++++++
 .../java/at/gv/egiz/moazs/tnvz/TnvzHelper.java     | 232 ---------------------
 .../java/at/gv/egiz/moazs/util/ClientFactory.java  | 111 ++++++++++
 ...StoreSOAPBodyBinaryInRepositoryInterceptor.java |  64 ++++++
 .../gv/egiz/moazs/ConfigProfileGeneratorTest.java  |   4 +-
 .../egiz/moazs/DeliveryRequestAugmenterTest.java   |   4 +-
 .../java/at/gv/egiz/moazs/ITMsgClientTest.java     |   2 +-
 .../egiz/moazs/SameThreadDeliveryPipelineTest.java | 189 -----------------
 .../moazs/SingleThreadedDeliveryPipelineTest.java  | 188 +++++++++++++++++
 src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java |   2 +-
 24 files changed, 949 insertions(+), 954 deletions(-)
 delete mode 100644 src/main/java/at/gv/egiz/moazs/msg/ClientFactory.java
 delete mode 100644 src/main/java/at/gv/egiz/moazs/msg/StoreSOAPBodyBinaryInRepositoryInterceptor.java
 delete mode 100644 src/main/java/at/gv/egiz/moazs/mzs/MzsClient.java
 delete mode 100644 src/main/java/at/gv/egiz/moazs/mzs/MzsService.java
 delete mode 100644 src/main/java/at/gv/egiz/moazs/mzs/MzsValidator.java
 delete mode 100644 src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java
 create mode 100644 src/main/java/at/gv/egiz/moazs/pipeline/SingleThreadedDeliveryPipeline.java
 create mode 100644 src/main/java/at/gv/egiz/moazs/scheme/MzsClient.java
 create mode 100644 src/main/java/at/gv/egiz/moazs/scheme/MzsDeliveryRequestValidator.java
 create mode 100644 src/main/java/at/gv/egiz/moazs/scheme/MzsService.java
 create mode 100644 src/main/java/at/gv/egiz/moazs/scheme/TnvzHelper.java
 delete mode 100644 src/main/java/at/gv/egiz/moazs/tnvz/TnvzHelper.java
 create mode 100644 src/main/java/at/gv/egiz/moazs/util/ClientFactory.java
 create mode 100644 src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java
 delete mode 100644 src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java
 create mode 100644 src/test/java/at/gv/egiz/moazs/SingleThreadedDeliveryPipelineTest.java

diff --git a/src/main/java/at/gv/egiz/moazs/config/MzsServiceConfig.java b/src/main/java/at/gv/egiz/moazs/config/MzsServiceConfig.java
index 713c616..9fd14eb 100644
--- a/src/main/java/at/gv/egiz/moazs/config/MzsServiceConfig.java
+++ b/src/main/java/at/gv/egiz/moazs/config/MzsServiceConfig.java
@@ -1,6 +1,6 @@
 package at.gv.egiz.moazs.config;
 
-import at.gv.egiz.moazs.mzs.MzsService;
+import at.gv.egiz.moazs.scheme.MzsService;
 import at.gv.zustellung.app2mzs.xsd.App2Mzs;
 import org.apache.cxf.Bus;
 import org.apache.cxf.jaxws.EndpointImpl;
diff --git a/src/main/java/at/gv/egiz/moazs/config/PreprocessConfig.java b/src/main/java/at/gv/egiz/moazs/config/PreprocessConfig.java
index b90b6a3..b24db28 100644
--- a/src/main/java/at/gv/egiz/moazs/config/PreprocessConfig.java
+++ b/src/main/java/at/gv/egiz/moazs/config/PreprocessConfig.java
@@ -1,6 +1,6 @@
 package at.gv.egiz.moazs.config;
 
-import at.gv.egiz.moazs.mzs.MzsValidator;
+import at.gv.egiz.moazs.scheme.MzsDeliveryRequestValidator;
 import at.gv.egiz.moazs.preprocess.*;
 import at.gv.zustellung.app2mzs.xsd.ConfigType;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -28,7 +28,7 @@ public class PreprocessConfig {
     @Autowired
     public ConfigProfileGenerator configProfileGenerator(
             SpringPropertiesFacade properties,
-            MzsValidator validator,
+            MzsDeliveryRequestValidator validator,
             ConfigUtil util) {
 
         return configProfileGeneratorBuilder()
diff --git a/src/main/java/at/gv/egiz/moazs/msg/ClientFactory.java b/src/main/java/at/gv/egiz/moazs/msg/ClientFactory.java
deleted file mode 100644
index c2ad4a7..0000000
--- a/src/main/java/at/gv/egiz/moazs/msg/ClientFactory.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package at.gv.egiz.moazs.msg;
-
-import at.gv.egiz.moazs.util.FileUtils;
-import at.gv.egiz.moazs.util.SSLContextCreator;
-import at.gv.zustellung.app2mzs.xsd.ClientType;
-import at.gv.zustellung.app2mzs.xsd.KeyStoreType;
-import at.gv.zustellung.app2mzs.xsd.SSLType;
-import javax.xml.ws.soap.SOAPBinding;
-import org.apache.cxf.configuration.jsse.TLSClientParameters;
-import org.apache.cxf.endpoint.Client;
-import org.apache.cxf.frontend.ClientProxy;
-import org.apache.cxf.jaxws.JaxWsClientFactoryBean;
-import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
-import org.apache.cxf.transport.http.HTTPConduit;
-import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.Nullable;
-import javax.net.ssl.SSLContext;
-import javax.xml.ws.BindingProvider;
-
-import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder;
-
-@Component
-public class ClientFactory {
-
-    private static final Logger log = LoggerFactory.getLogger(ClientFactory.class);
-
-    private final StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor;
-    private final SSLContextCreator sslContextCreator;
-    private final FileUtils fileUtils;
-
-    @Autowired
-    public ClientFactory(StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor,
-                         SSLContextCreator creator,
-                         FileUtils fileUtils) {
-        this.storeResponseInterceptor = storeResponseInterceptor;
-        this.sslContextCreator = creator;
-        this.fileUtils = fileUtils;
-    }
-
-    /**
-     * Creates a client that communicates with a soap service.
-     *
-     * @param params for the client, such as service url and ssl parameters.
-     * @return the client
-     */
-    public <T> T create(ClientType params, Class<T> clazz) {
-
-        var factory = new JaxWsClientFactoryBean();
-        factory.setServiceClass(clazz);
-        factory.setAddress(params.getURL());
-        factory.getInInterceptors().add(storeResponseInterceptor);
-
-        var proxy = new JaxWsProxyFactoryBean(factory).create();
-        Client client = ClientProxy.getClient(proxy);
-        HTTPConduit http = (HTTPConduit) client.getConduit();
-
-        var bindingProvider = (BindingProvider) proxy;
-        SOAPBinding binding = (SOAPBinding) bindingProvider.getBinding();
-        binding.setMTOMEnabled(true);
-
-        var httpClientPolicy = new HTTPClientPolicy();
-        httpClientPolicy.setConnectionTimeout(params.getConnectionTimeout().longValueExact());
-        httpClientPolicy.setReceiveTimeout(params.getReceiveTimeout().longValueExact());
-        http.setClient(httpClientPolicy);
-
-        if (params.getURL().startsWith("https")) {
-            TLSClientParameters tlsParams = setupTLSParams(params.getSSL());
-            http.setTlsClientParameters(tlsParams);
-            log.info("SSLContext initialized. ");
-        }
-
-        return ((T)proxy);
-    }
-
-    private TLSClientParameters setupTLSParams(SSLType ssl) {
-
-        var tlsParams = new TLSClientParameters();
-        var keystore = resolveKeyStorePath(ssl.getKeyStore());
-
-        SSLContext sslContext;
-        if (ssl.isTrustAll()) {
-            sslContext = sslContextCreator.createUnsafeSSLContext(keystore);
-        } else {
-            var truststore = resolveKeyStorePath(ssl.getTrustStore());
-            sslContext = sslContextCreator.createSSLContext(keystore, truststore);
-        }
-        tlsParams.setSSLSocketFactory(sslContext.getSocketFactory());
-
-        if (ssl.isLaxHostNameVerification()) {
-            tlsParams.setDisableCNCheck(true);
-        }
-
-        return tlsParams;
-    }
-
-    private KeyStoreType resolveKeyStorePath(@Nullable KeyStoreType store) {
-
-        if (store == null) return null;
-
-        var resolvedURI = "file:" + fileUtils.determinePath(store.getFileName());
-        log.trace("Resolved key store path from {} to {}.", store.getFileName(), resolvedURI);
-
-        return keyStoreTypeBuilder(store)
-                .withFileName(resolvedURI)
-                .build();
-    }
-
-}
diff --git a/src/main/java/at/gv/egiz/moazs/msg/StoreSOAPBodyBinaryInRepositoryInterceptor.java b/src/main/java/at/gv/egiz/moazs/msg/StoreSOAPBodyBinaryInRepositoryInterceptor.java
deleted file mode 100644
index ac3acc6..0000000
--- a/src/main/java/at/gv/egiz/moazs/msg/StoreSOAPBodyBinaryInRepositoryInterceptor.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package at.gv.egiz.moazs.msg;
-
-import at.gv.egiz.moazs.repository.DeliveryRepository;
-import at.gv.egiz.moazs.scheme.SOAPUtils;
-import at.gv.egiz.moazs.util.CXFMessageUtils;
-import org.apache.cxf.message.Message;
-import org.apache.cxf.phase.AbstractPhaseInterceptor;
-import org.apache.cxf.phase.Phase;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import static at.gv.egiz.moazs.MoaZSException.moaZSException;
-
-@Component
-public class StoreSOAPBodyBinaryInRepositoryInterceptor extends AbstractPhaseInterceptor<Message> {
-
-    private static final Logger log = LoggerFactory.getLogger(StoreSOAPBodyBinaryInRepositoryInterceptor.class);
-
-    private final CXFMessageUtils messageUtils;
-    private final SOAPUtils soapUtils;
-    private final DeliveryRepository repository;
-
-    @Autowired
-    public StoreSOAPBodyBinaryInRepositoryInterceptor(CXFMessageUtils extractor, SOAPUtils soapUtils,
-                                                      DeliveryRepository repository) {
-        super(Phase.RECEIVE);
-        this.messageUtils = extractor;
-        this.soapUtils = soapUtils;
-        this.repository = repository;
-    }
-
-    public void handleMessage(Message message) {
-
-        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) {
-                Element document = soapUtils.toDOM(content);
-                byte[] status = soapUtils.unwrapSoapEnvelope(document);
-                String appDeliveryID = soapUtils.getAppDeliveryIDFrom(document);
-                repository.addSignedDeliveryRequestStatus(status, appDeliveryID);
-
-                if(log.isTraceEnabled()) {
-                    log.trace("DeliveryRequestStatus with AppDeliveryID={} unwrapped and stored: {}. ",
-                            appDeliveryID, new String(status, StandardCharsets.UTF_8));
-                }
-            }
-        } catch (ParserConfigurationException | SAXException | IOException | NullPointerException e) {
-            throw moaZSException("Could not extract signed data from message.", e);
-        }
-    }
-
-}
diff --git a/src/main/java/at/gv/egiz/moazs/mzs/MzsClient.java b/src/main/java/at/gv/egiz/moazs/mzs/MzsClient.java
deleted file mode 100644
index 980dce1..0000000
--- a/src/main/java/at/gv/egiz/moazs/mzs/MzsClient.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package at.gv.egiz.moazs.mzs;
-
-import at.gv.zustellung.app2mzs.xsd.DeliveryResponseType;
-import org.springframework.stereotype.Component;
-
-@Component
-public class MzsClient {
-
-    public void sendNotification(DeliveryResponseType responseType) {
-        throw new UnsupportedOperationException("Not implemented.");
-    }
-
-}
diff --git a/src/main/java/at/gv/egiz/moazs/mzs/MzsService.java b/src/main/java/at/gv/egiz/moazs/mzs/MzsService.java
deleted file mode 100644
index f4ecfdb..0000000
--- a/src/main/java/at/gv/egiz/moazs/mzs/MzsService.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package at.gv.egiz.moazs.mzs;
-
-import at.gv.egiz.moazs.pipeline.DeliveryPipeline;
-import at.gv.egiz.moazs.preprocess.DeliveryRequestAugmenter;
-import at.gv.egiz.moazs.repository.DeliveryRepository;
-import at.gv.egiz.moazs.scheme.Msg2MzsConverter;
-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;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import javax.jws.WebParam;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import static at.gv.egiz.moazs.MoaZSException.moaZSException;
-import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
-import static at.gv.zustellung.app2mzs.xsd.PartialSuccessType.partialSuccessTypeBuilder;
-import static java.util.concurrent.CompletableFuture.supplyAsync;
-
-//todo : validate Schema in both directions.
-@Service
-@SchemaValidation(type = SchemaValidation.SchemaValidationType.IN)
-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 final DeliveryRepository repository;
-    private final DeliveryPipeline pipeline;
-    private final MzsClient appClient;
-    private final DeliveryRequestAugmenter augmenter;
-    private final Msg2MzsConverter converter;
-
-    @Autowired
-    public MzsService(DeliveryRepository repository, DeliveryPipeline pipeline, MzsClient appClient,
-                      DeliveryRequestAugmenter augmenter, Msg2MzsConverter converter) {
-        this.repository = repository;
-        this.pipeline = pipeline;
-        this.appClient = appClient;
-        this.augmenter = augmenter;
-        this.converter = converter;
-    }
-
-    @Override
-    public DeliveryResponseType app2Mzs(
-            @WebParam(partName = "DeliveryRequest",
-                      name = "DeliveryRequest")
-                    DeliveryRequestType deliveryRequest) {
-
-        var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID();
-
-        var future = supplyAsync(() -> augmenter.augment(deliveryRequest))
-                .thenApply(this::process)
-                .thenApply(status -> converter.convert(status, repository.getSignedDeliveryRequestStatus(appDeliveryID)));
-
-        try {
-            return future.get(TIMEOUT_FOR_ANWSER, TimeUnit.SECONDS);
-        } catch (TimeoutException e) {
-            logger.info("Answer Timed Out", e);
-
-            future.thenAccept(appClient::sendNotification);
-            return generatePartialSuccessResponse(appDeliveryID);
-
-        } catch (Exception e) {
-            throw moaZSExceptionBuilder("An error occurred while processing DeliveryRequest " +
-                    "with AppDeliveryID=%s.", appDeliveryID)
-                    .withCause(e)
-                    .build();
-        }
-
-    }
-
-    private DeliveryRequestStatusType process(DeliveryRequestType deliveryRequest) {
-
-        var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID();
-        logger.info("Receive request with appDeliveryID = {}.", appDeliveryID);
-
-        repository.add(deliveryRequest);
-        pipeline.processRequest(appDeliveryID);
-
-        return repository.getDeliveryRequestStatus(appDeliveryID)
-                .orElseThrow(() -> moaZSException("Could not get a response for AppDeliveryID=%s.", appDeliveryID));
-
-    }
-
-    private DeliveryResponseType generatePartialSuccessResponse(String appDeliveryId) {
-
-        var partial = partialSuccessTypeBuilder()
-                .withAppDeliveryID(appDeliveryId)
-                .build();
-
-        return DeliveryResponseType.deliveryResponseTypeBuilder()
-                .withPartialSuccess(partial)
-                .build();
-    }
-
-}
diff --git a/src/main/java/at/gv/egiz/moazs/mzs/MzsValidator.java b/src/main/java/at/gv/egiz/moazs/mzs/MzsValidator.java
deleted file mode 100644
index 26ea7dc..0000000
--- a/src/main/java/at/gv/egiz/moazs/mzs/MzsValidator.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package at.gv.egiz.moazs.mzs;
-
-import at.gv.zustellung.app2mzs.xsd.ClientType;
-import at.gv.zustellung.app2mzs.xsd.ConfigType;
-import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
-import at.gv.zustellung.app2mzs.xsd.KeyStoreType;
-import org.springframework.lang.Nullable;
-import org.springframework.stereotype.Component;
-
-@Component
-public class MzsValidator {
-
-    /**
-     * Checks if the mandatory fields that are needed to send a tnvz:QueryPersonRequest are present.
-     * @param request
-     * @return true if mandatory fields are present.
-     */
-    public boolean isTnvzComplete(DeliveryRequestType request) {
-        return !request.getConfig().isPerformQueryPersonRequest() ||
-                (request.getTnvzMetaData() != null
-                && request.getSender().getCorporateBody() != null);
-
-
-    }
-
-    /**
-     * Check if all mandatory fields of configuration are present.
-     *
-     * @param profile
-     * @return true if all mandatory fields are present.
-     */
-    public boolean isConfigProfileComplete(@Nullable ConfigType profile) {
-        return profile != null
-                && profile.isPerformQueryPersonRequest() != null
-                && isTVNZClientConfigured(profile.getTNVZClient(), profile.isPerformQueryPersonRequest())
-                && isMSGClientConfigured(profile.getMSGClient());
-    }
-
-    private boolean isTVNZClientConfigured(ClientType tnvzClient, Boolean isPerformQueryPersonRequest) {
-        return !isPerformQueryPersonRequest || (tnvzClient != null
-                && tnvzClient.getURL() != null
-                && tnvzClient.getReceiveTimeout() != null
-                && tnvzClient.getConnectionTimeout() != null
-                && isSSLConfigured(tnvzClient));
-    }
-
-    private boolean isMSGClientConfigured(ClientType msgClientParams) {
-        return msgClientParams != null
-                && msgClientParams.getURL() != null
-                && isSSLConfigured(msgClientParams)
-                && msgClientParams.getReceiveTimeout() != null
-                && msgClientParams.getConnectionTimeout() != null;
-    }
-
-    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 boolean isKeyStoreConfigured(KeyStoreType keyStore) {
-        return keyStore == null || (keyStore.getPassword() != null
-                && keyStore.getFileType() != null
-                && keyStore.getFileName() != null);
-    }
-
-    private boolean isTrustStoreConfigured(KeyStoreType trustStore) {
-        return trustStore == null || (trustStore.getPassword() != null
-                && "JKS".equals(trustStore.getFileType())
-                && trustStore.getFileName() != null);
-    }
-}
diff --git a/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java b/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java
deleted file mode 100644
index ecf580d..0000000
--- a/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package at.gv.egiz.moazs.pipeline;
-
-
-import at.gv.egiz.moazs.MoaZSException;
-import at.gv.egiz.moazs.msg.ClientFactory;
-import at.gv.egiz.moazs.repository.DeliveryRepository;
-import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;
-import at.gv.egiz.moazs.scheme.NameSpace;
-import at.gv.egiz.moazs.tnvz.TnvzHelper;
-import at.gv.egiz.moazs.verify.SignatureVerifier;
-import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
-import at.gv.zustellung.msg.xsd.App2ZusePort;
-import at.gv.zustellung.msg.xsd.DeliveryAnswerType;
-import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType;
-import at.gv.zustellung.tnvz.xsd.TNVZServicePort;
-import org.apache.log4j.Logger;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Profile;
-import org.springframework.stereotype.Component;
-
-import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
-import static at.gv.egiz.moazs.util.NullCoalesce.coalesce;
-import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.Error.errorBuilder;
-import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.deliveryRequestStatusTypeBuilder;
-import static at.gv.zustellung.msg.xsd.ErrorInfoType.errorInfoTypeBuilder;
-import static java.lang.String.format;
-
-@Component
-@Profile("!cluster")
-public class SameThreadDeliveryPipeline implements DeliveryPipeline {
-
-    private static final Logger log = Logger.getLogger(SameThreadDeliveryPipeline.class);
-
-    private final DeliveryRepository repository;
-    private final TnvzHelper tnvzHelper;
-    private final Mzs2MsgConverter converter;
-    private final ClientFactory clientFactory;
-    private final SignatureVerifier verifier;
-
-    @Autowired
-    public SameThreadDeliveryPipeline(DeliveryRepository repository,
-                                      TnvzHelper tnvzHelper,
-                                      Mzs2MsgConverter converter,
-                                      ClientFactory clientFactory,
-                                      SignatureVerifier verifier
-    ) {
-        this.repository = repository;
-        this.tnvzHelper = tnvzHelper;
-        this.converter = converter;
-        this.clientFactory = clientFactory;
-        this.verifier = verifier;
-    }
-
-    @Override
-    public void processRequest(String appDeliveryId) {
-
-        var exceptionBuilder = moaZSExceptionBuilder();
-
-        try {
-            var mzsRequest = repository.getDeliveryRequest(appDeliveryId).orElseThrow();
-            exceptionBuilder.withMzsRequest(mzsRequest);
-
-            at.gv.zustellung.msg.xsd.DeliveryRequestType msgRequest = buildMsgRequest(mzsRequest, exceptionBuilder);
-            exceptionBuilder.withMsgRequest(msgRequest);
-
-            var msgClientParams = mzsRequest.getConfig().getMSGClient();
-            App2ZusePort client = clientFactory.create(msgClientParams, App2ZusePort.class);
-            var status = client.delivery(msgRequest);
-            exceptionBuilder.withMsgResult(status);
-
-            verifySignedStatus(appDeliveryId, exceptionBuilder);
-            repository.add(status);
-
-        } catch (MoaZSException exception) {
-
-            log.error(format("An error occured while processing the DeliveryRequest with AppDeliveryID=%s. ",
-                    appDeliveryId), exception);
-
-            var errorStatus = generateErrorStatus(exception, appDeliveryId);
-            repository.add(errorStatus);
-        }
-    }
-
-    private at.gv.zustellung.msg.xsd.DeliveryRequestType buildMsgRequest(DeliveryRequestType mzsRequest,
-                                                                         MoaZSException.Builder exceptionBuilder) {
-        if (mzsRequest.getConfig().isPerformQueryPersonRequest()) {
-            var tnvzClientParams = mzsRequest.getConfig().getTNVZClient();
-            TNVZServicePort tvnzPort = clientFactory.create(tnvzClientParams, TNVZServicePort.class);
-            var identification = tnvzHelper.performQueryPersonRequest(mzsRequest, tvnzPort, exceptionBuilder);
-            return converter.convert(mzsRequest, identification);
-        } else {
-            return converter.convert(mzsRequest);
-        }
-    }
-
-    private void verifySignedStatus(String appDeliveryId, MoaZSException.Builder exceptionBuilder) throws MoaZSException {
-
-        try {
-            var signedStatus = repository.getSignedDeliveryRequestStatus(appDeliveryId).get();
-            verifier.verify(signedStatus);
-        } catch (Exception ex) {
-            throw exceptionBuilder.withMessage(format("Signature of DeliveryRequestStatus with AppDeliveryID=%s " +
-                    " is not valid.", appDeliveryId))
-                    .withErrorCode(MoaZSException.ERROR_MOASP_SIGNATURE_INVALID)
-                    .withCause(ex)
-                    .build();
-        }
-    }
-
-    private DeliveryRequestStatusType generateErrorStatus(MoaZSException exception, String appDeliveryId) {
-
-        var infoBuilder = errorInfoTypeBuilder()
-                .withText(exception.getMessage())
-                .withCode(exception.getErrorCode());
-
-        var errorBuilder = errorBuilder()
-                .withErrorInfo(infoBuilder.build())
-                .withAppDeliveryID(appDeliveryId);
-
-        if (exception.getMzsRequest() != null) {
-            errorBuilder.withDeliverySystem(exception.getMzsRequest().getConfig().getMSGClient().getURL());
-        }
-
-        if (exception.getTnvzResult() != null && exception.getTnvzResult().getError() != null) {
-            errorBuilder.withPreAdviceNoteSent(exception.getTnvzResult().getError().getPreAdviceNoteSent());
-        }
-
-        if (exception.getMsgResult() != null) {
-            var answer = getAnswerFromResult(exception.getMsgResult());
-            errorBuilder.withGZ(answer.getGZ());
-            errorBuilder.withZSDeliveryID(answer.getZSDeliveryID());
-        }
-
-        return deliveryRequestStatusTypeBuilder()
-                .withError(errorBuilder.build())
-                .withVersion(NameSpace.MSG_VERSION)
-                .build();
-
-    }
-
-    private DeliveryAnswerType getAnswerFromResult(DeliveryRequestStatusType msgResult) {
-        return coalesce(msgResult.getSuccess(), msgResult.getPartialSuccess(), msgResult.getError()).get();
-    }
-
-
-}
diff --git a/src/main/java/at/gv/egiz/moazs/pipeline/SingleThreadedDeliveryPipeline.java b/src/main/java/at/gv/egiz/moazs/pipeline/SingleThreadedDeliveryPipeline.java
new file mode 100644
index 0000000..783df32
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/pipeline/SingleThreadedDeliveryPipeline.java
@@ -0,0 +1,146 @@
+package at.gv.egiz.moazs.pipeline;
+
+
+import at.gv.egiz.moazs.MoaZSException;
+import at.gv.egiz.moazs.util.ClientFactory;
+import at.gv.egiz.moazs.repository.DeliveryRepository;
+import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;
+import at.gv.egiz.moazs.scheme.NameSpace;
+import at.gv.egiz.moazs.scheme.TnvzHelper;
+import at.gv.egiz.moazs.verify.SignatureVerifier;
+import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
+import at.gv.zustellung.msg.xsd.App2ZusePort;
+import at.gv.zustellung.msg.xsd.DeliveryAnswerType;
+import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType;
+import at.gv.zustellung.tnvz.xsd.TNVZServicePort;
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
+import static at.gv.egiz.moazs.util.NullCoalesce.coalesce;
+import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.Error.errorBuilder;
+import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.deliveryRequestStatusTypeBuilder;
+import static at.gv.zustellung.msg.xsd.ErrorInfoType.errorInfoTypeBuilder;
+import static java.lang.String.format;
+
+@Component
+@Profile("!cluster")
+public class SingleThreadedDeliveryPipeline implements DeliveryPipeline {
+
+    private static final Logger log = Logger.getLogger(SingleThreadedDeliveryPipeline.class);
+
+    private final DeliveryRepository repository;
+    private final TnvzHelper tnvzHelper;
+    private final Mzs2MsgConverter converter;
+    private final ClientFactory clientFactory;
+    private final SignatureVerifier verifier;
+
+    @Autowired
+    public SingleThreadedDeliveryPipeline(DeliveryRepository repository,
+                                          TnvzHelper tnvzHelper,
+                                          Mzs2MsgConverter converter,
+                                          ClientFactory clientFactory,
+                                          SignatureVerifier verifier
+    ) {
+        this.repository = repository;
+        this.tnvzHelper = tnvzHelper;
+        this.converter = converter;
+        this.clientFactory = clientFactory;
+        this.verifier = verifier;
+    }
+
+    @Override
+    public void processRequest(String appDeliveryId) {
+
+        var exceptionBuilder = moaZSExceptionBuilder();
+
+        try {
+            var mzsRequest = repository.getDeliveryRequest(appDeliveryId).orElseThrow();
+            exceptionBuilder.withMzsRequest(mzsRequest);
+
+            at.gv.zustellung.msg.xsd.DeliveryRequestType msgRequest = buildMsgRequest(mzsRequest, exceptionBuilder);
+            exceptionBuilder.withMsgRequest(msgRequest);
+
+            var msgClientParams = mzsRequest.getConfig().getMSGClient();
+            App2ZusePort client = clientFactory.create(msgClientParams, App2ZusePort.class);
+            var status = client.delivery(msgRequest);
+            exceptionBuilder.withMsgResult(status);
+
+            verifySignedStatus(appDeliveryId, exceptionBuilder);
+            repository.add(status);
+
+        } catch (MoaZSException exception) {
+
+            log.error(format("An error occured while processing the DeliveryRequest with AppDeliveryID=%s. ",
+                    appDeliveryId), exception);
+
+            var errorStatus = generateErrorStatus(exception, appDeliveryId);
+            repository.add(errorStatus);
+        }
+    }
+
+    private at.gv.zustellung.msg.xsd.DeliveryRequestType buildMsgRequest(DeliveryRequestType mzsRequest,
+                                                                         MoaZSException.Builder exceptionBuilder) {
+        if (mzsRequest.getConfig().isPerformQueryPersonRequest()) {
+            var tnvzClientParams = mzsRequest.getConfig().getTNVZClient();
+            TNVZServicePort tvnzPort = clientFactory.create(tnvzClientParams, TNVZServicePort.class);
+            var identification = tnvzHelper.performQueryPersonRequest(mzsRequest, tvnzPort, exceptionBuilder);
+            return converter.convert(mzsRequest, identification);
+        } else {
+            return converter.convert(mzsRequest);
+        }
+    }
+
+    private void verifySignedStatus(String appDeliveryId, MoaZSException.Builder exceptionBuilder) throws MoaZSException {
+
+        try {
+            var signedStatus = repository.getSignedDeliveryRequestStatus(appDeliveryId).get();
+            verifier.verify(signedStatus);
+        } catch (Exception ex) {
+            throw exceptionBuilder.withMessage(format("Signature of DeliveryRequestStatus with AppDeliveryID=%s " +
+                    " is not valid.", appDeliveryId))
+                    .withErrorCode(MoaZSException.ERROR_MOASP_SIGNATURE_INVALID)
+                    .withCause(ex)
+                    .build();
+        }
+    }
+
+    private DeliveryRequestStatusType generateErrorStatus(MoaZSException exception, String appDeliveryId) {
+
+        var infoBuilder = errorInfoTypeBuilder()
+                .withText(exception.getMessage())
+                .withCode(exception.getErrorCode());
+
+        var errorBuilder = errorBuilder()
+                .withErrorInfo(infoBuilder.build())
+                .withAppDeliveryID(appDeliveryId);
+
+        if (exception.getMzsRequest() != null) {
+            errorBuilder.withDeliverySystem(exception.getMzsRequest().getConfig().getMSGClient().getURL());
+        }
+
+        if (exception.getTnvzResult() != null && exception.getTnvzResult().getError() != null) {
+            errorBuilder.withPreAdviceNoteSent(exception.getTnvzResult().getError().getPreAdviceNoteSent());
+        }
+
+        if (exception.getMsgResult() != null) {
+            var answer = getAnswerFromResult(exception.getMsgResult());
+            errorBuilder.withGZ(answer.getGZ());
+            errorBuilder.withZSDeliveryID(answer.getZSDeliveryID());
+        }
+
+        return deliveryRequestStatusTypeBuilder()
+                .withError(errorBuilder.build())
+                .withVersion(NameSpace.MSG_VERSION)
+                .build();
+
+    }
+
+    private DeliveryAnswerType getAnswerFromResult(DeliveryRequestStatusType msgResult) {
+        return coalesce(msgResult.getSuccess(), msgResult.getPartialSuccess(), msgResult.getError()).get();
+    }
+
+
+}
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 c2f2415..d651570 100644
--- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java
+++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java
@@ -1,7 +1,7 @@
 package at.gv.egiz.moazs.preprocess;
 
 import at.gv.egiz.moazs.MoaZSException;
-import at.gv.egiz.moazs.mzs.MzsValidator;
+import at.gv.egiz.moazs.scheme.MzsDeliveryRequestValidator;
 import at.gv.egiz.moazs.util.StringUtils;
 import at.gv.zustellung.app2mzs.xsd.ConfigType;
 import org.slf4j.Logger;
@@ -30,7 +30,7 @@ public class ConfigProfileGenerator {
 
     private final SpringPropertiesFacade properties;
     private final ConfigUtil util;
-    private final MzsValidator validator;
+    private final MzsDeliveryRequestValidator validator;
     private final boolean verifyCompletenessOfDefaultConfiguration;
     private final String profilePrefix;
     private final String defaultConfigKey;
@@ -42,7 +42,7 @@ public class ConfigProfileGenerator {
     private ConfigProfileGenerator(
             SpringPropertiesFacade properties,
             ConfigUtil util,
-            MzsValidator validator, boolean verifyCompletenessOfDefaultConfiguration,
+            MzsDeliveryRequestValidator validator, boolean verifyCompletenessOfDefaultConfiguration,
             String profilePrefix,
             String defaultConfigKey) {
         this.util = util;
@@ -113,7 +113,7 @@ public class ConfigProfileGenerator {
     public static class ConfigProfileGeneratorBuilder {
         private SpringPropertiesFacade properties;
         private ConfigUtil util;
-        private MzsValidator validator;
+        private MzsDeliveryRequestValidator validator;
         private boolean verify = true;
         private String profilePrefix = "delivery-request-configuration-profiles";
         private String defaultConfigKey = "default";
@@ -128,7 +128,7 @@ public class ConfigProfileGenerator {
             return this;
         }
 
-        public ConfigProfileGeneratorBuilder withValidator(MzsValidator validator) {
+        public ConfigProfileGeneratorBuilder withValidator(MzsDeliveryRequestValidator validator) {
             this.validator = validator;
             return this;
         }
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 37dbdc5..783536c 100644
--- a/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java
+++ b/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java
@@ -1,6 +1,6 @@
 package at.gv.egiz.moazs.preprocess;
 
-import at.gv.egiz.moazs.mzs.MzsValidator;
+import at.gv.egiz.moazs.scheme.MzsDeliveryRequestValidator;
 import at.gv.zustellung.app2mzs.xsd.ConfigType;
 import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -16,7 +16,7 @@ public class DeliveryRequestAugmenter {
 
     private final ConfigUtil util;
     private final Map<String, ConfigType> configs;
-    private final MzsValidator validator;
+    private final MzsDeliveryRequestValidator validator;
 
     private static final String INCOMPLETE_TNVZ_ERROR_MESSAGE = "mzs:DeliveryRequest is incomplete because mandatory " +
             "fields for sending a tnvz:QueryPersonRequest are missing.";
@@ -27,7 +27,7 @@ public class DeliveryRequestAugmenter {
             "configuration is incomplete.";
 
     @Autowired
-    public DeliveryRequestAugmenter(Map<String, ConfigType> deliveryRequestConfigs, ConfigUtil util, MzsValidator validator) {
+    public DeliveryRequestAugmenter(Map<String, ConfigType> deliveryRequestConfigs, ConfigUtil util, MzsDeliveryRequestValidator validator) {
         this.configs = deliveryRequestConfigs;
         this.util = util;
         this.validator = validator;
diff --git a/src/main/java/at/gv/egiz/moazs/scheme/MzsClient.java b/src/main/java/at/gv/egiz/moazs/scheme/MzsClient.java
new file mode 100644
index 0000000..98e0bc8
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/scheme/MzsClient.java
@@ -0,0 +1,13 @@
+package at.gv.egiz.moazs.scheme;
+
+import at.gv.zustellung.app2mzs.xsd.DeliveryResponseType;
+import org.springframework.stereotype.Component;
+
+@Component
+public class MzsClient {
+
+    public void sendNotification(DeliveryResponseType responseType) {
+        throw new UnsupportedOperationException("Not implemented.");
+    }
+
+}
diff --git a/src/main/java/at/gv/egiz/moazs/scheme/MzsDeliveryRequestValidator.java b/src/main/java/at/gv/egiz/moazs/scheme/MzsDeliveryRequestValidator.java
new file mode 100644
index 0000000..03f2664
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/scheme/MzsDeliveryRequestValidator.java
@@ -0,0 +1,74 @@
+package at.gv.egiz.moazs.scheme;
+
+import at.gv.zustellung.app2mzs.xsd.ClientType;
+import at.gv.zustellung.app2mzs.xsd.ConfigType;
+import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
+import at.gv.zustellung.app2mzs.xsd.KeyStoreType;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+
+@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.
+     */
+    public boolean isTnvzComplete(DeliveryRequestType request) {
+        return !request.getConfig().isPerformQueryPersonRequest() ||
+                (request.getTnvzMetaData() != null
+                && request.getSender().getCorporateBody() != null);
+
+
+    }
+
+    /**
+     * Check if all mandatory fields of configuration are present.
+     *
+     * @param profile
+     * @return true if all mandatory fields are present.
+     */
+    public boolean isConfigProfileComplete(@Nullable ConfigType profile) {
+        return profile != null
+                && profile.isPerformQueryPersonRequest() != null
+                && isTVNZClientConfigured(profile.getTNVZClient(), profile.isPerformQueryPersonRequest())
+                && isMSGClientConfigured(profile.getMSGClient());
+    }
+
+    private boolean isTVNZClientConfigured(ClientType tnvzClient, Boolean isPerformQueryPersonRequest) {
+        return !isPerformQueryPersonRequest || (tnvzClient != null
+                && tnvzClient.getURL() != null
+                && tnvzClient.getReceiveTimeout() != null
+                && tnvzClient.getConnectionTimeout() != null
+                && isSSLConfigured(tnvzClient));
+    }
+
+    private boolean isMSGClientConfigured(ClientType msgClientParams) {
+        return msgClientParams != null
+                && msgClientParams.getURL() != null
+                && isSSLConfigured(msgClientParams)
+                && msgClientParams.getReceiveTimeout() != null
+                && msgClientParams.getConnectionTimeout() != null;
+    }
+
+    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 boolean isKeyStoreConfigured(KeyStoreType keyStore) {
+        return keyStore == null || (keyStore.getPassword() != null
+                && keyStore.getFileType() != null
+                && keyStore.getFileName() != null);
+    }
+
+    private boolean isTrustStoreConfigured(KeyStoreType trustStore) {
+        return trustStore == null || (trustStore.getPassword() != null
+                && "JKS".equals(trustStore.getFileType())
+                && trustStore.getFileName() != null);
+    }
+}
diff --git a/src/main/java/at/gv/egiz/moazs/scheme/MzsService.java b/src/main/java/at/gv/egiz/moazs/scheme/MzsService.java
new file mode 100644
index 0000000..f245b93
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/scheme/MzsService.java
@@ -0,0 +1,104 @@
+package at.gv.egiz.moazs.scheme;
+
+import at.gv.egiz.moazs.pipeline.DeliveryPipeline;
+import at.gv.egiz.moazs.preprocess.DeliveryRequestAugmenter;
+import at.gv.egiz.moazs.repository.DeliveryRepository;
+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;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.jws.WebParam;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSException;
+import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
+import static at.gv.zustellung.app2mzs.xsd.PartialSuccessType.partialSuccessTypeBuilder;
+import static java.util.concurrent.CompletableFuture.supplyAsync;
+
+//todo : validate Schema in both directions.
+@Service
+@SchemaValidation(type = SchemaValidation.SchemaValidationType.IN)
+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 final DeliveryRepository repository;
+    private final DeliveryPipeline pipeline;
+    private final MzsClient appClient;
+    private final DeliveryRequestAugmenter augmenter;
+    private final Msg2MzsConverter converter;
+
+    @Autowired
+    public MzsService(DeliveryRepository repository, DeliveryPipeline pipeline, MzsClient appClient,
+                      DeliveryRequestAugmenter augmenter, Msg2MzsConverter converter) {
+        this.repository = repository;
+        this.pipeline = pipeline;
+        this.appClient = appClient;
+        this.augmenter = augmenter;
+        this.converter = converter;
+    }
+
+    @Override
+    public DeliveryResponseType app2Mzs(
+            @WebParam(partName = "DeliveryRequest",
+                      name = "DeliveryRequest")
+                    DeliveryRequestType deliveryRequest) {
+
+        var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID();
+
+        var future = supplyAsync(() -> augmenter.augment(deliveryRequest))
+                .thenApply(this::process)
+                .thenApply(status -> converter.convert(status, repository.getSignedDeliveryRequestStatus(appDeliveryID)));
+
+        try {
+            return future.get(TIMEOUT_FOR_ANWSER, TimeUnit.SECONDS);
+        } catch (TimeoutException e) {
+            logger.info("Answer Timed Out", e);
+
+            future.thenAccept(appClient::sendNotification);
+            return generatePartialSuccessResponse(appDeliveryID);
+
+        } catch (Exception e) {
+            throw moaZSExceptionBuilder("An error occurred while processing DeliveryRequest " +
+                    "with AppDeliveryID=%s.", appDeliveryID)
+                    .withCause(e)
+                    .build();
+        }
+
+    }
+
+    private DeliveryRequestStatusType process(DeliveryRequestType deliveryRequest) {
+
+        var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID();
+        logger.info("Receive request with appDeliveryID = {}.", appDeliveryID);
+
+        repository.add(deliveryRequest);
+        pipeline.processRequest(appDeliveryID);
+
+        return repository.getDeliveryRequestStatus(appDeliveryID)
+                .orElseThrow(() -> moaZSException("Could not get a response for AppDeliveryID=%s.", appDeliveryID));
+
+    }
+
+    private DeliveryResponseType generatePartialSuccessResponse(String appDeliveryId) {
+
+        var partial = partialSuccessTypeBuilder()
+                .withAppDeliveryID(appDeliveryId)
+                .build();
+
+        return DeliveryResponseType.deliveryResponseTypeBuilder()
+                .withPartialSuccess(partial)
+                .build();
+    }
+
+}
diff --git a/src/main/java/at/gv/egiz/moazs/scheme/TnvzHelper.java b/src/main/java/at/gv/egiz/moazs/scheme/TnvzHelper.java
new file mode 100644
index 0000000..70c20bb
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/scheme/TnvzHelper.java
@@ -0,0 +1,232 @@
+package at.gv.egiz.moazs.scheme;
+
+import at.gv.egiz.moazs.MoaZSException;
+import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;
+import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
+import at.gv.zustellung.app2mzs.xsd.persondata.AbstractAddressType;
+import at.gv.zustellung.msg.xsd.persondata.IdentificationType;
+import at.gv.zustellung.msg.xsd.persondata.ObjectFactory;
+import at.gv.zustellung.msg.xsd.persondata.PostalAddressType;
+import at.gv.zustellung.tnvz.xsd.*;
+import at.gv.zustellung.tnvz.xsd.QueryPersonRequest.QueryEntryList;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nullable;
+import javax.xml.bind.JAXBElement;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static at.gv.zustellung.tnvz.xsd.PersonQueryType.MetaData.metaDataBuilder;
+import static at.gv.zustellung.tnvz.xsd.PersonQueryType.personQueryTypeBuilder;
+import static at.gv.zustellung.tnvz.xsd.QueryPersonRequest.QueryEntryList.queryEntryListBuilder;
+import static at.gv.zustellung.tnvz.xsd.QueryPersonRequest.queryPersonRequestBuilder;
+import static at.gv.zustellung.tnvz.xsd.Receiver.receiverBuilder;
+import static at.gv.zustellung.tnvz.xsd.Sender.senderBuilder;
+import static java.lang.String.join;
+import static java.util.stream.Collectors.toSet;
+
+@Component
+public class TnvzHelper {
+
+    private final Mzs2MsgConverter converter;
+
+    private static final ObjectFactory FACTORY = new at.gv.zustellung.msg.xsd.persondata.ObjectFactory();
+    private static final String ENTRY_ID = "entry-id";
+    private static final String RECEIVER_NOT_ADRESSABLE_ERROR_MSG = "Receiver is not addressable. Reason: %s";
+    private static final String MIMETYPE_MISSMATCH_ERROR_MSG = "Request contains attachment of type(s) %s, but " +
+            "receiver only accepts attachments of type(s) %s.";
+    private static final String MZS_NO_TNVZ_PERSON_QUERY_RESULTS_ERROR_MSG = "tnvz:QueryResultList was empty.";
+
+
+    @Autowired
+    public TnvzHelper(Mzs2MsgConverter converter) {
+        this.converter = converter;
+    }
+
+    /**
+     * Performs all tasks related to making a request to the tnvz service:
+     * Derives a tnvz:QueryPersonRequest from the {@code mzsRequest}, sends the QueryPersonRequest to
+     * {@code tvnzPort}, validates the tnvz's response and extracts the {@code Identification} Element.
+     * @param mzsRequest Data source for the QueryPersonRequest
+     * @param tvnzPort Client for communicating with the tnvz service
+     * @param exceptionBuilder Utility to collect information and build a meaningful exception in case of errors.
+     * @throws MoaZSException in case of an error.
+     * @return
+     */
+    public IdentificationType performQueryPersonRequest(DeliveryRequestType mzsRequest,
+                                                        TNVZServicePort tvnzPort,
+                                                        MoaZSException.Builder exceptionBuilder) {
+
+        var tvnzQuery = buildQuery(mzsRequest);
+        var tvnzResponse = tvnzPort.queryPerson(tvnzQuery);
+        verifyResponse(tvnzResponse, exceptionBuilder);
+
+        var tvnzResult = getResult(tvnzResponse);
+        var typesInRequest = extractListOfMimemtypesIn(mzsRequest);
+        checkMimetypes(tvnzResult, typesInRequest, exceptionBuilder);
+
+        return tvnzResult.getSuccess().getIdentification();
+    }
+
+    private QueryPersonRequest buildQuery(DeliveryRequestType mzsRequest) {
+
+        Sender sender = extractSender(mzsRequest.getSender());
+        Receiver receiver = extractReceiver(mzsRequest.getReceiver());
+        var metadata = extractMetaData(mzsRequest);
+
+        PersonQueryType personQuery = personQueryTypeBuilder()
+                .withEntryID(ENTRY_ID)
+                .withSender(sender)
+                .withReceiver(receiver)
+                .withMetaData(metadata)
+                .build();
+
+        QueryEntryList entryList = queryEntryListBuilder()
+                .withQueryEntry(List.of(personQuery))
+                .build();
+
+        return queryPersonRequestBuilder()
+                .withQueryEntryList(entryList)
+                .build();
+    }
+
+    private PersonQueryType.MetaData extractMetaData(DeliveryRequestType request) {
+
+        var builder = metaDataBuilder();
+
+        var meta = request.getTnvzMetaData();
+
+        if (meta.getDeliveryQuality() != null) {
+            builder.withDeliveryQuality(meta.getDeliveryQuality());
+        } else {
+            builder.withPrivateMessageQuality(meta.getPrivateMessageQuality());
+        }
+
+        return builder
+                .withOrigin(meta.getOrigin())
+                .withPreAdviceNote(request.getReceiver().getPreAdviceNote())
+                .withIgnorePostRedirectionOrder(meta.getIgnorePostRedirectionOrder())
+                .build();
+    }
+
+    private Sender extractSender(DeliveryRequestType.Sender sender) {
+
+        var corporateBody = sender.getCorporateBody();
+        var mzsIdentification = corporateBody.getIdentification().get(0);
+        var msgIdentification = converter.convert(mzsIdentification);
+
+        var msgPerson = FACTORY.createPerson(converter.convert(corporateBody));
+
+        return senderBuilder()
+                .withIdentification(msgIdentification)
+                .withPerson(msgPerson)
+                .build();
+    }
+
+    private Receiver extractReceiver(DeliveryRequestType.Receiver receiver) {
+
+        var builder = receiverBuilder();
+
+        if (receiver.getIdentification() == null) {
+            builder
+                .withPerson(converter.convert(receiver.getPerson()))
+                .withAustrianAddressesOnly(receiver.getAustrianAddressesOnly());
+
+            var postalAddress = findPostalAddress(receiver.getAddress());
+            if (postalAddress != null)
+                builder.withPostalAddress(postalAddress);
+
+            if (receiver.getPreAdviceNote() != null) {
+                builder.withNotificationAddressList(receiver.getPreAdviceNote().getNotificationAddressList());
+            }
+        } else {
+            builder.withIdentification(converter.convert(receiver.getIdentification()));
+        }
+
+        return builder.build();
+    }
+
+    private @Nullable PostalAddressType findPostalAddress(List<JAXBElement<? extends AbstractAddressType>> addresses) {
+
+        if (addresses == null) return null;
+
+        for (JAXBElement<? extends AbstractAddressType> address : addresses) {
+            if(address.getValue() instanceof at.gv.zustellung.app2mzs.xsd.persondata.PostalAddressType) {
+                var mzsPostalAddress = (at.gv.zustellung.app2mzs.xsd.persondata.PostalAddressType) address.getValue();
+                return converter.convert(mzsPostalAddress);
+            }
+        }
+
+        return null;
+
+    }
+
+    private Set<String> extractListOfMimemtypesIn(DeliveryRequestType mzsRequest) {
+        return mzsRequest.getPayload().stream()
+                .map(DeliveryRequestType.Payload::getMIMEType)
+                .collect(toSet());
+    }
+
+    private void verifyResponse(QueryPersonResponse tvnzResponse, MoaZSException.Builder mzsBuilder) {
+
+        var error = tvnzResponse.getError();
+        if (error != null) {
+            throw mzsBuilder.withErrorCode(error.getCode())
+                    .withMessage(error.getText())
+                    .build();
+        }
+
+        var results = tvnzResponse.getQueryResultList().getQueryResult();
+        if (results.isEmpty()) {
+            throw mzsBuilder.withErrorCode(MoaZSException.ERROR_MZS_NO_TNVZ_PERSON_QUERY_RESULTS)
+                    .withMessage(MZS_NO_TNVZ_PERSON_QUERY_RESULTS_ERROR_MSG)
+                    .build();
+        }
+
+        var tnvzResult = results.get(0);
+        mzsBuilder.withTnvzResult(tnvzResult);
+        if (tnvzResult.getError() != null) {
+            var info = tnvzResult.getError().getErrorInfo();
+            throw mzsBuilder.withErrorCode(info.getCode())
+                    .withMessage(RECEIVER_NOT_ADRESSABLE_ERROR_MSG, info.getText())
+                    .build();
+        }
+    }
+
+    private PersonResultType getResult(QueryPersonResponse tvnzResponse) {
+        return tvnzResponse.getQueryResultList().getQueryResult().get(0);
+    }
+
+    private void checkMimetypes(PersonResultType tnvzResult, Set<String> typesInRequest, MoaZSException.Builder mzsBuilder) {
+        var mismatchedTypes = findMimeTypeMismatches(tnvzResult, typesInRequest);
+        if (!mismatchedTypes.isEmpty()) {
+            var acceptedTypesString = join(",", getAcceptedTypes(tnvzResult));
+            var mismatchedTypesString = join(",", mismatchedTypes);
+            throw mzsBuilder.withErrorCode(MoaZSException.ERROR_MZS_MIMETYPE_MISSMATCH)
+                    .withMessage(MIMETYPE_MISSMATCH_ERROR_MSG, mismatchedTypesString, acceptedTypesString)
+                    .build();
+        }
+    }
+
+    private Collection<String> findMimeTypeMismatches(PersonResultType result, Set<String> typesInRequest) {
+        var acceptedTypes = getAcceptedTypes(result);
+
+        if (acceptedTypes.contains("*/*")) {
+            return List.of();
+        }
+
+        var typesInRequestCopy = new HashSet<>(typesInRequest);
+        typesInRequestCopy.removeAll(acceptedTypes);
+
+        return typesInRequestCopy;
+    }
+
+    private List<String> getAcceptedTypes(PersonResultType result) {
+        return result.getSuccess().getMimeTypeList().getMimeType();
+    }
+
+
+}
diff --git a/src/main/java/at/gv/egiz/moazs/tnvz/TnvzHelper.java b/src/main/java/at/gv/egiz/moazs/tnvz/TnvzHelper.java
deleted file mode 100644
index f71b992..0000000
--- a/src/main/java/at/gv/egiz/moazs/tnvz/TnvzHelper.java
+++ /dev/null
@@ -1,232 +0,0 @@
-package at.gv.egiz.moazs.tnvz;
-
-import at.gv.egiz.moazs.MoaZSException;
-import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;
-import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
-import at.gv.zustellung.app2mzs.xsd.persondata.AbstractAddressType;
-import at.gv.zustellung.msg.xsd.persondata.IdentificationType;
-import at.gv.zustellung.msg.xsd.persondata.ObjectFactory;
-import at.gv.zustellung.msg.xsd.persondata.PostalAddressType;
-import at.gv.zustellung.tnvz.xsd.*;
-import at.gv.zustellung.tnvz.xsd.QueryPersonRequest.QueryEntryList;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.Nullable;
-import javax.xml.bind.JAXBElement;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static at.gv.zustellung.tnvz.xsd.PersonQueryType.MetaData.metaDataBuilder;
-import static at.gv.zustellung.tnvz.xsd.PersonQueryType.personQueryTypeBuilder;
-import static at.gv.zustellung.tnvz.xsd.QueryPersonRequest.QueryEntryList.queryEntryListBuilder;
-import static at.gv.zustellung.tnvz.xsd.QueryPersonRequest.queryPersonRequestBuilder;
-import static at.gv.zustellung.tnvz.xsd.Receiver.receiverBuilder;
-import static at.gv.zustellung.tnvz.xsd.Sender.senderBuilder;
-import static java.lang.String.join;
-import static java.util.stream.Collectors.toSet;
-
-@Component
-public class TnvzHelper {
-
-    private final Mzs2MsgConverter converter;
-
-    private static final ObjectFactory FACTORY = new at.gv.zustellung.msg.xsd.persondata.ObjectFactory();
-    private static final String ENTRY_ID = "entry-id";
-    private static final String RECEIVER_NOT_ADRESSABLE_ERROR_MSG = "Receiver is not addressable. Reason: %s";
-    private static final String MIMETYPE_MISSMATCH_ERROR_MSG = "Request contains attachment of type(s) %s, but " +
-            "receiver only accepts attachments of type(s) %s.";
-    private static final String MZS_NO_TNVZ_PERSON_QUERY_RESULTS_ERROR_MSG = "tnvz:QueryResultList was empty.";
-
-
-    @Autowired
-    public TnvzHelper(Mzs2MsgConverter converter) {
-        this.converter = converter;
-    }
-
-    /**
-     * Performs all tasks related to making a request to the tnvz service:
-     * Derives a tnvz:QueryPersonRequest from the {@code mzsRequest}, sends the QueryPersonRequest to
-     * {@code tvnzPort}, validates the tnvz's response and extracts the {@code Identification} Element.
-     * @param mzsRequest Data source for the QueryPersonRequest
-     * @param tvnzPort Client for communicating with the tnvz service
-     * @param exceptionBuilder Utility to collect information and build a meaningful exception in case of errors.
-     * @throws MoaZSException in case of an error.
-     * @return
-     */
-    public IdentificationType performQueryPersonRequest(DeliveryRequestType mzsRequest,
-                                                        TNVZServicePort tvnzPort,
-                                                        MoaZSException.Builder exceptionBuilder) {
-
-        var tvnzQuery = buildQuery(mzsRequest);
-        var tvnzResponse = tvnzPort.queryPerson(tvnzQuery);
-        verifyResponse(tvnzResponse, exceptionBuilder);
-
-        var tvnzResult = getResult(tvnzResponse);
-        var typesInRequest = extractListOfMimemtypesIn(mzsRequest);
-        checkMimetypes(tvnzResult, typesInRequest, exceptionBuilder);
-
-        return tvnzResult.getSuccess().getIdentification();
-    }
-
-    private QueryPersonRequest buildQuery(DeliveryRequestType mzsRequest) {
-
-        Sender sender = extractSender(mzsRequest.getSender());
-        Receiver receiver = extractReceiver(mzsRequest.getReceiver());
-        var metadata = extractMetaData(mzsRequest);
-
-        PersonQueryType personQuery = personQueryTypeBuilder()
-                .withEntryID(ENTRY_ID)
-                .withSender(sender)
-                .withReceiver(receiver)
-                .withMetaData(metadata)
-                .build();
-
-        QueryEntryList entryList = queryEntryListBuilder()
-                .withQueryEntry(List.of(personQuery))
-                .build();
-
-        return queryPersonRequestBuilder()
-                .withQueryEntryList(entryList)
-                .build();
-    }
-
-    private PersonQueryType.MetaData extractMetaData(DeliveryRequestType request) {
-
-        var builder = metaDataBuilder();
-
-        var meta = request.getTnvzMetaData();
-
-        if (meta.getDeliveryQuality() != null) {
-            builder.withDeliveryQuality(meta.getDeliveryQuality());
-        } else {
-            builder.withPrivateMessageQuality(meta.getPrivateMessageQuality());
-        }
-
-        return builder
-                .withOrigin(meta.getOrigin())
-                .withPreAdviceNote(request.getReceiver().getPreAdviceNote())
-                .withIgnorePostRedirectionOrder(meta.getIgnorePostRedirectionOrder())
-                .build();
-    }
-
-    private Sender extractSender(DeliveryRequestType.Sender sender) {
-
-        var corporateBody = sender.getCorporateBody();
-        var mzsIdentification = corporateBody.getIdentification().get(0);
-        var msgIdentification = converter.convert(mzsIdentification);
-
-        var msgPerson = FACTORY.createPerson(converter.convert(corporateBody));
-
-        return senderBuilder()
-                .withIdentification(msgIdentification)
-                .withPerson(msgPerson)
-                .build();
-    }
-
-    private Receiver extractReceiver(DeliveryRequestType.Receiver receiver) {
-
-        var builder = receiverBuilder();
-
-        if (receiver.getIdentification() == null) {
-            builder
-                .withPerson(converter.convert(receiver.getPerson()))
-                .withAustrianAddressesOnly(receiver.getAustrianAddressesOnly());
-
-            var postalAddress = findPostalAddress(receiver.getAddress());
-            if (postalAddress != null)
-                builder.withPostalAddress(postalAddress);
-
-            if (receiver.getPreAdviceNote() != null) {
-                builder.withNotificationAddressList(receiver.getPreAdviceNote().getNotificationAddressList());
-            }
-        } else {
-            builder.withIdentification(converter.convert(receiver.getIdentification()));
-        }
-
-        return builder.build();
-    }
-
-    private @Nullable PostalAddressType findPostalAddress(List<JAXBElement<? extends AbstractAddressType>> addresses) {
-
-        if (addresses == null) return null;
-
-        for (JAXBElement<? extends AbstractAddressType> address : addresses) {
-            if(address.getValue() instanceof at.gv.zustellung.app2mzs.xsd.persondata.PostalAddressType) {
-                var mzsPostalAddress = (at.gv.zustellung.app2mzs.xsd.persondata.PostalAddressType) address.getValue();
-                return converter.convert(mzsPostalAddress);
-            }
-        }
-
-        return null;
-
-    }
-
-    private Set<String> extractListOfMimemtypesIn(DeliveryRequestType mzsRequest) {
-        return mzsRequest.getPayload().stream()
-                .map(DeliveryRequestType.Payload::getMIMEType)
-                .collect(toSet());
-    }
-
-    private void verifyResponse(QueryPersonResponse tvnzResponse, MoaZSException.Builder mzsBuilder) {
-
-        var error = tvnzResponse.getError();
-        if (error != null) {
-            throw mzsBuilder.withErrorCode(error.getCode())
-                    .withMessage(error.getText())
-                    .build();
-        }
-
-        var results = tvnzResponse.getQueryResultList().getQueryResult();
-        if (results.isEmpty()) {
-            throw mzsBuilder.withErrorCode(MoaZSException.ERROR_MZS_NO_TNVZ_PERSON_QUERY_RESULTS)
-                    .withMessage(MZS_NO_TNVZ_PERSON_QUERY_RESULTS_ERROR_MSG)
-                    .build();
-        }
-
-        var tnvzResult = results.get(0);
-        mzsBuilder.withTnvzResult(tnvzResult);
-        if (tnvzResult.getError() != null) {
-            var info = tnvzResult.getError().getErrorInfo();
-            throw mzsBuilder.withErrorCode(info.getCode())
-                    .withMessage(RECEIVER_NOT_ADRESSABLE_ERROR_MSG, info.getText())
-                    .build();
-        }
-    }
-
-    private PersonResultType getResult(QueryPersonResponse tvnzResponse) {
-        return tvnzResponse.getQueryResultList().getQueryResult().get(0);
-    }
-
-    private void checkMimetypes(PersonResultType tnvzResult, Set<String> typesInRequest, MoaZSException.Builder mzsBuilder) {
-        var mismatchedTypes = findMimeTypeMismatches(tnvzResult, typesInRequest);
-        if (!mismatchedTypes.isEmpty()) {
-            var acceptedTypesString = join(",", getAcceptedTypes(tnvzResult));
-            var mismatchedTypesString = join(",", mismatchedTypes);
-            throw mzsBuilder.withErrorCode(MoaZSException.ERROR_MZS_MIMETYPE_MISSMATCH)
-                    .withMessage(MIMETYPE_MISSMATCH_ERROR_MSG, mismatchedTypesString, acceptedTypesString)
-                    .build();
-        }
-    }
-
-    private Collection<String> findMimeTypeMismatches(PersonResultType result, Set<String> typesInRequest) {
-        var acceptedTypes = getAcceptedTypes(result);
-
-        if (acceptedTypes.contains("*/*")) {
-            return List.of();
-        }
-
-        var typesInRequestCopy = new HashSet<>(typesInRequest);
-        typesInRequestCopy.removeAll(acceptedTypes);
-
-        return typesInRequestCopy;
-    }
-
-    private List<String> getAcceptedTypes(PersonResultType result) {
-        return result.getSuccess().getMimeTypeList().getMimeType();
-    }
-
-
-}
diff --git a/src/main/java/at/gv/egiz/moazs/util/ClientFactory.java b/src/main/java/at/gv/egiz/moazs/util/ClientFactory.java
new file mode 100644
index 0000000..e7761fe
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/util/ClientFactory.java
@@ -0,0 +1,111 @@
+package at.gv.egiz.moazs.util;
+
+import at.gv.zustellung.app2mzs.xsd.ClientType;
+import at.gv.zustellung.app2mzs.xsd.KeyStoreType;
+import at.gv.zustellung.app2mzs.xsd.SSLType;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.frontend.ClientProxy;
+import org.apache.cxf.jaxws.JaxWsClientFactoryBean;
+import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nullable;
+import javax.net.ssl.SSLContext;
+import javax.xml.ws.BindingProvider;
+import javax.xml.ws.soap.SOAPBinding;
+
+import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder;
+
+@Component
+public class ClientFactory {
+
+    private static final Logger log = LoggerFactory.getLogger(ClientFactory.class);
+
+    private final StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor;
+    private final SSLContextCreator sslContextCreator;
+    private final FileUtils fileUtils;
+
+    @Autowired
+    public ClientFactory(StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor,
+                         SSLContextCreator creator,
+                         FileUtils fileUtils) {
+        this.storeResponseInterceptor = storeResponseInterceptor;
+        this.sslContextCreator = creator;
+        this.fileUtils = fileUtils;
+    }
+
+    /**
+     * Creates a client that communicates with a soap service.
+     *
+     * @param params for the client, such as service url and ssl parameters.
+     * @return the client
+     */
+    public <T> T create(ClientType params, Class<T> clazz) {
+
+        var factory = new JaxWsClientFactoryBean();
+        factory.setServiceClass(clazz);
+        factory.setAddress(params.getURL());
+        factory.getInInterceptors().add(storeResponseInterceptor);
+
+        var proxy = new JaxWsProxyFactoryBean(factory).create();
+        Client client = ClientProxy.getClient(proxy);
+        HTTPConduit http = (HTTPConduit) client.getConduit();
+
+        var bindingProvider = (BindingProvider) proxy;
+        SOAPBinding binding = (SOAPBinding) bindingProvider.getBinding();
+        binding.setMTOMEnabled(true);
+
+        var httpClientPolicy = new HTTPClientPolicy();
+        httpClientPolicy.setConnectionTimeout(params.getConnectionTimeout().longValueExact());
+        httpClientPolicy.setReceiveTimeout(params.getReceiveTimeout().longValueExact());
+        http.setClient(httpClientPolicy);
+
+        if (params.getURL().startsWith("https")) {
+            TLSClientParameters tlsParams = setupTLSParams(params.getSSL());
+            http.setTlsClientParameters(tlsParams);
+            log.info("SSLContext initialized. ");
+        }
+
+        return ((T)proxy);
+    }
+
+    private TLSClientParameters setupTLSParams(SSLType ssl) {
+
+        var tlsParams = new TLSClientParameters();
+        var keystore = resolveKeyStorePath(ssl.getKeyStore());
+
+        SSLContext sslContext;
+        if (ssl.isTrustAll()) {
+            sslContext = sslContextCreator.createUnsafeSSLContext(keystore);
+        } else {
+            var truststore = resolveKeyStorePath(ssl.getTrustStore());
+            sslContext = sslContextCreator.createSSLContext(keystore, truststore);
+        }
+        tlsParams.setSSLSocketFactory(sslContext.getSocketFactory());
+
+        if (ssl.isLaxHostNameVerification()) {
+            tlsParams.setDisableCNCheck(true);
+        }
+
+        return tlsParams;
+    }
+
+    private KeyStoreType resolveKeyStorePath(@Nullable KeyStoreType store) {
+
+        if (store == null) return null;
+
+        var resolvedURI = "file:" + fileUtils.determinePath(store.getFileName());
+        log.trace("Resolved key store path from {} to {}.", store.getFileName(), resolvedURI);
+
+        return keyStoreTypeBuilder(store)
+                .withFileName(resolvedURI)
+                .build();
+    }
+
+}
diff --git a/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java b/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java
new file mode 100644
index 0000000..2db81ab
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java
@@ -0,0 +1,64 @@
+package at.gv.egiz.moazs.util;
+
+import at.gv.egiz.moazs.repository.DeliveryRepository;
+import at.gv.egiz.moazs.scheme.SOAPUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSException;
+
+@Component
+public class StoreSOAPBodyBinaryInRepositoryInterceptor extends AbstractPhaseInterceptor<Message> {
+
+    private static final Logger log = LoggerFactory.getLogger(StoreSOAPBodyBinaryInRepositoryInterceptor.class);
+
+    private final CXFMessageUtils messageUtils;
+    private final SOAPUtils soapUtils;
+    private final DeliveryRepository repository;
+
+    @Autowired
+    public StoreSOAPBodyBinaryInRepositoryInterceptor(CXFMessageUtils extractor, SOAPUtils soapUtils,
+                                                      DeliveryRepository repository) {
+        super(Phase.RECEIVE);
+        this.messageUtils = extractor;
+        this.soapUtils = soapUtils;
+        this.repository = repository;
+    }
+
+    public void handleMessage(Message message) {
+
+        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) {
+                Element document = soapUtils.toDOM(content);
+                byte[] status = soapUtils.unwrapSoapEnvelope(document);
+                String appDeliveryID = soapUtils.getAppDeliveryIDFrom(document);
+                repository.addSignedDeliveryRequestStatus(status, appDeliveryID);
+
+                if(log.isTraceEnabled()) {
+                    log.trace("DeliveryRequestStatus with AppDeliveryID={} unwrapped and stored: {}. ",
+                            appDeliveryID, new String(status, StandardCharsets.UTF_8));
+                }
+            }
+        } catch (ParserConfigurationException | SAXException | IOException | NullPointerException e) {
+            throw moaZSException("Could not extract signed data from message.", e);
+        }
+    }
+
+}
diff --git a/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java b/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java
index 3157447..ff981a9 100644
--- a/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java
+++ b/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java
@@ -1,6 +1,6 @@
 package at.gv.egiz.moazs;
 
-import at.gv.egiz.moazs.mzs.MzsValidator;
+import at.gv.egiz.moazs.scheme.MzsDeliveryRequestValidator;
 import at.gv.egiz.moazs.preprocess.*;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -25,7 +25,7 @@ public class ConfigProfileGeneratorTest {
     private ConfigUtil util;
 
     @Mock
-    private MzsValidator validator;
+    private MzsDeliveryRequestValidator validator;
 
     @Mock
     private SpringPropertiesFacade properties;
diff --git a/src/test/java/at/gv/egiz/moazs/DeliveryRequestAugmenterTest.java b/src/test/java/at/gv/egiz/moazs/DeliveryRequestAugmenterTest.java
index 424dce1..3bc0d3b 100644
--- a/src/test/java/at/gv/egiz/moazs/DeliveryRequestAugmenterTest.java
+++ b/src/test/java/at/gv/egiz/moazs/DeliveryRequestAugmenterTest.java
@@ -1,6 +1,6 @@
 package at.gv.egiz.moazs;
 
-import at.gv.egiz.moazs.mzs.MzsValidator;
+import at.gv.egiz.moazs.scheme.MzsDeliveryRequestValidator;
 import at.gv.egiz.moazs.preprocess.ConfigUtil;
 import at.gv.egiz.moazs.preprocess.DeliveryRequestAugmenter;
 import at.gv.zustellung.app2mzs.xsd.ConfigType;
@@ -29,7 +29,7 @@ public class DeliveryRequestAugmenterTest {
     private ConfigUtil configUtil;
 
     @Mock
-    private MzsValidator validator;
+    private MzsDeliveryRequestValidator validator;
 
     @Before
     public void setupMock() {
diff --git a/src/test/java/at/gv/egiz/moazs/ITMsgClientTest.java b/src/test/java/at/gv/egiz/moazs/ITMsgClientTest.java
index bb9633e..70e300c 100644
--- a/src/test/java/at/gv/egiz/moazs/ITMsgClientTest.java
+++ b/src/test/java/at/gv/egiz/moazs/ITMsgClientTest.java
@@ -1,6 +1,6 @@
 package at.gv.egiz.moazs;
 
-import at.gv.egiz.moazs.msg.ClientFactory;
+import at.gv.egiz.moazs.util.ClientFactory;
 import at.gv.egiz.moazs.scheme.Marshaller;
 import at.gv.zustellung.app2mzs.xsd.ClientType;
 import at.gv.zustellung.app2mzs.xsd.KeyStoreType;
diff --git a/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java b/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java
deleted file mode 100644
index 74e6157..0000000
--- a/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java
+++ /dev/null
@@ -1,189 +0,0 @@
-package at.gv.egiz.moazs;
-
-import at.gv.egiz.moazs.msg.ClientFactory;
-import at.gv.egiz.moazs.pipeline.DeliveryPipeline;
-import at.gv.egiz.moazs.pipeline.SameThreadDeliveryPipeline;
-import at.gv.egiz.moazs.repository.DeliveryRepository;
-import at.gv.egiz.moazs.repository.InMemoryDeliveryRepository;
-import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;
-import at.gv.egiz.moazs.tnvz.TnvzHelper;
-import at.gv.egiz.moazs.verify.MoaSPSSSignatureVerifier;
-import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
-import at.gv.zustellung.msg.xsd.App2ZusePort;
-import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType;
-import at.gv.zustellung.msg.xsd.MetaData;
-import at.gv.zustellung.msg.xsd.persondata.IdentificationType;
-import at.gv.zustellung.tnvz.xsd.TNVZServicePort;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import static at.gv.egiz.moazs.MoaZSException.moaZSException;
-import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
-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.DeliveryRequestType.deliveryRequestTypeBuilder;
-import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.Success.successBuilder;
-import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.deliveryRequestStatusTypeBuilder;
-import static at.gv.zustellung.msg.xsd.MetaData.metaDataBuilder;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.*;
-
-@RunWith(MockitoJUnitRunner.class)
-public class SameThreadDeliveryPipelineTest {
-
-    private DeliveryRepository repository = new InMemoryDeliveryRepository();
-
-    @Mock
-    private TnvzHelper tnvzHelper;
-
-    @Mock
-    private ClientFactory clientFactory;
-
-    @Mock
-    private App2ZusePort msgClient;
-
-    @Mock
-    private TNVZServicePort tnvzClient;
-
-    @Mock
-    private Mzs2MsgConverter converter;
-
-    @Mock
-    private MoaSPSSSignatureVerifier verifier;
-
-
-    private DeliveryPipeline pipeline;
-
-    @Before
-    public void setup() {
-        pipeline = new SameThreadDeliveryPipeline(repository, tnvzHelper, converter, clientFactory, verifier);
-    }
-
-    @Test
-    public void executePipelineWithoutTnvzRequest() {
-        var appDeliveryId = "no-tnvz-request";
-        var expectedStatus = setupMocks(appDeliveryId, false);
-
-        pipeline.processRequest(appDeliveryId);
-
-        verifyZeroInteractions(tnvzHelper);
-        var actualStatus = repository.getDeliveryRequestStatus(appDeliveryId).get();
-
-        assertThat(actualStatus).isEqualTo(expectedStatus);
-    }
-
-    @Test
-    public void rejectDeliveryWhenReceiverIsNotAddressable() {
-        var appDeliveryId = "not-addressable";
-        setupMocks(appDeliveryId, true);
-        when(tnvzHelper.performQueryPersonRequest(any(), any(), any()))
-                .thenThrow(buildMzsException("400"));
-
-        pipeline.processRequest(appDeliveryId);
-        var actualCode = repository.getDeliveryRequestStatus(appDeliveryId).get()
-                .getError().getErrorInfo().getCode();
-
-        verify(tnvzHelper).performQueryPersonRequest(any(), any(), any());
-        assertThat(actualCode).isEqualTo("400");
-    }
-
-    @Test
-    public void executePipelineWithTnvzRequest() {
-        var appDeliveryId = "tnvz-request";
-        var expectedStatus = setupMocks(appDeliveryId, true);
-
-        pipeline.processRequest(appDeliveryId);
-        var actualStatus = repository.getDeliveryRequestStatus(appDeliveryId).get();
-
-        assertThat(actualStatus).isEqualTo(expectedStatus);
-    }
-
-    @Test
-    public void rejectInvalidSignature() {
-        var appDeliveryId = "invalid-signature";
-        setupMocks(appDeliveryId, true);
-        doThrow(moaZSException("Signature Invalid!")).when(verifier).verify(any());
-
-        pipeline.processRequest(appDeliveryId);
-        var actualCode = repository.getDeliveryRequestStatus(appDeliveryId).get()
-                .getError().getErrorInfo().getCode();
-
-        assertThat(actualCode).isEqualTo(MoaZSException.ERROR_MOASP_SIGNATURE_INVALID);
-
-    }
-
-    private DeliveryRequestStatusType setupMocks(String appDeliveryId, boolean tnvzRequest) {
-
-        var mzsRequest = setupMzsRequest(appDeliveryId, tnvzRequest);
-        var msgRequest = setupMsgRequest(appDeliveryId);
-        var status = setupStatus(appDeliveryId);
-        var signedStatus = new byte[0];
-        var identification = IdentificationType.identificationTypeBuilder().build();
-        repository.add(mzsRequest);
-        repository.addSignedDeliveryRequestStatus(signedStatus, appDeliveryId);
-
-        when(converter.convert(eq(mzsRequest)       )).thenReturn(msgRequest);
-        when(converter.convert(eq(mzsRequest), any())).thenReturn(msgRequest);
-        when(clientFactory.create(any(), same(App2ZusePort.class))).thenReturn(msgClient);
-        when(msgClient.delivery(msgRequest)).thenReturn(status);
-
-        if (tnvzRequest) {
-            when(clientFactory.create(any(), same(TNVZServicePort.class))).thenReturn(tnvzClient);
-            when(tnvzHelper.performQueryPersonRequest(any(), any(), any())).thenReturn(identification);
-        }
-        return status;
-
-    }
-
-    private MoaZSException buildMzsException(String code) {
-        return moaZSExceptionBuilder("").withErrorCode(code).build();
-    }
-
-    private DeliveryRequestStatusType setupStatus(String appDeliveryId) {
-
-        var success = successBuilder()
-                .withAppDeliveryID(appDeliveryId)
-                .build();
-
-        return deliveryRequestStatusTypeBuilder()
-                .withSuccess(success)
-                .build();
-
-    }
-
-    private DeliveryRequestType setupMzsRequest(String appDeliveryId, boolean tnvzRequest) {
-
-        var msgClient = clientTypeBuilder()
-                .withURL("http://zuse")
-                .build();
-
-        var config = configTypeBuilder()
-                .withPerformQueryPersonRequest(tnvzRequest)
-                .withMSGClient(msgClient)
-                .build();
-
-        return deliveryRequestTypeBuilder()
-                .withMetaData(setupMetaData(appDeliveryId))
-                .withConfig(config)
-                .build();
-    }
-
-    private at.gv.zustellung.msg.xsd.DeliveryRequestType setupMsgRequest(String appDeliveryId) {
-        return at.gv.zustellung.msg.xsd.DeliveryRequestType.deliveryRequestTypeBuilder()
-                .withMetaData(setupMetaData(appDeliveryId))
-                .build();
-    }
-
-    private MetaData setupMetaData(String appDeliveryId) {
-        return metaDataBuilder()
-                .withAppDeliveryID(appDeliveryId)
-                .build();
-    }
-
-}
diff --git a/src/test/java/at/gv/egiz/moazs/SingleThreadedDeliveryPipelineTest.java b/src/test/java/at/gv/egiz/moazs/SingleThreadedDeliveryPipelineTest.java
new file mode 100644
index 0000000..c08d567
--- /dev/null
+++ b/src/test/java/at/gv/egiz/moazs/SingleThreadedDeliveryPipelineTest.java
@@ -0,0 +1,188 @@
+package at.gv.egiz.moazs;
+
+import at.gv.egiz.moazs.util.ClientFactory;
+import at.gv.egiz.moazs.pipeline.DeliveryPipeline;
+import at.gv.egiz.moazs.pipeline.SingleThreadedDeliveryPipeline;
+import at.gv.egiz.moazs.repository.DeliveryRepository;
+import at.gv.egiz.moazs.repository.InMemoryDeliveryRepository;
+import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;
+import at.gv.egiz.moazs.scheme.TnvzHelper;
+import at.gv.egiz.moazs.verify.MoaSPSSSignatureVerifier;
+import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
+import at.gv.zustellung.msg.xsd.App2ZusePort;
+import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType;
+import at.gv.zustellung.msg.xsd.MetaData;
+import at.gv.zustellung.msg.xsd.persondata.IdentificationType;
+import at.gv.zustellung.tnvz.xsd.TNVZServicePort;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSException;
+import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
+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.DeliveryRequestType.deliveryRequestTypeBuilder;
+import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.Success.successBuilder;
+import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.deliveryRequestStatusTypeBuilder;
+import static at.gv.zustellung.msg.xsd.MetaData.metaDataBuilder;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SingleThreadedDeliveryPipelineTest {
+
+    private DeliveryRepository repository = new InMemoryDeliveryRepository();
+
+    @Mock
+    private TnvzHelper tnvzHelper;
+
+    @Mock
+    private ClientFactory clientFactory;
+
+    @Mock
+    private App2ZusePort msgClient;
+
+    @Mock
+    private TNVZServicePort tnvzClient;
+
+    @Mock
+    private Mzs2MsgConverter converter;
+
+    @Mock
+    private MoaSPSSSignatureVerifier verifier;
+
+
+    private DeliveryPipeline pipeline;
+
+    @Before
+    public void setup() {
+        pipeline = new SingleThreadedDeliveryPipeline(repository, tnvzHelper, converter, clientFactory, verifier);
+    }
+
+    @Test
+    public void executePipelineWithoutTnvzRequest() {
+        var appDeliveryId = "no-tnvz-request";
+        var expectedStatus = setupMocks(appDeliveryId, false);
+
+        pipeline.processRequest(appDeliveryId);
+
+        verifyZeroInteractions(tnvzHelper);
+        var actualStatus = repository.getDeliveryRequestStatus(appDeliveryId).get();
+
+        assertThat(actualStatus).isEqualTo(expectedStatus);
+    }
+
+    @Test
+    public void rejectDeliveryWhenReceiverIsNotAddressable() {
+        var appDeliveryId = "not-addressable";
+        setupMocks(appDeliveryId, true);
+        when(tnvzHelper.performQueryPersonRequest(any(), any(), any()))
+                .thenThrow(buildMzsException("400"));
+
+        pipeline.processRequest(appDeliveryId);
+        var actualCode = repository.getDeliveryRequestStatus(appDeliveryId).get()
+                .getError().getErrorInfo().getCode();
+
+        verify(tnvzHelper).performQueryPersonRequest(any(), any(), any());
+        assertThat(actualCode).isEqualTo("400");
+    }
+
+    @Test
+    public void executePipelineWithTnvzRequest() {
+        var appDeliveryId = "tnvz-request";
+        var expectedStatus = setupMocks(appDeliveryId, true);
+
+        pipeline.processRequest(appDeliveryId);
+        var actualStatus = repository.getDeliveryRequestStatus(appDeliveryId).get();
+
+        assertThat(actualStatus).isEqualTo(expectedStatus);
+    }
+
+    @Test
+    public void rejectInvalidSignature() {
+        var appDeliveryId = "invalid-signature";
+        setupMocks(appDeliveryId, true);
+        doThrow(moaZSException("Signature Invalid!")).when(verifier).verify(any());
+
+        pipeline.processRequest(appDeliveryId);
+        var actualCode = repository.getDeliveryRequestStatus(appDeliveryId).get()
+                .getError().getErrorInfo().getCode();
+
+        assertThat(actualCode).isEqualTo(MoaZSException.ERROR_MOASP_SIGNATURE_INVALID);
+
+    }
+
+    private DeliveryRequestStatusType setupMocks(String appDeliveryId, boolean tnvzRequest) {
+
+        var mzsRequest = setupMzsRequest(appDeliveryId, tnvzRequest);
+        var msgRequest = setupMsgRequest(appDeliveryId);
+        var status = setupStatus(appDeliveryId);
+        var signedStatus = new byte[0];
+        var identification = IdentificationType.identificationTypeBuilder().build();
+        repository.add(mzsRequest);
+        repository.addSignedDeliveryRequestStatus(signedStatus, appDeliveryId);
+
+        when(converter.convert(eq(mzsRequest)       )).thenReturn(msgRequest);
+        when(converter.convert(eq(mzsRequest), any())).thenReturn(msgRequest);
+        when(clientFactory.create(any(), same(App2ZusePort.class))).thenReturn(msgClient);
+        when(msgClient.delivery(msgRequest)).thenReturn(status);
+
+        if (tnvzRequest) {
+            when(clientFactory.create(any(), same(TNVZServicePort.class))).thenReturn(tnvzClient);
+            when(tnvzHelper.performQueryPersonRequest(any(), any(), any())).thenReturn(identification);
+        }
+        return status;
+
+    }
+
+    private MoaZSException buildMzsException(String code) {
+        return moaZSExceptionBuilder("").withErrorCode(code).build();
+    }
+
+    private DeliveryRequestStatusType setupStatus(String appDeliveryId) {
+
+        var success = successBuilder()
+                .withAppDeliveryID(appDeliveryId)
+                .build();
+
+        return deliveryRequestStatusTypeBuilder()
+                .withSuccess(success)
+                .build();
+
+    }
+
+    private DeliveryRequestType setupMzsRequest(String appDeliveryId, boolean tnvzRequest) {
+
+        var msgClient = clientTypeBuilder()
+                .withURL("http://zuse")
+                .build();
+
+        var config = configTypeBuilder()
+                .withPerformQueryPersonRequest(tnvzRequest)
+                .withMSGClient(msgClient)
+                .build();
+
+        return deliveryRequestTypeBuilder()
+                .withMetaData(setupMetaData(appDeliveryId))
+                .withConfig(config)
+                .build();
+    }
+
+    private at.gv.zustellung.msg.xsd.DeliveryRequestType setupMsgRequest(String appDeliveryId) {
+        return at.gv.zustellung.msg.xsd.DeliveryRequestType.deliveryRequestTypeBuilder()
+                .withMetaData(setupMetaData(appDeliveryId))
+                .build();
+    }
+
+    private MetaData setupMetaData(String appDeliveryId) {
+        return metaDataBuilder()
+                .withAppDeliveryID(appDeliveryId)
+                .build();
+    }
+
+}
diff --git a/src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java b/src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java
index 4425297..66873d4 100644
--- a/src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java
+++ b/src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java
@@ -2,7 +2,7 @@ package at.gv.egiz.moazs;
 
 
 import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;
-import at.gv.egiz.moazs.tnvz.TnvzHelper;
+import at.gv.egiz.moazs.scheme.TnvzHelper;
 import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
 import at.gv.zustellung.app2mzs.xsd.persondata.IdentificationType;
 import at.gv.zustellung.tnvz.xsd.MimeTypeList;
-- 
cgit v1.2.3