From a05df692c856279cb5071bf4ff1b48bdf9229117 Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Fri, 18 Oct 2024 18:21:34 +0200 Subject: [PATCH] OZG-6891 Transform runtime exceptions to checked exceptions --- .../de/ozgcloud/xta/client/XtaClient.java | 27 ++- .../ozgcloud/xta/client/XtaClientFactory.java | 8 +- .../xta/client/config/XtaConfigValidator.java | 6 +- .../client/core/WrappedXtaServiceFactory.java | 4 +- .../xta/client/core/XtaClientService.java | 20 +- .../client/core/XtaClientServiceFactory.java | 4 +- .../xta/client/core/XtaExceptionHandler.java | 67 +++++++ .../core/XtaExceptionHandlerFactory.java | 14 ++ .../xta/client/core/XtaPortTripleFactory.java | 12 +- .../core/XtaTLSClientParametersFactory.java | 8 +- ...Exception.java => XtaClientException.java} | 67 +++---- ... => XtaClientInitializationException.java} | 67 ++++--- .../xdomea/XdomeaMetaDataValidator.java | 18 +- .../xdomea/XdomeaXtaMessageCreator.java | 10 +- .../xdomea/reader/XdomeaValueReader.java | 8 +- .../ozgcloud/xta/client/XtaClientITCase.java | 51 ++++- .../xta/client/XtaClientRemoteITCase.java | 2 + .../de/ozgcloud/xta/client/XtaClientTest.java | 86 ++++++++- .../client/config/XtaConfigValidatorTest.java | 18 +- .../core/XtaExceptionHandlerFactoryTest.java | 29 +++ .../client/core/XtaExceptionHandlerTest.java | 176 ++++++++++++++++++ .../XtaTLSClientParametersFactoryTest.java | 4 +- .../ClientRuntimeExceptionTestFactory.java | 33 ++++ .../xdomea/XdomeaMetaDataValidatorTest.java | 8 +- .../xdomea/XdomeaXtaMessageCreatorITCase.java | 6 +- .../xdomea/XdomeaXtaMessageCreatorTest.java | 4 +- .../xdomea/reader/XdomeaValueReaderTest.java | 4 +- 27 files changed, 614 insertions(+), 147 deletions(-) create mode 100644 src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandler.java create mode 100644 src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactory.java rename src/main/java/de/ozgcloud/xta/client/exception/{ClientException.java => XtaClientException.java} (76%) rename src/main/java/de/ozgcloud/xta/client/exception/{ClientInitializationException.java => XtaClientInitializationException.java} (71%) create mode 100644 src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactoryTest.java create mode 100644 src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerTest.java create mode 100644 src/test/java/de/ozgcloud/xta/client/factory/ClientRuntimeExceptionTestFactory.java diff --git a/src/main/java/de/ozgcloud/xta/client/XtaClient.java b/src/main/java/de/ozgcloud/xta/client/XtaClient.java index b11bac9..3a25a8f 100644 --- a/src/main/java/de/ozgcloud/xta/client/XtaClient.java +++ b/src/main/java/de/ozgcloud/xta/client/XtaClient.java @@ -10,8 +10,10 @@ import jakarta.validation.constraints.NotNull; import de.ozgcloud.xta.client.config.XtaClientConfig; import de.ozgcloud.xta.client.core.XtaClientService; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.core.XtaExceptionHandler; import de.ozgcloud.xta.client.exception.ClientRuntimeException; +import de.ozgcloud.xta.client.exception.XtaClientException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import de.ozgcloud.xta.client.model.XtaIdentifier; import de.ozgcloud.xta.client.model.XtaMessage; import de.ozgcloud.xta.client.model.XtaMessageMetaData; @@ -31,14 +33,23 @@ public class XtaClient { private final XtaClientService service; private final XtaClientConfig config; private final FetchMessageParameterFactory fetchMessageParameterFactory; + private final XtaExceptionHandler exceptionHandler; static final String NO_MESSAGE_CLOSED_WARNING = "No message has been closed although more are pending. Try increasing max list items."; - public static XtaClient from(XtaClientConfig config) throws ClientInitializationException { + public static XtaClient from(XtaClientConfig config) throws XtaClientInitializationException { return XtaClientFactory.from(config).create(); } - public List<XtaTransportReport> fetchMessages(@NotNull Consumer<XtaMessage> processMessage) { + public List<XtaTransportReport> fetchMessages(@NotNull Consumer<XtaMessage> processMessage) throws XtaClientException { + try { + return fetchMessagesRaw(processMessage); + } catch (RuntimeException exception) { + throw exceptionHandler.deriveXtaClientException(exception); + } + } + + List<XtaTransportReport> fetchMessagesRaw(Consumer<XtaMessage> processMessage) { return config.getClientIdentifiers().stream() .filter(service::checkAccountActive) .map(clientIdentifier -> fetchMessageParameterFactory.create(clientIdentifier, processMessage)) @@ -125,7 +136,15 @@ public class XtaClient { log.error("Processing of message '%s' failed! Not closing message.".formatted(messageId), exception); } - public XtaTransportReport sendMessage(@Valid XtaMessage messageWithoutMessageId) { + public XtaTransportReport sendMessage(@Valid XtaMessage message) throws XtaClientException { + try { + return sendMessageRaw(message); + } catch (RuntimeException exception) { + throw exceptionHandler.deriveXtaClientException(exception); + } + } + + XtaTransportReport sendMessageRaw(@Valid XtaMessage messageWithoutMessageId) { var metaData = messageWithoutMessageId.metaData(); throwExceptionIfAccountInactive(metaData.authorIdentifier()); throwExceptionIfServiceNotAvailable(metaData); diff --git a/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java b/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java index 1a38191..81d0fa5 100644 --- a/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java @@ -3,7 +3,8 @@ package de.ozgcloud.xta.client; import de.ozgcloud.xta.client.config.XtaClientConfig; import de.ozgcloud.xta.client.config.XtaConfigValidator; import de.ozgcloud.xta.client.core.XtaClientServiceFactory; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.core.XtaExceptionHandlerFactory; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.Builder; import lombok.RequiredArgsConstructor; @@ -15,6 +16,7 @@ public class XtaClientFactory { private final XtaClientServiceFactory xtaClientServiceFactory; private final XtaClientConfig config; private final FetchMessageParameterFactory fetchMessageParameterFactory; + private final XtaExceptionHandlerFactory exceptionHandlerFactory; public static XtaClientFactory from(final XtaClientConfig config) { return XtaClientFactory.builder() @@ -22,15 +24,17 @@ public class XtaClientFactory { .configValidator(XtaConfigValidator.builder().build()) .xtaClientServiceFactory(XtaClientServiceFactory.from(config)) .fetchMessageParameterFactory(FetchMessageParameterFactory.builder().build()) + .exceptionHandlerFactory(XtaExceptionHandlerFactory.builder().build()) .build(); } - public XtaClient create() throws ClientInitializationException { + public XtaClient create() throws XtaClientInitializationException { configValidator.validate(config); return XtaClient.builder() .config(config) .service(xtaClientServiceFactory.create()) .fetchMessageParameterFactory(fetchMessageParameterFactory) + .exceptionHandler(exceptionHandlerFactory.create()) .build(); } } diff --git a/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java b/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java index 9582164..f0fd961 100644 --- a/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java +++ b/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java @@ -7,7 +7,7 @@ import java.util.stream.Collectors; import jakarta.validation.Validation; import jakarta.validation.Validator; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.Builder; import lombok.RequiredArgsConstructor; @@ -22,10 +22,10 @@ public class XtaConfigValidator { } } - public void validate(final XtaClientConfig config) throws ClientInitializationException { + public void validate(final XtaClientConfig config) throws XtaClientInitializationException { var violations = VALIDATOR.validate(config); if (!violations.isEmpty()) { - throw new ClientInitializationException("Client configuration is invalid:\n" + violations.stream() + throw new XtaClientInitializationException("Client configuration is invalid:\n" + violations.stream() .map(v -> "'%s' %s".formatted(v.getPropertyPath().toString(), v.getMessage())) .collect(Collectors.joining("\n"))); } diff --git a/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java b/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java index 6a909b5..9d89e96 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java @@ -3,7 +3,7 @@ package de.ozgcloud.xta.client.core; import org.mapstruct.factory.Mappers; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import de.ozgcloud.xta.client.mapper.RequestMapper; import de.ozgcloud.xta.client.mapper.ResponseMapper; import lombok.Builder; @@ -25,7 +25,7 @@ public class WrappedXtaServiceFactory { .build(); } - public WrappedXtaService create() throws ClientInitializationException { + public WrappedXtaService create() throws XtaClientInitializationException { return WrappedXtaService.builder() .ports(xtaPortTripleFactory.create()) .requestMapper(requestMapper) diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaClientService.java b/src/main/java/de/ozgcloud/xta/client/core/XtaClientService.java index 3c1aacc..6dc98c8 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/XtaClientService.java +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaClientService.java @@ -28,11 +28,13 @@ public class XtaClientService { private final WrappedXtaService service; private final XtaClientConfig config; + static final String TRANSPORT_REPORT_FAILED_ERROR = "Failed to get transport report!"; + public Optional<XtaTransportReport> getTransportReport(XtaMessageMetaData messageMetaData) { try { return Optional.of(getTransportReportOrThrowException(messageMetaData)); } catch (ClientRuntimeException e) { - logError("Failed to get transport report!", e); + logError(TRANSPORT_REPORT_FAILED_ERROR, e); return Optional.empty(); } } @@ -42,7 +44,7 @@ public class XtaClientService { var readerClientIdentifier = message.metaData().readerIdentifier(); try { service.close(messageId, readerClientIdentifier); - } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException e) { + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException | RuntimeException e) { logError("Failed to close message! '%s' (reader: %s)".formatted(messageId, readerClientIdentifier), e); } } @@ -52,7 +54,7 @@ public class XtaClientService { var readerClientIdentifier = messageMetaData.readerIdentifier(); try { return Optional.of(service.getMessage(messageId, readerClientIdentifier)); - } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException e) { + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException | RuntimeException e) { logError("Failed to get message by id ! '%s' (reader: %s)".formatted(messageId, readerClientIdentifier.value()), e); return Optional.empty(); } @@ -61,7 +63,7 @@ public class XtaClientService { public Optional<XtaMessageMetaDataListing> getStatusList(XtaIdentifier clientIdentifier) { try { return Optional.of(service.getStatusList(clientIdentifier, config.getMaxListItems())); - } catch (PermissionDeniedException | XTAWSTechnicalProblemException e) { + } catch (PermissionDeniedException | XTAWSTechnicalProblemException | RuntimeException e) { logError("Failed to get status list!", e); return Optional.empty(); } @@ -75,7 +77,7 @@ public class XtaClientService { try { service.checkAccountActive(clientIdentifier); return true; - } catch (XTAWSTechnicalProblemException e) { + } catch (XTAWSTechnicalProblemException | RuntimeException e) { throw new ClientRuntimeException("Failed to check account active!", e); } catch (PermissionDeniedException e) { return false; @@ -88,7 +90,7 @@ public class XtaClientService { service.sendMessage(message); return getTransportReportOrThrowException(message.metaData()); } catch (XTAWSTechnicalProblemException | PermissionDeniedException | SyncAsyncException | ParameterIsNotValidException | - MessageVirusDetectionException | MessageSchemaViolationException e) { + MessageVirusDetectionException | MessageSchemaViolationException | RuntimeException e) { throw new ClientRuntimeException("Failed to send message!", e); } } @@ -98,7 +100,7 @@ public class XtaClientService { try { var messageId = service.createMessageId(authorIdentifier); return createXtaMessageWithMessageId(messageWithoutMessageId, messageId); - } catch (XTAWSTechnicalProblemException | PermissionDeniedException e) { + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | RuntimeException e) { throw new ClientRuntimeException("Failed to create message id!", e); } } @@ -116,7 +118,7 @@ public class XtaClientService { var authorId = messageMetaData.authorIdentifier(); try { return service.getTransportReport(messageId, authorId); - } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException e) { + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException | RuntimeException e) { throw new ClientRuntimeException( "Failed to get transport report! (messageId: %s, reader: %s)".formatted(messageId, authorId.value()), e); } @@ -129,7 +131,7 @@ public class XtaClientService { messageMetaData.readerIdentifier(), messageMetaData.authorIdentifier() ); - } catch (XTAWSTechnicalProblemException | PermissionDeniedException | ParameterIsNotValidException exception) { + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | ParameterIsNotValidException | RuntimeException exception) { logWarning("Failed to lookup service for message '%s'!".formatted(messageMetaData.messageId()), exception); return false; } diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaClientServiceFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaClientServiceFactory.java index 61a95de..0343c50 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/XtaClientServiceFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaClientServiceFactory.java @@ -1,7 +1,7 @@ package de.ozgcloud.xta.client.core; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.Builder; @Builder @@ -17,7 +17,7 @@ public class XtaClientServiceFactory { .build(); } - public XtaClientService create() throws ClientInitializationException { + public XtaClientService create() throws XtaClientInitializationException { return XtaClientService.builder() .config(config) .service(wrappedXtaServiceFactory.create()) diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandler.java b/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandler.java new file mode 100644 index 0000000..1fe3570 --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandler.java @@ -0,0 +1,67 @@ +package de.ozgcloud.xta.client.core; + +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import de.ozgcloud.xta.client.exception.ClientRuntimeException; +import de.ozgcloud.xta.client.exception.XtaClientException; +import genv3.de.xoev.transport.xta.x211.ExceptionType; +import lombok.Builder; +import lombok.RequiredArgsConstructor; + +@Builder +@RequiredArgsConstructor +public class XtaExceptionHandler { + + static final String UNEXPECTED_ERROR_MESSAGE = "An unexpected error occurred. Please report this to the xta-client maintainers."; + + public XtaClientException deriveXtaClientException(RuntimeException exception) { + if (exception instanceof ClientRuntimeException) { + return deriveXtaClientExceptionFromClientRuntimeException((ClientRuntimeException) exception); + } + return new XtaClientException(UNEXPECTED_ERROR_MESSAGE, exception); + } + + XtaClientException deriveXtaClientExceptionFromClientRuntimeException(ClientRuntimeException exception) { + var cause = exception.getCause(); + var detailMessageLines = getDetailLines(cause); + var message = Stream.concat( + Stream.of(exception.getMessage()), + detailMessageLines + ).collect(Collectors.joining("\n")); + return new XtaClientException(message, cause); + } + + Stream<String> getDetailLines(Throwable cause) { + return Optional.ofNullable(cause) + .filter(Exception.class::isInstance) + .map(Exception.class::cast) + .map(this::deriveDetailLinesFromException) + .orElse(Stream.empty()); + } + + Stream<String> deriveDetailLinesFromException(Exception exception) { + return getExceptionType(exception) + .map(ExceptionType::getErrorCode) + .map(num -> Stream.of(exception.getMessage(), "[%s] %s".formatted(num.getCode(), num.getName()))) + .orElse(Stream.empty()); + } + + private Optional<ExceptionType> getExceptionType(Exception exception) { + return getPrivateFieldValue(exception, "faultInfo") + .filter(ExceptionType.class::isInstance) + .map(ExceptionType.class::cast); + } + + private Optional<Object> getPrivateFieldValue(Object object, String fieldName) { + try { + var field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return Optional.of(field.get(object)); + } catch (NoSuchFieldException | IllegalAccessException | RuntimeException e) { + return Optional.empty(); + } + } + +} diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactory.java new file mode 100644 index 0000000..67cb8a8 --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactory.java @@ -0,0 +1,14 @@ +package de.ozgcloud.xta.client.core; + +import lombok.Builder; +import lombok.RequiredArgsConstructor; + +@Builder +@RequiredArgsConstructor +public class XtaExceptionHandlerFactory { + + public XtaExceptionHandler create() { + return XtaExceptionHandler.builder() + .build(); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaPortTripleFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaPortTripleFactory.java index 3da0a62..908dc1e 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/XtaPortTripleFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaPortTripleFactory.java @@ -14,7 +14,7 @@ import org.apache.cxf.message.Message; import org.apache.cxf.transport.http.HTTPConduit; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import genv3.de.xoev.transport.xta.x211.XTAService; import lombok.Builder; import lombok.RequiredArgsConstructor; @@ -41,7 +41,7 @@ public class XtaPortTripleFactory { .build(); } - public XtaPortTriple create() throws ClientInitializationException { + public XtaPortTriple create() throws XtaClientInitializationException { log.debug("[createXtaService] Using config: {}", config); return new XtaPortTriple( configurePort(config.getManagementServiceUrl(), xtaService.getManagementPort()), @@ -50,13 +50,13 @@ public class XtaPortTripleFactory { ); } - private <T> T configurePort(final String endpointUrl, final T port) throws ClientInitializationException { + private <T> T configurePort(final String endpointUrl, final T port) throws XtaClientInitializationException { var bindingProvider = (BindingProvider) port; configureBinding(endpointUrl, bindingProvider); return port; } - void configureBinding(final String endpointUrl, final BindingProvider port) throws ClientInitializationException { + void configureBinding(final String endpointUrl, final BindingProvider port) throws XtaClientInitializationException { configureRequestContext(endpointUrl, port.getRequestContext()); configureClient(getClientFromPort(port)); } @@ -74,7 +74,7 @@ public class XtaPortTripleFactory { )); } - void configureClient(Client client) throws ClientInitializationException { + void configureClient(Client client) throws XtaClientInitializationException { if (config.isLogSoapRequests()) { configureOutInterceptors(client.getOutInterceptors()); } @@ -105,7 +105,7 @@ public class XtaPortTripleFactory { return inInterceptor; } - void configureHttpConduit(HTTPConduit conduit) throws ClientInitializationException { + void configureHttpConduit(HTTPConduit conduit) throws XtaClientInitializationException { conduit.setTlsClientParameters(tlsClientParametersFactory.create()); } } diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java index fee35b4..f314f4f 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java @@ -15,7 +15,7 @@ import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.hc.core5.ssl.SSLContextBuilder; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.Builder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -29,7 +29,7 @@ public class XtaTLSClientParametersFactory { private final XtaClientConfig config; - public TLSClientParameters create() throws ClientInitializationException { + public TLSClientParameters create() throws XtaClientInitializationException { log.debug("[createXtaTLsParameters] Using config: {}", config); var sslContext = createXtaSslContext(); @@ -38,7 +38,7 @@ public class XtaTLSClientParametersFactory { return parameters; } - public SSLContext createXtaSslContext() throws ClientInitializationException { + public SSLContext createXtaSslContext() throws XtaClientInitializationException { try { var sslContextBuilder = createSSLContextBuilder(); var clientCertKeystore = config.getClientCertKeystore(); @@ -52,7 +52,7 @@ public class XtaTLSClientParametersFactory { return sslContextBuilder.build(); } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException | IOException | CertificateException e) { - throw new ClientInitializationException("Failed to create SSL context: " + e.getMessage(), e); + throw new XtaClientInitializationException("Failed to create SSL context: " + e.getMessage(), e); } } diff --git a/src/main/java/de/ozgcloud/xta/client/exception/ClientException.java b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientException.java similarity index 76% rename from src/main/java/de/ozgcloud/xta/client/exception/ClientException.java rename to src/main/java/de/ozgcloud/xta/client/exception/XtaClientException.java index 0b89833..e00a6b1 100644 --- a/src/main/java/de/ozgcloud/xta/client/exception/ClientException.java +++ b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientException.java @@ -1,33 +1,34 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.exception; - - -/** - * Allgemeiner Fehler zum signalisieren von clientseitigen Problemen. - */ -public class ClientException extends Exception { - - public ClientException(final String message, final Throwable cause) { - super(message, cause); - } - - public ClientException(final String message) { - super(message); - } -} +/** + * XTA-Client Java Library + * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package de.ozgcloud.xta.client.exception; + + +/** + * A generic exception during xta client method execution. + * + */ +public class XtaClientException extends Exception { + + public XtaClientException(final String message, final Throwable cause) { + super(message, cause); + } + + public XtaClientException(final String message) { + super(message); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/exception/ClientInitializationException.java b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientInitializationException.java similarity index 71% rename from src/main/java/de/ozgcloud/xta/client/exception/ClientInitializationException.java rename to src/main/java/de/ozgcloud/xta/client/exception/XtaClientInitializationException.java index 5a1b2a7..534c435 100644 --- a/src/main/java/de/ozgcloud/xta/client/exception/ClientInitializationException.java +++ b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientInitializationException.java @@ -1,34 +1,33 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.exception; - -/** - * Spezieller Fehler zum signalisieren von clientseitigen Problemen während der Initialisierung. - * - * @author hmaterny - */ -public class ClientInitializationException extends ClientException { - - public ClientInitializationException(final String message, final Throwable cause) { - super(message, cause); - } - - public ClientInitializationException(final String message) { - super(message); - } -} +/** + * XTA-Client Java Library + * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package de.ozgcloud.xta.client.exception; + +/** + * A generic exception during xta client initialization. + * + */ +public class XtaClientInitializationException extends XtaClientException { + + public XtaClientInitializationException(final String message, final Throwable cause) { + super(message, cause); + } + + public XtaClientInitializationException(final String message) { + super(message); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidator.java b/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidator.java index eafbad0..7a5c363 100644 --- a/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidator.java +++ b/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidator.java @@ -5,7 +5,7 @@ import static de.ozgcloud.xta.client.xdomea.mapper.MetadataMapper.*; import java.util.List; import java.util.Set; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.model.XtaFile; import de.ozgcloud.xta.client.model.XtaMessageMetaData; import de.ozgcloud.xta.client.xdomea.reader.XdomeaXmlValues; @@ -26,7 +26,7 @@ public class XdomeaMetaDataValidator { XtaFile xdomeaZipFile, XdomeaXmlValues xdomeaXmlValues, XtaMessageMetaData messageMetadataData - ) throws ClientException { + ) throws XtaClientException { validateIdentifierPrefixes( xdomeaXmlValues.authorIdPrefix(), xdomeaXmlValues.readerIdPrefix() @@ -39,14 +39,14 @@ public class XdomeaMetaDataValidator { validatePrimaryDocumentReferences(xdomeaZipFile, xdomeaXmlValues.primaryDocumentNames()); } - void validateIdentifierPrefixes(String authorIdPrefix, String readerIdPrefix) throws ClientException { + void validateIdentifierPrefixes(String authorIdPrefix, String readerIdPrefix) throws XtaClientException { validateIdentifierPrefix(authorIdPrefix, AUTHOR_ID_PREFIX, AUTHOR_ID_PREFIX_NAME); validateIdentifierPrefix(readerIdPrefix, READER_ID_PREFIX, READER_ID_PREFIX_NAME); } - void validateIdentifierPrefix(String prefix, String expectedPrefix, String prefixName) throws ClientException { + void validateIdentifierPrefix(String prefix, String expectedPrefix, String prefixName) throws XtaClientException { if (!expectedPrefix.equals(normalizePrefix(prefix))) { - throw new ClientException("Expect prefix of %s identifier to be '%s'! (actual: %s)".formatted(prefixName, expectedPrefix, prefix)); + throw new XtaClientException("Expect prefix of %s identifier to be '%s'! (actual: %s)".formatted(prefixName, expectedPrefix, prefix)); } } @@ -54,19 +54,19 @@ public class XdomeaMetaDataValidator { return prefix.endsWith(":") ? prefix.substring(0, prefix.length() - 1) : prefix; } - void validatePrimaryDocumentReferences(XtaFile xdomeaZipFile, List<String> primaryDocumentNames) throws ClientException { + void validatePrimaryDocumentReferences(XtaFile xdomeaZipFile, List<String> primaryDocumentNames) throws XtaClientException { var entryNames = Set.copyOf(zipFileEntryReader.getEntryNames(xdomeaZipFile.content())); for (var primaryDocumentName : primaryDocumentNames) { if (!entryNames.contains(primaryDocumentName)) { - throw new ClientException("Primary document reference '%s' not found in xdomea zip file!".formatted(primaryDocumentName)); + throw new XtaClientException("Primary document reference '%s' not found in xdomea zip file!".formatted(primaryDocumentName)); } } } - void validateZipFileName(String xdomeaZipFileName, String processId, String messageTypeCode) throws ClientException { + void validateZipFileName(String xdomeaZipFileName, String processId, String messageTypeCode) throws XtaClientException { var expectedXdomeaZipFileName = "%s_%s.zip".formatted(processId, messageTypeCode); if (!expectedXdomeaZipFileName.equals(xdomeaZipFileName)) { - throw new ClientException( + throw new XtaClientException( "Expect xdomea zip file name to equal '%s'! (actual: '%s')".formatted(expectedXdomeaZipFileName, xdomeaZipFileName) ); } diff --git a/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreator.java b/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreator.java index 68ad3d9..05c1949 100644 --- a/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreator.java +++ b/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreator.java @@ -11,7 +11,7 @@ import org.w3c.dom.Document; import org.xml.sax.SAXException; import de.ozgcloud.common.errorhandling.TechnicalException; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.model.XtaFile; import de.ozgcloud.xta.client.model.XtaMessage; import de.ozgcloud.xta.client.model.XtaMessageMetaData; @@ -36,7 +36,7 @@ public class XdomeaXtaMessageCreator { return XdomeaXtaMessageCreatorFactory.createInstance().create(); } - public XtaMessage createMessage(XtaFile xdomeaZipFile) throws ClientException { + public XtaMessage createMessage(XtaFile xdomeaZipFile) throws XtaClientException { return XtaMessage.builder() .metaData(deriveValidMetaData(xdomeaZipFile)) .messageFile(xdomeaZipFile) @@ -44,18 +44,18 @@ public class XdomeaXtaMessageCreator { .build(); } - XtaMessageMetaData deriveValidMetaData(XtaFile xdomeaZipFile) throws ClientException { + XtaMessageMetaData deriveValidMetaData(XtaFile xdomeaZipFile) throws XtaClientException { try { var xdomeaXmlValues = readXdomeaXmlValues(xdomeaZipFile); var messageMetadataData = metadataMapper.mapXtaMessageMetadata(xdomeaXmlValues); metaDataValidator.validate(xdomeaZipFile, xdomeaXmlValues, messageMetadataData); return messageMetadataData; } catch (TechnicalException e) { - throw new ClientException("Failed to derive valid message metadata from xdomea document!", e); + throw new XtaClientException("Failed to derive valid message metadata from xdomea document!", e); } } - XdomeaXmlValues readXdomeaXmlValues(XtaFile xdomeaZipFile) throws ClientException { + XdomeaXmlValues readXdomeaXmlValues(XtaFile xdomeaZipFile) throws XtaClientException { var document = readXdomeaXmlDocument(xdomeaZipFile); return xdomeaValueReader.readValues(document); } diff --git a/src/main/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReader.java b/src/main/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReader.java index 90dcbeb..52f3a87 100644 --- a/src/main/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReader.java +++ b/src/main/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReader.java @@ -7,7 +7,7 @@ import java.util.stream.Collectors; import org.w3c.dom.Document; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import lombok.Builder; import lombok.RequiredArgsConstructor; @@ -34,7 +34,7 @@ public class XdomeaValueReader { private final Map<String, XmlValueReader> xmlValueReaders; - public XdomeaXmlValues readValues(Document xdomeaXmlDocument) throws ClientException { + public XdomeaXmlValues readValues(Document xdomeaXmlDocument) throws XtaClientException { return XdomeaXmlValues.builder() .processId(readRequiredValue(xdomeaXmlDocument, PROCESS_ID_XPATH)) .messageTypeCode(readRequiredValue(xdomeaXmlDocument, MESSAGE_TYPE_ID_SUFFIX_XPATH)) @@ -52,11 +52,11 @@ public class XdomeaValueReader { .toList(); } - String readRequiredValue(Document xdomeaXmlDocument, String xpathString) throws ClientException { + String readRequiredValue(Document xdomeaXmlDocument, String xpathString) throws XtaClientException { return getXmlValueReader(xpathString) .readNonEmptyTexts(xdomeaXmlDocument) .findFirst() - .orElseThrow(() -> new ClientException("Required value " + xpathString + " not found in xdomea xml document!")); + .orElseThrow(() -> new XtaClientException("Required value " + xpathString + " not found in xdomea xml document!")); } XmlValueReader getXmlValueReader(String xpath) { diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java b/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java index b28ff46..aedd99a 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -17,6 +16,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.extension.StaticStringListAppender; import de.ozgcloud.xta.client.extension.XtaMessageExampleLoader; import de.ozgcloud.xta.client.extension.XtaTestServerSetupExtension; @@ -61,7 +61,6 @@ class XtaClientITCase { closeAllMessages(setupClient, READER_CLIENT_IDENTIFIER3); } - @DisplayName("fetch messages") @Nested class TestFetchMessages { @@ -93,6 +92,16 @@ class XtaClientITCase { .build()); } + @DisplayName("should throw exception on connection failure") + @Test + @SneakyThrows + void shouldThrowExceptionOnConnectionFailure() { + setupClientWithoutTrustStore(); + + assertThatThrownBy(() -> testClient.fetchMessages((message) -> fail("Should not process any message!"))) + .isInstanceOf(XtaClientException.class); + } + @DisplayName("should fetch no messages if no client identifier is configured") @Test void shouldFetchNoMessagesIfNoClientIdentifierIsConfigured() { @@ -308,14 +317,21 @@ class XtaClientITCase { var transportReports = fetchMessages(); if (hasLogLineContaining(NO_MESSAGE_CLOSED_WARNING)) { + // Case: sendMessages.get(3) was not fetched. assertThat(supportCheckedMetadataItems).hasSize(1 + 2 + 2); + assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.get(4)); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyGreenStatusFor(messageIdBySendIndex(4)); } else { + // Case: sendMessages.get(3) was fetched. assertThat(supportCheckedMetadataItems).hasSize(1 + 3 + 2); + assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.get(3), sendMessages.get(4)); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyGreenStatusFor(messageIdBySendIndex(3), messageIdBySendIndex(4)); } - assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.get(3), sendMessages.get(4)); - assertThatTransportReports(transportReports) - .reportExactlyFor(processedMessages) - .haveExactlyGreenStatusFor(messageIdBySendIndex(3), messageIdBySendIndex(4)); + } @DisplayName("should process messages only if supported, with support for author3") @@ -352,6 +368,7 @@ class XtaClientITCase { return sendMessageIds.get(sendIndex); } + @SneakyThrows private List<XtaTransportReport> fetchMessages() { return testClient.fetchMessages((message) -> { processedMessages.add(message); @@ -376,6 +393,16 @@ class XtaClientITCase { message = XtaMessageExampleLoader.load(messageConfig); } + @DisplayName("should throw exception on connection failure") + @Test + @SneakyThrows + void shouldThrowExceptionOnConnectionFailure() { + setupClientWithoutTrustStore(); + + assertThatThrownBy(() -> testClient.sendMessage(message)) + .isInstanceOf(XtaClientException.class); + } + @DisplayName("should return transport report with open status") @Test @SneakyThrows @@ -385,5 +412,17 @@ class XtaClientITCase { assertThat(transportReport.status()).isEqualTo(XtaMessageStatus.OPEN); } } + + @SneakyThrows + private void setupClientWithoutTrustStore() { + testClient = XtaClient.from( + XTA_TEST_SERVER_SETUP_EXTENSION.createSpecificClientConfigBuilder() + .trustStore(null) + .clientIdentifiers(List.of(READER_CLIENT_IDENTIFIER1)) + .maxListItems(TWO_MAX_LIST_ITEMS) + .isMessageSupported(metaData -> fail("Should not process any message!")) + .build() + ); + } } diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientRemoteITCase.java b/src/test/java/de/ozgcloud/xta/client/XtaClientRemoteITCase.java index 1ab424d..72384e7 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientRemoteITCase.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientRemoteITCase.java @@ -230,10 +230,12 @@ class XtaClientRemoteITCase { ).toList(); } + @SneakyThrows private List<XtaTransportReport> fetchDevMessages() { return devClient.fetchMessages(this::processMessage); } + @SneakyThrows private List<XtaTransportReport> fetchTestMessages() { return testClient.fetchMessages(this::processMessage); } diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java b/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java index 37c7ef4..181f3db 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java @@ -26,7 +26,10 @@ import org.mockito.Spy; import de.ozgcloud.xta.client.config.XtaClientConfig; import de.ozgcloud.xta.client.core.XtaClientService; +import de.ozgcloud.xta.client.core.XtaExceptionHandler; import de.ozgcloud.xta.client.exception.ClientRuntimeException; +import de.ozgcloud.xta.client.exception.XtaClientException; +import de.ozgcloud.xta.client.factory.ClientRuntimeExceptionTestFactory; import de.ozgcloud.xta.client.factory.XtaMessageMetaDataListingTestFactory; import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory; import de.ozgcloud.xta.client.factory.XtaMessageTestFactory; @@ -49,6 +52,9 @@ class XtaClientTest { @Mock private FetchMessageParameterFactory fetchMessageParameterFactory; + @Mock + private XtaExceptionHandler exceptionHandler; + @Spy @InjectMocks private XtaClient client; @@ -77,6 +83,43 @@ class XtaClientTest { @Mock private Consumer<XtaMessage> processMessage; + @Mock + private XtaTransportReport transportReport; + + @Mock + private XtaClientException exception; + + @DisplayName("should return") + @Test + @SneakyThrows + void shouldReturn() { + doReturn(List.of(transportReport)).when(client).fetchMessagesRaw(processMessage); + + var result = client.fetchMessagesRaw(processMessage); + + assertThat(result).containsExactly(transportReport); + } + + @DisplayName("should throw checked exception on runtime exception") + @Test + void shouldThrowCheckedExceptionOnRuntimeException() { + var runTimeException = ClientRuntimeExceptionTestFactory.create(); + doThrow(runTimeException).when(client).sendMessageRaw(message); + when(exceptionHandler.deriveXtaClientException(runTimeException)).thenReturn(exception); + + assertThatThrownBy(() -> client.fetchMessagesRaw(processMessage)) + .isEqualTo(exception); + } + + } + + @DisplayName("fetch messages raw") + @Nested + class TestFetchMessagesRaw { + + @Mock + private Consumer<XtaMessage> processMessage; + @Mock private FetchMessageParameter parameter; @@ -99,7 +142,7 @@ class XtaClientTest { when(fetchMessageParameterFactory.create(SELF_IDENTIFIER, processMessage)).thenReturn(parameter); doReturn(Stream.of(transportReport)).when(client).fetchMessagesForClientIdentifier(parameter); - var result = client.fetchMessages(processMessage); + var result = fetchMessages(); assertThat(result).containsExactly(transportReport); } @@ -114,11 +157,16 @@ class XtaClientTest { void shouldFetchNoMessages() { doReturn(false).when(service).checkAccountActive(SELF_IDENTIFIER); - var result = client.fetchMessages(processMessage); + var result = fetchMessages(); assertThat(result).isEmpty(); } } + + @SneakyThrows + private List<XtaTransportReport> fetchMessages() { + return client.fetchMessages(processMessage); + } } @DisplayName("fetch messages for client identifier") @@ -586,6 +634,39 @@ class XtaClientTest { @Mock private XtaTransportReport transportReport; + @Mock + private XtaClientException exception; + + @DisplayName("should return") + @Test + @SneakyThrows + void shouldReturn() { + doReturn(transportReport).when(client).sendMessageRaw(message); + + var result = client.sendMessage(message); + + assertThat(result).isEqualTo(transportReport); + } + + @DisplayName("should throw checked exception on runtime exception") + @Test + void shouldThrowCheckedExceptionOnRuntimeException() { + var runtimeException = ClientRuntimeExceptionTestFactory.create(); + doThrow(runtimeException).when(client).sendMessageRaw(message); + when(exceptionHandler.deriveXtaClientException(runtimeException)).thenReturn(exception); + + assertThatThrownBy(() -> client.sendMessage(message)) + .isEqualTo(exception); + } + } + + @DisplayName("send message raw") + @Nested + class TestSendMessageRaw { + + @Mock + private XtaTransportReport transportReport; + @BeforeEach void mock() { doNothing().when(client).throwExceptionIfAccountInactive(any()); @@ -619,6 +700,7 @@ class XtaClientTest { assertThat(result).isEqualTo(transportReport); } + @SneakyThrows private XtaTransportReport sendMessage() { return client.sendMessage(message); } diff --git a/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java b/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java index 0f6625c..4e0a958 100644 --- a/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java +++ b/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory; import de.ozgcloud.xta.client.model.XtaIdentifier; import lombok.SneakyThrows; @@ -52,7 +52,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("clientIdentifiers"); } @@ -64,7 +64,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("managementServiceUrl"); } @@ -76,7 +76,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("sendServiceUrl"); } @@ -88,7 +88,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("msgBoxServiceUrl"); } @@ -100,7 +100,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("maxListItems"); } @@ -113,7 +113,7 @@ class XtaConfigValidatorTest { var config = createKeystoreWithClientCertKeyStore(o -> o.password(null)); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("password"); } @@ -123,7 +123,7 @@ class XtaConfigValidatorTest { var config = createKeystoreWithClientCertKeyStore(o -> o.type("")); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("type"); } @@ -133,7 +133,7 @@ class XtaConfigValidatorTest { var config = createKeystoreWithClientCertKeyStore(o -> o.content(null)); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("content"); } diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactoryTest.java new file mode 100644 index 0000000..985c7aa --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactoryTest.java @@ -0,0 +1,29 @@ +package de.ozgcloud.xta.client.core; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Spy; + +class XtaExceptionHandlerFactoryTest { + + @InjectMocks + @Spy + private XtaExceptionHandlerFactory exceptionHandlerFactory; + + @DisplayName("create") + @Nested + class TestCreate { + @DisplayName("should return") + @Test + void shouldReturn() { + var result = exceptionHandlerFactory.create(); + + assertThat(result).isNotNull(); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerTest.java new file mode 100644 index 0000000..df6b0a2 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerTest.java @@ -0,0 +1,176 @@ +package de.ozgcloud.xta.client.core; + +import static de.ozgcloud.xta.client.core.XtaExceptionHandler.*; +import static de.ozgcloud.xta.client.factory.ClientRuntimeExceptionTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.xta.client.exception.ClientRuntimeException; +import de.ozgcloud.xta.client.exception.XtaClientException; +import de.ozgcloud.xta.client.factory.ClientRuntimeExceptionTestFactory; +import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; + +class XtaExceptionHandlerTest { + + @InjectMocks + @Spy + XtaExceptionHandler exceptionHandler; + + private ClientRuntimeException exception; + + @BeforeEach + void setUp() { + exception = ClientRuntimeExceptionTestFactory.create(); + } + + @DisplayName("derive xta client exception") + @Nested + class TestDeriveXtaClientException { + + @Mock + private XtaClientException derivedClientException; + + @DisplayName("should derive xta client exception from client runtime exception") + @Test + void shouldDeriveXtaClientExceptionFromClientRuntimeException() { + doReturn(derivedClientException).when(exceptionHandler).deriveXtaClientExceptionFromClientRuntimeException(exception); + + var result = exceptionHandler.deriveXtaClientException(exception); + + assertThat(result).isEqualTo(derivedClientException); + } + + @DisplayName("without client runtime exception instance") + @Nested + class TestWithoutClientRuntimeExceptionInstance { + + @Mock + private RuntimeException runtimeException; + + @DisplayName("should return with unknown error message") + @Test + void shouldReturnWithUnknownErrorMessage() { + var result = exceptionHandler.deriveXtaClientException(runtimeException); + + assertThat(result.getMessage()).isEqualTo(UNEXPECTED_ERROR_MESSAGE); + } + + @DisplayName("should have cause") + @Test + void shouldHaveCause() { + var result = exceptionHandler.deriveXtaClientException(runtimeException); + + assertThat(result.getCause()).isEqualTo(runtimeException); + } + } + } + + @DisplayName("derive xta client exception from client runtime exception") + @Nested + class TestDeriveXtaClientExceptionFromClientRuntimeException { + + @DisplayName("should keep message if no cause") + @Test + void shouldKeepMessageIfNoCause() { + var exception = new ClientRuntimeException(MESSAGE); + + var result = exceptionHandler.deriveXtaClientExceptionFromClientRuntimeException(exception); + + assertThat(result.getMessage()).isEqualTo(MESSAGE); + } + + @DisplayName("should keep message if cause is not an xta exception") + @Test + void shouldKeepMessageIfCauseIsNotAnXtaException() { + var cause = new Exception(); + var exception = new ClientRuntimeException(MESSAGE, cause); + + var result = exceptionHandler.deriveXtaClientExceptionFromClientRuntimeException(exception); + + assertThat(result.getMessage()).isEqualTo(MESSAGE); + + } + + @DisplayName("should extend detail message if cause is an xta exception") + @Test + void shouldExtendDetailMessageIfCauseIsAnXtaException() { + var result = exceptionHandler.deriveXtaClientExceptionFromClientRuntimeException(exception); + + assertThat(result.getMessage()).contains(MESSAGE, CAUSE_MESSAGE, CAUSE_CODE, CAUSE_NAME); + } + } + + @DisplayName("get detail lines") + @Nested + class TestGetDetailLines { + + @DisplayName("should return empty stream if cause is null") + @Test + void shouldReturnEmptyStreamIfCauseIsNull() { + var result = exceptionHandler.getDetailLines(null).toList(); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return empty stream if cause is not an exception") + @Test + void shouldReturnEmptyStreamIfCauseIsNotAnException() { + var result = exceptionHandler.getDetailLines(new Throwable()).toList(); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return empty stream if cause is not an xta exception") + @Test + void shouldReturnEmptyStreamIfCauseIsNotAnXtaException() { + var result = exceptionHandler.getDetailLines(new Exception()).toList(); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return detail lines if cause is an xta exception") + @Test + void shouldReturnDetailLinesIfCauseIsAnXtaException() { + var causeException = (PermissionDeniedException) exception.getCause(); + doReturn(Stream.of(CAUSE_MESSAGE)).when(exceptionHandler).deriveDetailLinesFromException(causeException); + + var result = exceptionHandler.getDetailLines(causeException).toList(); + + assertThat(result).containsExactly(CAUSE_MESSAGE); + } + } + + @DisplayName("derive detail lines from exception") + @Nested + class TestDeriveDetailLinesFromException { + + @DisplayName("should return empty stream if exception type is not present") + @Test + void shouldReturnEmptyStreamIfExceptionTypeIsNotPresent() { + var result = exceptionHandler.deriveDetailLinesFromException(new Exception()).toList(); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return stream with error code and name if exception type is present") + @Test + void shouldReturnStreamWithErrorCodeAndNameIfExceptionTypeIsPresent() { + var causeException = (PermissionDeniedException) exception.getCause(); + + var result = exceptionHandler.deriveDetailLinesFromException(causeException).toList(); + + assertThat(result).containsExactly(CAUSE_MESSAGE, "[%s] %s".formatted(CAUSE_CODE, CAUSE_NAME)); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java index f9077cf..8047c2d 100644 --- a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java @@ -22,7 +22,7 @@ import org.mockito.Spy; import de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.SneakyThrows; class XtaTLSClientParametersFactoryTest { @@ -123,7 +123,7 @@ class XtaTLSClientParametersFactoryTest { doThrow(new KeyStoreException("something")).when(sslContextBuilder).loadKeyMaterial(any(), any()); assertThatThrownBy(() -> factory.createXtaSslContext()) - .isInstanceOf(ClientInitializationException.class); + .isInstanceOf(XtaClientInitializationException.class); } } diff --git a/src/test/java/de/ozgcloud/xta/client/factory/ClientRuntimeExceptionTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/ClientRuntimeExceptionTestFactory.java new file mode 100644 index 0000000..bfa3c0f --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/factory/ClientRuntimeExceptionTestFactory.java @@ -0,0 +1,33 @@ +package de.ozgcloud.xta.client.factory; + +import de.ozgcloud.xta.client.exception.ClientRuntimeException; +import genv3.de.xoev.transport.xta.x211.CodeFehlernummer; +import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; +import genv3.de.xoev.transport.xta.x211.PermissionDeniedExceptionType; + +public class ClientRuntimeExceptionTestFactory { + + public static final String MESSAGE = "message"; + public static final String CAUSE_MESSAGE = "cause message"; + public static final String CAUSE_CODE = "cause code"; + public static final String CAUSE_NAME = "cause name"; + + public static ClientRuntimeException create() { + return new ClientRuntimeException(MESSAGE, createPermissionDeniedException()); + } + + private static PermissionDeniedException createPermissionDeniedException() { + return new PermissionDeniedException(CAUSE_MESSAGE, createPermissionDeniedExceptionType()); + } + + private static PermissionDeniedExceptionType createPermissionDeniedExceptionType() { + var code = new CodeFehlernummer(); + code.setCode(CAUSE_CODE); + code.setName(CAUSE_NAME); + + var exceptionType = new PermissionDeniedExceptionType(); + exceptionType.setErrorCode(code); + return exceptionType; + } + +} diff --git a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidatorTest.java b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidatorTest.java index b3251be..97dd894 100644 --- a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidatorTest.java +++ b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidatorTest.java @@ -21,7 +21,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.factory.XdomeaXmlValuesTestFactory; import de.ozgcloud.xta.client.factory.XtaFileTestFactory; import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory; @@ -170,7 +170,7 @@ class XdomeaMetaDataValidatorTest { }) void shouldThrowClientExceptionForWrongPrefix(String prefix) { assertThatThrownBy(() -> validator.validateIdentifierPrefix(prefix, EXPECTED_PREFIX, PREFIX_NAME)) - .isInstanceOf(ClientException.class) + .isInstanceOf(XtaClientException.class) .hasMessageContaining(PREFIX_NAME); } @@ -200,7 +200,7 @@ class XdomeaMetaDataValidatorTest { }) void shouldThrowClientExceptionForWrongZipFileName(String wrongZipFileName) { assertThatThrownBy(() -> validator.validateZipFileName(wrongZipFileName, PROCESS_ID, MESSAGE_TYPE_CODE)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } } @@ -241,7 +241,7 @@ class XdomeaMetaDataValidatorTest { when(zipFileEntryReader.getEntryNames(contentDataHandler)).thenReturn(INVALID_ZIP_ENTRY_NAMES); assertThatThrownBy(() -> validator.validatePrimaryDocumentReferences(xdomeaZipFile, PRIMARY_DOCUMENT_NAMES)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } } diff --git a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorITCase.java b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorITCase.java index 6256c85..7ff3ffd 100644 --- a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorITCase.java +++ b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorITCase.java @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.extension.XtaMessageExampleLoader; import de.ozgcloud.xta.client.model.XtaFile; import lombok.SneakyThrows; @@ -77,7 +77,7 @@ class XdomeaXtaMessageCreatorITCase { ); assertThatThrownBy(() -> creator.createMessage(invalidMessageZipFile)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } @DisplayName("should not throw with valid message 0201") @@ -118,7 +118,7 @@ class XdomeaXtaMessageCreatorITCase { ); assertThatThrownBy(() -> creator.createMessage(invalidMessageZipFile)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } @DisplayName("should not throw with valid message 0401") diff --git a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorTest.java b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorTest.java index 6ffa37b..a183007 100644 --- a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorTest.java +++ b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorTest.java @@ -20,7 +20,7 @@ import org.mockito.Spy; import org.w3c.dom.Document; import de.ozgcloud.common.errorhandling.TechnicalException; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.factory.XdomeaXmlValuesTestFactory; import de.ozgcloud.xta.client.factory.XtaFileTestFactory; import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory; @@ -156,7 +156,7 @@ class XdomeaXtaMessageCreatorTest { doThrow(technicalException).when(metadataMapper).mapXtaMessageMetadata(xdomeaXmlValues); assertThatThrownBy(() -> creator.deriveValidMetaData(xdomeaZipFile)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } } diff --git a/src/test/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReaderTest.java b/src/test/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReaderTest.java index 9c72e7a..5b8fd71 100644 --- a/src/test/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReaderTest.java +++ b/src/test/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReaderTest.java @@ -16,7 +16,7 @@ import org.mockito.Mock; import org.mockito.Spy; import org.w3c.dom.Document; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import lombok.SneakyThrows; class XdomeaValueReaderTest { @@ -183,7 +183,7 @@ class XdomeaValueReaderTest { when(xmlValueReader.readNonEmptyTexts(xdomeaXmlDocument)).thenReturn(Stream.empty()); assertThatThrownBy(() -> xdomeaValueReader.readRequiredValue(xdomeaXmlDocument, XPATH_STRING)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } } -- GitLab