From 9556dc6bd7e327dfbfc3c2d5228ad920ad7b9f8e Mon Sep 17 00:00:00 2001 From: Christof Rabensteiner Date: Thu, 27 Jun 2019 14:41:51 +0200 Subject: Inject Configurable HTTP Policies to MsgClient - Refactor: Get rid of MsgClient, because MsgClientFactory can do all the work. - Add Connection Timeout and Request Timeout (Policies of HTTP Client) to mzs:DeliveryRequest/Config and application.yaml. - Update readme: Add JDK 12 Requirement --- readme.md | 4 +- src/main/java/at/gv/egiz/moazs/msg/MsgClient.java | 80 ---------------------- .../at/gv/egiz/moazs/msg/MsgClientFactory.java | 35 ++++++++-- .../moazs/pipeline/SameThreadDeliveryPipeline.java | 7 +- .../at/gv/egiz/moazs/preprocess/ConfigUtil.java | 54 +++++++++++---- src/main/resources/application.yaml | 11 ++- src/main/resources/mzs/app2mzs.xsd | 2 + src/test/java/at/gv/egiz/moazs/MsgClientTest.java | 10 +-- .../egiz/moazs/SameThreadDeliveryPipelineTest.java | 12 ++-- 9 files changed, 94 insertions(+), 121 deletions(-) delete mode 100644 src/main/java/at/gv/egiz/moazs/msg/MsgClient.java diff --git a/readme.md b/readme.md index 606dcf0..7572af1 100644 --- a/readme.md +++ b/readme.md @@ -53,8 +53,10 @@ cp /eaaf_modules/eaaf_module_moa-sig/rep ## Compile & Test -Requirements: openJDK 11 +Requirements: openJDK 12 (OpenJDK 11 has an unfixed bug [1] that prevents the completion of TLS handshakes.) ``` mvn test ``` + +[1] https://bugs.openjdk.java.net/browse/JDK-8214098 \ No newline at end of file diff --git a/src/main/java/at/gv/egiz/moazs/msg/MsgClient.java b/src/main/java/at/gv/egiz/moazs/msg/MsgClient.java deleted file mode 100644 index d834eff..0000000 --- a/src/main/java/at/gv/egiz/moazs/msg/MsgClient.java +++ /dev/null @@ -1,80 +0,0 @@ -package at.gv.egiz.moazs.msg; - -import at.gv.zustellung.msg.xsd.App2ZusePort; -import at.gv.zustellung.msg.xsd.App2ZusePortService; -import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType; -import at.gv.zustellung.msg.xsd.DeliveryRequestType; -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.message.Message; -import org.apache.cxf.phase.PhaseInterceptor; -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.lang.Nullable; - -import javax.net.ssl.SSLContext; - -//TODO: Rethink design. could this entire class be replaced? -// Because everything the send() method does could be initialized in -// the MsgClientFactory as well. -public class MsgClient { - - private static final Logger log = LoggerFactory.getLogger(MsgClient.class); - - private final PhaseInterceptor interceptor; - - private final String address; - - //TODO: make configurable - private final int connectionTimeout = 0; - private final int receiveTimeout = 0; - - @Nullable - private final SSLContext sslContext; - - public MsgClient(PhaseInterceptor interceptor, - String address, - @Nullable SSLContext sslContext) { - this.interceptor = interceptor; - this.address = address; - this.sslContext = sslContext; - } - - /** - * Send {@code msgRequest} to {@code Config/Server/ZUSEUrlID} and run {@code interceptor} on response. - * @return - */ - public DeliveryRequestStatusType send(DeliveryRequestType msgRequest) { - - var factory = new JaxWsClientFactoryBean(); - - factory.setServiceClass(App2ZusePort.class); - factory.setAddress(address); - factory.getInInterceptors().add(interceptor); - - var proxy = new JaxWsProxyFactoryBean(factory).create(); - - Client client = ClientProxy.getClient(proxy); - HTTPConduit http = (HTTPConduit) client.getConduit(); - - var httpClientPolicy = new HTTPClientPolicy(); - httpClientPolicy.setConnectionTimeout(connectionTimeout); - httpClientPolicy.setReceiveTimeout(receiveTimeout); - http.setClient(httpClientPolicy); - - if (sslContext != null) { - var tlsParams = new TLSClientParameters(); - tlsParams.setSSLSocketFactory(sslContext.getSocketFactory()); - http.setTlsClientParameters(tlsParams); - log.info("SSLContext initialized. "); - } - - return ((App2ZusePort)proxy).delivery(msgRequest); - } - -} diff --git a/src/main/java/at/gv/egiz/moazs/msg/MsgClientFactory.java b/src/main/java/at/gv/egiz/moazs/msg/MsgClientFactory.java index 071a243..c40aec1 100644 --- a/src/main/java/at/gv/egiz/moazs/msg/MsgClientFactory.java +++ b/src/main/java/at/gv/egiz/moazs/msg/MsgClientFactory.java @@ -4,7 +4,15 @@ 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.msg.xsd.App2ZusePort; import com.sun.istack.Nullable; +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; @@ -23,7 +31,6 @@ public class MsgClientFactory { private final SSLContextCreator sslContextCreator; private final FileUtils fileUtils; - @Autowired public MsgClientFactory(StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor, SSLContextCreator creator, FileUtils fileUtils) { this.storeResponseInterceptor = storeResponseInterceptor; @@ -31,7 +38,6 @@ public class MsgClientFactory { this.fileUtils = fileUtils; } - /** * Creates a client that communicates with a msg service. * @@ -39,17 +45,33 @@ public class MsgClientFactory { * @return the msg client */ //TODO evaluate and honor laxhostnameverification and trustall parameter! - public MsgClient create(ClientType params) { + public App2ZusePort create(ClientType params) { + + var factory = new JaxWsClientFactoryBean(); + factory.setServiceClass(App2ZusePort.class); + factory.setAddress(params.getURL()); + factory.getInInterceptors().add(storeResponseInterceptor); - SSLContext sslContext = null; + var proxy = new JaxWsProxyFactoryBean(factory).create(); + Client client = ClientProxy.getClient(proxy); + HTTPConduit http = (HTTPConduit) client.getConduit(); + + var httpClientPolicy = new HTTPClientPolicy(); + httpClientPolicy.setConnectionTimeout(params.getConnectionTimeout().longValueExact()); + httpClientPolicy.setReceiveTimeout(params.getReceiveTimeout().longValueExact()); + http.setClient(httpClientPolicy); if (params.getURL().startsWith("https")) { var keystore = resolveKeyStorePath(params.getSSL().getKeyStore()); var truststore = resolveKeyStorePath(params.getSSL().getTrustStore()); - sslContext = sslContextCreator.createSSLContext(keystore, truststore); + SSLContext sslContext = sslContextCreator.createSSLContext(keystore, truststore); + var tlsParams = new TLSClientParameters(); + tlsParams.setSSLSocketFactory(sslContext.getSocketFactory()); + http.setTlsClientParameters(tlsParams); + log.info("SSLContext initialized. "); } - return new MsgClient(storeResponseInterceptor, params.getURL(), sslContext); + return ((App2ZusePort)proxy); } private KeyStoreType resolveKeyStorePath(@Nullable KeyStoreType store) { @@ -57,7 +79,6 @@ public class MsgClientFactory { 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) diff --git a/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java b/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java index 20320c4..2e0433f 100644 --- a/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java +++ b/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java @@ -4,7 +4,6 @@ package at.gv.egiz.moazs.pipeline; import at.gv.egiz.moazs.MoaZSException; import at.gv.egiz.moazs.msg.MsgClientFactory; import at.gv.egiz.moazs.verify.SignatureVerifier; -import at.gv.egiz.moazs.msg.StoreSOAPBodyBinaryInRepositoryInterceptor; import at.gv.egiz.moazs.repository.DeliveryRepository; import at.gv.egiz.moazs.scheme.Mzs2MsgConverter; import at.gv.egiz.moazs.scheme.NameSpace; @@ -38,14 +37,12 @@ public class SameThreadDeliveryPipeline implements DeliveryPipeline { private final Mzs2MsgConverter converter; private final MsgClientFactory msgClientFactory; private final SignatureVerifier verifier; - private final StoreSOAPBodyBinaryInRepositoryInterceptor interceptor; @Autowired public SameThreadDeliveryPipeline(DeliveryRepository repository, TnvzClient tnvzClient, TnvzResultVerifier tnvzVerifier, Mzs2MsgConverter converter, - StoreSOAPBodyBinaryInRepositoryInterceptor interceptor, MsgClientFactory msgClientFactory, SignatureVerifier verifier ) { @@ -55,7 +52,6 @@ public class SameThreadDeliveryPipeline implements DeliveryPipeline { this.converter = converter; this.msgClientFactory = msgClientFactory; this.verifier = verifier; - this.interceptor = interceptor; } @Override @@ -77,10 +73,9 @@ public class SameThreadDeliveryPipeline implements DeliveryPipeline { } else { msgRequest = converter.convert(mzsRequest); } - exceptionBuilder.withMsgRequest(msgRequest); - var status = msgClientFactory.create(mzsRequest.getConfig().getMSGClient()).send(msgRequest); + var status = msgClientFactory.create(mzsRequest.getConfig().getMSGClient()).delivery(msgRequest); exceptionBuilder.withMsgResult(status); verifySignedStatus(appDeliveryId, exceptionBuilder); 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 aa86873..2392ce0 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java @@ -8,6 +8,7 @@ import at.gv.zustellung.app2mzs.xsd.SSLType; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; +import java.math.BigInteger; import java.util.Map; import static at.gv.zustellung.app2mzs.xsd.ClientType.clientTypeBuilder; @@ -31,6 +32,8 @@ public class ConfigUtil { public static final String FILENAME_KEY = "filename"; public static final String FILETYPE_KEY = "filetype"; public static final String PASSWORD_KEY = "password"; + public static final String RECEIVE_TIMEOUT = "receive-timeout"; + public static final String CONNECTION_TIMEOUT_KEY = "connection-timeout"; /** @@ -69,11 +72,24 @@ public class ConfigUtil { var url = clientParams.get(URL_KEY); + BigInteger connectionTimeout = clientParams.containsKey(CONNECTION_TIMEOUT_KEY) + ? new BigInteger(clientParams.get(CONNECTION_TIMEOUT_KEY)) + : null; + + BigInteger receiveTimeout = clientParams.containsKey(RECEIVE_TIMEOUT) + ? new BigInteger(clientParams.get(RECEIVE_TIMEOUT)) + : null; + var sslParams = filterMapByPrefix(clientParams, SSL_KEY); SSLType ssl = sslParams.isEmpty() ? null : buildSSL(sslParams); - return clientTypeBuilder().withURL(url).withSSL(ssl).build(); + return clientTypeBuilder() + .withURL(url) + .withSSL(ssl) + .withConnectionTimeout(connectionTimeout) + .withReceiveTimeout(receiveTimeout) + .build(); } @@ -153,6 +169,14 @@ public class ConfigUtil { builder.withSSL(merge(primary.getSSL(), fallback.getSSL())); } + if (primary.getConnectionTimeout() != null) { + builder.withConnectionTimeout(primary.getConnectionTimeout()); + } + + if (primary.getReceiveTimeout() != null) { + builder.withConnectionTimeout(primary.getReceiveTimeout()); + } + return builder.build(); } @@ -207,24 +231,28 @@ public class ConfigUtil { private boolean isTVNZClientConfigured(ClientType tnvzClient, Boolean isPerformQueryPersonRequest) { return (tnvzClient != null && tnvzClient.getURL() != null + && tnvzClient.getReceiveTimeout() != null + && tnvzClient.getConnectionTimeout() != null && isSSLConfigured(tnvzClient)) || isPerformQueryPersonRequest == false; } - private boolean isMSGClientConfigured(ClientType msgClient) { - return msgClient != null - && msgClient.getURL() != null - && isSSLConfigured(msgClient); + private boolean isMSGClientConfigured(ClientType msgClientParams) { + return msgClientParams != null + && msgClientParams.getURL() != null + && isSSLConfigured(msgClientParams) + && msgClientParams.getReceiveTimeout() != null + && msgClientParams.getConnectionTimeout() != null; } - private boolean isSSLConfigured(ClientType params) { - return (params.getURL().startsWith("https") - && params.getSSL() != null - && params.getSSL().isTrustAll() != null - && params.getSSL().isLaxHostNameVerification() != null - && isKeyStoreConfigured(params.getSSL().getKeyStore()) - && isKeyStoreConfigured(params.getSSL().getTrustStore())) - || !params.getURL().startsWith("https"); + 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()) + && isKeyStoreConfigured(clientParams.getSSL().getTrustStore())) + || !clientParams.getURL().startsWith("https"); } private boolean isKeyStoreConfigured(KeyStoreType keyStore) { diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 2d376a8..1903067 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -9,12 +9,19 @@ spring: # Configure parameters here or in DeliveryRequest/Config. # Choose a profile in DeliveryRequest/Config/ProfileId. -# If a parameter is missing, moa zs falls back to default +# If a parameter is missing, moa zs falls back to "default" # Order: DeliveryRequest/Config > [chosen-profile] > default delivery-request-configuration-profiles: default: perform-query-person-request: false - msg-client.url: http://localhost:8081/services/DeliveryRequest + msg-client: + url: http://localhost:8081/services/DeliveryRequest + # Time in ms after which a connection will be closed. + # 0 means indefinitely. + connection-timeout: 0 + # Time in ms that the client waits after having sent the request. + # 0 means indefinitely. + receive-timeout: 0 ssl-profile: perform-query-person-request: false diff --git a/src/main/resources/mzs/app2mzs.xsd b/src/main/resources/mzs/app2mzs.xsd index 956cd31..d99b248 100644 --- a/src/main/resources/mzs/app2mzs.xsd +++ b/src/main/resources/mzs/app2mzs.xsd @@ -92,6 +92,8 @@ + + diff --git a/src/test/java/at/gv/egiz/moazs/MsgClientTest.java b/src/test/java/at/gv/egiz/moazs/MsgClientTest.java index 294b2b8..7c9bf7d 100644 --- a/src/test/java/at/gv/egiz/moazs/MsgClientTest.java +++ b/src/test/java/at/gv/egiz/moazs/MsgClientTest.java @@ -1,6 +1,5 @@ package at.gv.egiz.moazs; -import at.gv.egiz.moazs.msg.MsgClient; import at.gv.egiz.moazs.msg.MsgClientFactory; import at.gv.egiz.moazs.msg.StoreSOAPBodyBinaryInRepositoryInterceptor; import at.gv.egiz.moazs.scheme.Marshaller; @@ -19,6 +18,7 @@ import javax.xml.bind.JAXBElement; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; +import java.math.BigInteger; import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.SSLType.SSLTypeBuilder; @@ -28,7 +28,7 @@ import static at.gv.zustellung.app2mzs.xsd.SSLType.SSLTypeBuilder; public class MsgClientTest { - private final static Logger log = LoggerFactory.getLogger(MsgClient.class); + private final static Logger log = LoggerFactory.getLogger(MsgClientTest.class); private final String basePath = "src/test/resources/at/gv/egiz/moazs/MsgClientTest/"; @Autowired @@ -54,7 +54,7 @@ public class MsgClientTest { var client = factory.create(clientParams); try{ - var status = client.send(request); + var status = client.delivery(request); log.info("status: " + msgMarshaller.marshallXml(OF.createDeliveryResponse(status))); } catch (Exception ex) { System.out.println(ex.getMessage()); @@ -69,7 +69,7 @@ public class MsgClientTest { var clientParams = generateSSLClientParams(sslServiceUri); var client = factory.create(clientParams); - var status = client.send(request); + var status = client.delivery(request); log.info("status: " + msgMarshaller.marshallXml(OF.createDeliveryRequestStatus(status))); } @@ -98,6 +98,8 @@ public class MsgClientTest { return ClientType.clientTypeBuilder() .withURL(sslServiceUri) .withSSL(sslParams) + .withReceiveTimeout(BigInteger.ZERO) + .withConnectionTimeout(BigInteger.ZERO) .build(); } diff --git a/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java b/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java index df54ef7..e685816 100644 --- a/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java +++ b/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java @@ -1,8 +1,6 @@ package at.gv.egiz.moazs; -import at.gv.egiz.moazs.msg.MsgClient; import at.gv.egiz.moazs.msg.MsgClientFactory; -import at.gv.egiz.moazs.msg.StoreSOAPBodyBinaryInRepositoryInterceptor; import at.gv.egiz.moazs.pipeline.DeliveryPipeline; import at.gv.egiz.moazs.pipeline.SameThreadDeliveryPipeline; import at.gv.egiz.moazs.repository.DeliveryRepository; @@ -12,6 +10,7 @@ import at.gv.egiz.moazs.tnvz.TnvzClient; import at.gv.egiz.moazs.tnvz.TnvzResultVerifier; 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.tnvz.xsd.MimeTypeList; @@ -55,14 +54,11 @@ public class SameThreadDeliveryPipelineTest { private MsgClientFactory msgClientFactory; @Mock - private MsgClient msgClient; + private App2ZusePort msgClient; @Mock private Mzs2MsgConverter converter; - @Mock - private StoreSOAPBodyBinaryInRepositoryInterceptor interceptor; - @Mock private MoaSPSSSignatureVerifier verifier; @@ -72,7 +68,7 @@ public class SameThreadDeliveryPipelineTest { @Before public void setup() { pipeline = new SameThreadDeliveryPipeline(repository, tnvzClient, new TnvzResultVerifier(), - converter,interceptor, msgClientFactory, verifier); + converter, msgClientFactory, verifier); } @Test @@ -174,7 +170,7 @@ public class SameThreadDeliveryPipelineTest { when(converter.convert(eq(mzsRequest) )).thenReturn(msgRequest); when(converter.convert(eq(mzsRequest), any())).thenReturn(msgRequest); when(msgClientFactory.create(any())).thenReturn(msgClient); - when(msgClient.send(msgRequest)).thenReturn(status); + when(msgClient.delivery(msgRequest)).thenReturn(status); return status; -- cgit v1.2.3