> httpClientCustomizer = Optional.empty();
private Builder(KeyStoreConfig keyStoreConfig) {
this.keyStoreConfig = keyStoreConfig;
- this.jaxrsConfig = new ClientConfig();
+ this.httpClientBuilder = HttpClientBuilder.create();
}
/**
@@ -217,22 +192,29 @@ public Builder serviceUri(URI uri) {
/**
* Override the
- * {@link ClientConfiguration#DEFAULT_SOCKET_TIMEOUT_MS default socket timeout value}.
+ * {@link ClientConfiguration#DEFAULT_SOCKET_TIMEOUT default socket timeout value}.
*/
- public Builder socketTimeoutMillis(int millis) {
- this.socketTimeoutMs = millis;
+ public Builder socketTimeout(Duration timeout) {
+ this.socketTimeout = timeout;
return this;
}
/**
* Override the
- * {@link ClientConfiguration#DEFAULT_CONNECT_TIMEOUT_MS default connect timeout value}.
+ * {@link ClientConfiguration#DEFAULT_CONNECT_TIMEOUT default connect timeout value}.
*/
- public Builder connectTimeoutMillis(int millis) {
- this.connectTimeoutMs = millis;
+ public Builder connectTimeout(Duration timeout) {
+ this.connectTimeout = timeout;
return this;
}
+ /**
+ * Set proxy to be used by {@link HttpClient}.
+ */
+ public void proxy(HttpHost proxy) {
+ this.proxy = Optional.of(proxy);
+ }
+
public Builder trustStore(Certificates certificates) {
if (certificates == TEST) {
LOG.warn("Using test certificates in trust store. This should never be done for production environments.");
@@ -240,7 +222,6 @@ public Builder trustStore(Certificates certificates) {
return trustStore(certificates.certificatePaths);
}
-
/**
* Override the trust store configuration to load DER-encoded certificates from the given folder(s).
*
@@ -288,7 +269,7 @@ public Builder includeInUserAgent(String userAgentCustomPart) {
* {@link ClientConfiguration#HTTP_REQUEST_RESPONSE_LOGGER_NAME}.
*/
public Builder enableRequestAndResponseLogging() {
- loggingFeature = Optional.of(new LoggingFeature(java.util.logging.Logger.getLogger(HTTP_REQUEST_RESPONSE_LOGGER_NAME), 16 * 1024));
+ //loggingFeature = Optional.of(new LoggingFeature(java.util.logging.Logger.getLogger(HTTP_REQUEST_RESPONSE_LOGGER_NAME), 16 * 1024));
return this;
}
@@ -317,7 +298,7 @@ public Builder enableDocumentBundleDiskDump(Path directory) {
* together with the {@link SignatureJob job} it was created for. The processor is not responsible for closing
* the stream, as this is handled by the library itself.
*
- * A note on performance
+ * A note on performance:
* The processor is free to do what it want with the passed stream, but bear in mind that the time
* used by a processor adds to the processing time to create signature jobs.
*
@@ -330,7 +311,7 @@ public Builder addDocumentBundleProcessor(DocumentBundleProcessor processor) {
}
/**
- * This methods allows for custom configuration of JAX-RS (i.e. Jersey) if anything is
+ * This methods allows for custom configuration of the {@link HttpClient} if anything is
* needed that is not already supported by the {@link ClientConfiguration.Builder}.
* This method should not be used to configure anything that is already directly supported by the
* {@code ClientConfiguration.Builder} API.
@@ -338,11 +319,10 @@ public Builder addDocumentBundleProcessor(DocumentBundleProcessor processor) {
* If you still need to use this method, consider requesting first-class support for your requirement
* on the library's web site on GitHub.
*
- * @param customizer The operations to do on the JAX-RS {@link Configurable}, e.g.
- * {@link Configurable#register(Object) registering components}.
+ * @param customizer The operations to do on the {@link HttpClientBuilder}.
*/
- public Builder customizeJaxRs(Consumer super Configurable extends Configuration>> customizer) {
- customizer.accept(jaxrsConfig);
+ public Builder customizeHttpClient(Consumer super HttpClientBuilder> customizer) {
+ this.httpClientCustomizer = Optional.of(customizer);
return this;
}
@@ -386,34 +366,53 @@ public Builder usingClock(Clock clock) {
return this;
}
-
- /**
- * Disable the pre-initialization step of the internal HTTP client (Jersey Client) when
- * instantiating the Signature API Client.
- *
- * @see org.glassfish.jersey.client.JerseyClient#preInitialize()
- */
- public Builder disablePreInitializingHttpClient() {
- this.jaxrsConfig.property(PRE_INIT_CLIENT, false);
- return this;
- }
-
public ClientConfiguration build() {
- jaxrsConfig.property(ClientProperties.READ_TIMEOUT, socketTimeoutMs);
- jaxrsConfig.property(ClientProperties.CONNECT_TIMEOUT, connectTimeoutMs);
- jaxrsConfig.register(MultiPartFeature.class);
- jaxrsConfig.register(JaxbMessageReaderWriterProvider.class);
- jaxrsConfig.register(new AddRequestHeaderFilter(USER_AGENT, createUserAgentString()));
- this.loggingFeature.ifPresent(jaxrsConfig::register);
- return new ClientConfiguration(
- keyStoreConfig, jaxrsConfig, globalSender, serviceRoot, certificatePaths,
- documentBundleProcessors, serverCertificateTrustStrategy, clock);
+ // TODO: Add possibility to add logger for http client
+
+ SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(Timeout.ofMilliseconds(socketTimeout.toMillis())).build();
+ PoolingHttpClientConnectionManagerBuilder poolingHttpClientConnectionManager = PoolingHttpClientConnectionManagerBuilder.create()
+ .setDefaultSocketConfig(socketConfig)
+ .setSSLSocketFactory(SSLConnectionSocketFactoryBuilder.create()
+ .setSslContext(sslContext())
+ .setHostnameVerifier(NoopHostnameVerifier.INSTANCE)
+ .build());
+
+ RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
+ .setConnectionRequestTimeout(Timeout.ofMilliseconds(connectionRequestTimeout.toMillis()))
+ .setConnectTimeout(Timeout.ofMilliseconds(connectTimeout.toMillis()))
+ .setResponseTimeout(Timeout.ofMilliseconds(responseArrivalTimeout.toMillis()));
+
+ httpClientBuilder
+ .setConnectionManager(poolingHttpClientConnectionManager.build())
+ .setDefaultRequestConfig(requestConfigBuilder.build())
+ .setUserAgent(createUserAgentString());
+ proxy.ifPresent(httpClientBuilder::setProxy);
+ httpClientCustomizer.ifPresent(customizer -> customizer.accept(httpClientBuilder));
+
+ return new ClientConfiguration(keyStoreConfig, httpClientBuilder.build(), globalSender, serviceRoot, certificatePaths, documentBundleProcessors, clock);
}
String createUserAgentString() {
return MANDATORY_USER_AGENT + customUserAgentPart.map(ua -> String.format(" (%s)", ua)).orElse("");
}
+ private SSLContext sslContext() {
+ try {
+ return SSLContexts.custom()
+ .loadKeyMaterial(keyStoreConfig.keyStore, keyStoreConfig.privatekeyPassword.toCharArray(), (aliases, socket) -> keyStoreConfig.alias)
+ .loadTrustMaterial(TrustStoreLoader.build(() -> certificatePaths), new SignatureApiTrustStrategy(serverCertificateTrustStrategy))
+ .build();
+ } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) {
+ if (e instanceof UnrecoverableKeyException && "Given final block not properly padded".equals(e.getMessage())) {
+ throw new KeyException(
+ "Unable to load key from keystore, because " + e.getClass().getSimpleName() + ": '" + e.getMessage() + "'. Possible causes:\n" +
+ "* Wrong password for private key (the password for the keystore and the private key may not be the same)\n" +
+ "* Multiple private keys in the keystore with different passwords (private keys in the same key store must have the same password)", e);
+ } else {
+ throw new KeyException("Unable to create the SSLContext, because " + e.getClass().getSimpleName() + ": '" + e.getMessage() + "'", e);
+ }
+ }
+ }
}
}
diff --git a/src/main/java/no/digipost/signature/client/archive/ArchiveClient.java b/src/main/java/no/digipost/signature/client/archive/ArchiveClient.java
index b3075bbe..1f345c9a 100644
--- a/src/main/java/no/digipost/signature/client/archive/ArchiveClient.java
+++ b/src/main/java/no/digipost/signature/client/archive/ArchiveClient.java
@@ -16,7 +16,7 @@ public ArchiveClient(HttpIntegrationConfiguration httpIntegrationConfig) {
}
public ResponseInputStream getPAdES(ArchiveOwner owner, String id) {
- return client.getDataStream(root -> root.path(owner.getOrganizationNumber()).path("archive/documents/").path(id).path("pades"));
+ return client.getDataStream(owner.getOrganizationNumber() + "/archive/documents/" + id + "/pades");
}
}
diff --git a/src/main/java/no/digipost/signature/client/asice/manifest/ManifestCreator.java b/src/main/java/no/digipost/signature/client/asice/manifest/ManifestCreator.java
index 230f32fd..05ba8aed 100644
--- a/src/main/java/no/digipost/signature/client/asice/manifest/ManifestCreator.java
+++ b/src/main/java/no/digipost/signature/client/asice/manifest/ManifestCreator.java
@@ -3,9 +3,6 @@
import no.digipost.signature.client.core.Sender;
import no.digipost.signature.client.core.SignatureJob;
import no.digipost.signature.client.core.exceptions.RuntimeIOException;
-import no.digipost.signature.client.core.exceptions.XmlValidationException;
-import org.springframework.oxm.MarshallingFailureException;
-import org.xml.sax.SAXParseException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -20,13 +17,6 @@ public Manifest createManifest(JOB job, Sender sender) {
try (ByteArrayOutputStream manifestStream = new ByteArrayOutputStream()) {
marshal(xmlManifest, manifestStream);
return new Manifest(manifestStream.toByteArray());
- } catch (MarshallingFailureException e) {
- if (e.getMostSpecificCause() instanceof SAXParseException) {
- throw new XmlValidationException("Unable to validate generated Manifest XML. " +
- "This typically happens if one or more values are not in accordance with the XSD. " +
- "You may inspect the cause (by calling getCause()) to see which constraint has been violated.", (SAXParseException) e.getMostSpecificCause());
- }
- throw e;
} catch (IOException e) {
throw new RuntimeIOException(e);
}
diff --git a/src/main/java/no/digipost/signature/client/asice/signature/CreateSignature.java b/src/main/java/no/digipost/signature/client/asice/signature/CreateSignature.java
index f20c4fc9..1d0096d8 100644
--- a/src/main/java/no/digipost/signature/client/asice/signature/CreateSignature.java
+++ b/src/main/java/no/digipost/signature/client/asice/signature/CreateSignature.java
@@ -4,15 +4,15 @@
import no.digipost.signature.client.core.exceptions.XmlConfigurationException;
import no.digipost.signature.client.core.exceptions.XmlValidationException;
import no.digipost.signature.client.security.KeyStoreConfig;
+import no.digipost.signature.jaxb.JaxbMarshaller;
import no.digipost.signature.xsd.SignatureApiSchemas;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-import org.springframework.xml.validation.SchemaLoaderUtils;
-import org.springframework.xml.validation.XmlValidatorFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import javax.xml.XMLConstants;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.NodeSetData;
import javax.xml.crypto.URIDereferencer;
@@ -33,21 +33,34 @@
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
import javax.xml.validation.Schema;
-
+import javax.xml.validation.SchemaFactory;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
import java.io.UnsupportedEncodingException;
+import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.time.Clock;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
+import static java.util.Objects.requireNonNull;
public class CreateSignature {
@@ -82,14 +95,50 @@ public CreateSignature(Clock clock) {
schema = loadSchema();
}
- private Schema loadSchema() {
+ private static InputSource createInputSource(String resource) {
+ URL resourceUrl = requireNonNull(JaxbMarshaller.class.getResource(resource), resource);
+ try (InputStream inputStream = resourceUrl.openStream()) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ final int bufLen = 1024;
+ byte[] buf = new byte[bufLen];
+ int readLen;
+ while ((readLen = inputStream.read(buf, 0, bufLen)) != -1)
+ outputStream.write(buf, 0, readLen);
+
+ InputSource source = new InputSource(new ByteArrayInputStream(outputStream.toByteArray()));
+ source.setSystemId(resourceUrl.toString());
+ return source;
+ } catch (IOException e) {
+ throw new UncheckedIOException(
+ "Unable to resolve " + resource + " from " + resourceUrl + ", " +
+ "because " + e.getClass().getSimpleName() + " '" + e.getMessage() + "'", e);
+ }
+ }
+
+ private static Schema createSchema(Collection resources) {
try {
- return SchemaLoaderUtils.loadSchema(new Resource[]{new ClassPathResource(SignatureApiSchemas.XMLDSIG_SCHEMA), new ClassPathResource(SignatureApiSchemas.ASICE_SCHEMA)}, XmlValidatorFactory.SCHEMA_W3C_XML);
- } catch (IOException | SAXException e) {
- throw new ConfigurationException("Failed to load schemas for validating signatures", e);
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ parserFactory.setNamespaceAware(true);
+ parserFactory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+
+ SAXParser saxParser = parserFactory.newSAXParser();
+ XMLReader xmlReader = saxParser.getXMLReader();
+ Source[] schemaSources = resources.stream()
+ .map(resource -> new SAXSource(xmlReader, createInputSource(resource)))
+ .toArray(Source[]::new);
+
+ SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ return schemaFactory.newSchema(schemaSources);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not create schema from resources [" + String.join(", ", resources) + "]", e);
}
}
+ private Schema loadSchema() {
+ return createSchema(new HashSet<>(Arrays.asList(SignatureApiSchemas.XMLDSIG_SCHEMA, SignatureApiSchemas.ASICE_SCHEMA)));
+ }
+
public Signature createSignature(final List extends SignableFileReference> attachedFiles, final KeyStoreConfig keyStoreConfig) {
return new Signature(domUtils.serializeToXml(createXmlSignature(attachedFiles, keyStoreConfig)));
}
diff --git a/src/main/java/no/digipost/signature/client/asice/signature/CreateXAdESArtifacts.java b/src/main/java/no/digipost/signature/client/asice/signature/CreateXAdESArtifacts.java
index 2ea05a09..810e9f1b 100644
--- a/src/main/java/no/digipost/signature/client/asice/signature/CreateXAdESArtifacts.java
+++ b/src/main/java/no/digipost/signature/client/asice/signature/CreateXAdESArtifacts.java
@@ -44,7 +44,7 @@ XAdESArtifacts createArtifactsToSign(List extends SignableFileReference> files
}
DigestAlgAndValueType certificateDigest = new DigestAlgAndValueType(sha1DigestMethod, certificateDigestValue);
- X509IssuerSerialType certificateIssuer = new X509IssuerSerialType(certificate.getIssuerDN().getName(), certificate.getSerialNumber());
+ X509IssuerSerialType certificateIssuer = new X509IssuerSerialType(certificate.getIssuerX500Principal().getName(), certificate.getSerialNumber());
SigningCertificate signingCertificate = new SigningCertificate(singletonList(new CertIDType(certificateDigest, certificateIssuer, null)));
ZonedDateTime now = ZonedDateTime.now(clock);
diff --git a/src/main/java/no/digipost/signature/client/asice/signature/XAdESArtifacts.java b/src/main/java/no/digipost/signature/client/asice/signature/XAdESArtifacts.java
index 54cfb94a..2ec0f40f 100644
--- a/src/main/java/no/digipost/signature/client/asice/signature/XAdESArtifacts.java
+++ b/src/main/java/no/digipost/signature/client/asice/signature/XAdESArtifacts.java
@@ -1,29 +1,40 @@
package no.digipost.signature.client.asice.signature;
import no.digipost.signature.api.xml.thirdparty.xades.QualifyingProperties;
-import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
import javax.xml.transform.dom.DOMResult;
import static java.util.stream.IntStream.range;
final class XAdESArtifacts {
- private static Jaxb2Marshaller marshaller;
+ private static Marshaller marshaller;
static {
- marshaller = new Jaxb2Marshaller();
- marshaller.setClassesToBeBound(QualifyingProperties.class);
+ try {
+ marshaller = JAXBContext.newInstance(QualifyingProperties.class).createMarshaller();
+ } catch (JAXBException e) {
+ throw new RuntimeException(e);
+ }
}
public static XAdESArtifacts from(QualifyingProperties qualifyingProperties) {
DOMResult domResult = new DOMResult();
- marshaller.marshal(qualifyingProperties, domResult);
+ try {
+ marshaller.marshal(qualifyingProperties, domResult);
+ } catch (JAXBException e) {
+ throw new RuntimeException(
+ "Failed to marshal qualifying properties, because " +
+ e.getClass().getSimpleName() + " '" + e.getMessage() + "'", e);
+ }
return from((Document) domResult.getNode());
}
diff --git a/src/main/java/no/digipost/signature/client/core/PollingQueue.java b/src/main/java/no/digipost/signature/client/core/PollingQueue.java
index 1141cbd9..196aeed3 100644
--- a/src/main/java/no/digipost/signature/client/core/PollingQueue.java
+++ b/src/main/java/no/digipost/signature/client/core/PollingQueue.java
@@ -30,4 +30,9 @@ public int hashCode() {
return Objects.hash(value);
}
+ @Override
+ public String toString() {
+ return value != null ? "polling-queue '" + value + "'" : "no specified polling-queue (default)";
+ }
+
}
diff --git a/src/main/java/no/digipost/signature/client/core/ResponseInputStream.java b/src/main/java/no/digipost/signature/client/core/ResponseInputStream.java
index 85427c4a..4f203256 100644
--- a/src/main/java/no/digipost/signature/client/core/ResponseInputStream.java
+++ b/src/main/java/no/digipost/signature/client/core/ResponseInputStream.java
@@ -5,14 +5,14 @@
public class ResponseInputStream extends FilterInputStream {
- private final int contentLength;
+ private final long contentLength;
- public ResponseInputStream(InputStream in, int contentLength) {
+ public ResponseInputStream(InputStream in, long contentLength) {
super(in);
this.contentLength = contentLength;
}
- public int getContentLength() {
+ public long getContentLength() {
return contentLength;
}
}
diff --git a/src/main/java/no/digipost/signature/client/core/Sender.java b/src/main/java/no/digipost/signature/client/core/Sender.java
index 3bf061ac..684fbfb6 100644
--- a/src/main/java/no/digipost/signature/client/core/Sender.java
+++ b/src/main/java/no/digipost/signature/client/core/Sender.java
@@ -41,4 +41,9 @@ public int hashCode() {
return Objects.hash(organizationNumber, pollingQueue);
}
+ @Override
+ public String toString() {
+ return "sender " + organizationNumber + ", " + pollingQueue;
+ }
+
}
diff --git a/src/main/java/no/digipost/signature/client/core/exceptions/CantQueryStatusException.java b/src/main/java/no/digipost/signature/client/core/exceptions/CantQueryStatusException.java
index c0e4bce5..0d85b01e 100644
--- a/src/main/java/no/digipost/signature/client/core/exceptions/CantQueryStatusException.java
+++ b/src/main/java/no/digipost/signature/client/core/exceptions/CantQueryStatusException.java
@@ -1,13 +1,13 @@
package no.digipost.signature.client.core.exceptions;
-import javax.ws.rs.core.Response.StatusType;
+import no.digipost.signature.client.core.internal.http.StatusCode;
public class CantQueryStatusException extends SignatureException {
- public CantQueryStatusException(StatusType status, String errorMessageFromServer) {
+ public CantQueryStatusException(StatusCode status, String errorMessageFromServer) {
super("The service refused to process the status request. This happens when the job has not been completed " +
"(i.e. the signer haven't signed or rejected). Please wait until the signer have been redirected to " +
"one of the exit URLs provided in the initial request before querying the job's status. The server response was " +
- status.getStatusCode() + " " + status.getReasonPhrase() + " '" + errorMessageFromServer + "'");
+ status.value() + " '" + errorMessageFromServer + "'");
}
}
diff --git a/src/main/java/no/digipost/signature/client/core/exceptions/JobCannotBeCancelledException.java b/src/main/java/no/digipost/signature/client/core/exceptions/JobCannotBeCancelledException.java
index 13f24acd..891dc0c9 100644
--- a/src/main/java/no/digipost/signature/client/core/exceptions/JobCannotBeCancelledException.java
+++ b/src/main/java/no/digipost/signature/client/core/exceptions/JobCannotBeCancelledException.java
@@ -1,20 +1,19 @@
package no.digipost.signature.client.core.exceptions;
import no.digipost.signature.api.xml.XMLError;
-
-import javax.ws.rs.core.Response.StatusType;
+import no.digipost.signature.client.core.internal.http.StatusCode;
public class JobCannotBeCancelledException extends SignatureException {
- public JobCannotBeCancelledException(StatusType status, XMLError errorEntity) {
+ public JobCannotBeCancelledException(StatusCode status, XMLError errorEntity) {
this(status, errorEntity.getErrorCode(), errorEntity.getErrorMessage());
}
- public JobCannotBeCancelledException(StatusType status, String errorCode, String errorMessageFromServer) {
+ public JobCannotBeCancelledException(StatusCode status, String errorCode, String errorMessageFromServer) {
super("The service refused to process the cancellation. This happens when the job has been completed " +
"(i.e. all signers have signed or rejected, the job has expired, etc.) since receiving last update. " +
"Please ask the service for status changes to get the latest changes. The server response was " +
- status.getStatusCode() + " " + status.getReasonPhrase() + " '" + errorCode + ": " + errorMessageFromServer + "'");
+ status.value() + " '" + errorCode + ": " + errorMessageFromServer + "'");
}
}
diff --git a/src/main/java/no/digipost/signature/client/core/exceptions/UnexpectedResponseException.java b/src/main/java/no/digipost/signature/client/core/exceptions/UnexpectedResponseException.java
index b8456ead..7dc2b9f5 100644
--- a/src/main/java/no/digipost/signature/client/core/exceptions/UnexpectedResponseException.java
+++ b/src/main/java/no/digipost/signature/client/core/exceptions/UnexpectedResponseException.java
@@ -1,49 +1,39 @@
package no.digipost.signature.client.core.exceptions;
import no.digipost.signature.api.xml.XMLError;
-
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.Response.StatusType;
+import no.digipost.signature.client.core.internal.http.StatusCode;
import java.util.Objects;
public class UnexpectedResponseException extends SignatureException {
private final XMLError error;
- private final StatusType actualStatus;
+ private final int httpStatusCode;
- public UnexpectedResponseException(StatusType actual) {
+ public UnexpectedResponseException(StatusCode actual) {
this(null, actual);
}
- public UnexpectedResponseException(Object errorEntity, StatusType actual, StatusType ... expected) {
+ public UnexpectedResponseException(Object errorEntity, StatusCode actual, StatusCode ... expected) {
this(errorEntity, null, actual, expected);
}
- public UnexpectedResponseException(Object errorEntity, Throwable cause, StatusType actual, StatusType ... expected) {
+ public UnexpectedResponseException(Object errorEntity, Throwable cause, StatusCode actual, StatusCode ... expected) {
super("Expected " + prettyprintExpectedStatuses(expected) +
- ", but got " + actual.getStatusCode() + " " + actual.getReasonPhrase() +
+ ", but got " + actual.value() +
(errorEntity != null ? " [" + errorEntity + "]" : "") +
(cause != null ? " - " + cause.getClass().getSimpleName() + ": '" + cause.getMessage() + "'.": ""),
cause);
this.error = errorEntity instanceof XMLError ? (XMLError) errorEntity : null;
- this.actualStatus = actual;
- }
-
- public StatusType getActualStatus() {
- return actualStatus;
- }
-
- public boolean is(Status.Family family) {
- return actualStatus != null && actualStatus.getFamily() == family;
+ this.httpStatusCode = actual.value();
}
- public boolean isStatusCode(int statusCode) {
- return actualStatus != null && actualStatus.getStatusCode() == statusCode;
+ public boolean isHttpStatusCode(int statusCode) {
+ return this.httpStatusCode == statusCode;
}
- public boolean isStatusCodeOf(StatusType status) {
- return isStatusCode(status.getStatusCode());
+ public int httpStatusCode() {
+ return httpStatusCode;
}
public String getErrorCode() {
@@ -59,10 +49,10 @@ public String getErrorType() {
}
public boolean isErrorCode(String errorCode) {
- return error != null & Objects.equals(error.getErrorCode(), errorCode);
+ return error != null && Objects.equals(error.getErrorCode(), errorCode);
}
- private static String prettyprintExpectedStatuses(StatusType ... statuses) {
+ private static String prettyprintExpectedStatuses(StatusCode ... statuses) {
if (statuses == null || statuses.length == 0) {
return "status not specified";
}
@@ -73,8 +63,8 @@ private static String prettyprintExpectedStatuses(StatusType ... statuses) {
return message + "]";
}
- private static String prettyprintSingleStatus(StatusType status) {
- return status.getStatusCode() + " " + status.getReasonPhrase();
+ private static String prettyprintSingleStatus(StatusCode status) {
+ return status.value() +"";
}
}
diff --git a/src/main/java/no/digipost/signature/client/core/internal/ClientExceptionMapper.java b/src/main/java/no/digipost/signature/client/core/internal/ClientExceptionMapper.java
index 62c221a2..d852d90e 100644
--- a/src/main/java/no/digipost/signature/client/core/internal/ClientExceptionMapper.java
+++ b/src/main/java/no/digipost/signature/client/core/internal/ClientExceptionMapper.java
@@ -1,11 +1,8 @@
package no.digipost.signature.client.core.internal;
-import no.digipost.signature.client.core.exceptions.ConfigurationException;
import no.digipost.signature.client.core.exceptions.SignatureException;
-import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
-import javax.ws.rs.ProcessingException;
import java.util.function.Supplier;
class ClientExceptionMapper {
@@ -20,23 +17,13 @@ void doWithMappedClientException(Runnable action) {
T doWithMappedClientException(Supplier produceResult) {
try {
return produceResult.get();
- } catch (ProcessingException e) {
+ } catch (RuntimeException e) {
throw map(e);
}
}
- private RuntimeException map(ProcessingException e) {
- if (e.getCause() instanceof SSLException) {
- String sslExceptionMessage = e.getCause().getMessage();
- if (sslExceptionMessage != null && sslExceptionMessage.contains("protocol_version")) {
- return new ConfigurationException(
- "Invalid TLS protocol version. This will typically happen if you're running on an older Java version, which doesn't support TLS 1.2. " +
- "Java 7 needs to be explicitly configured to support TLS 1.2. See 'JSSE tuning parameters' at " +
- "https://blogs.oracle.com/java-platform-group/entry/diagnosing_tls_ssl_and_https.", e);
- }
- }
-
+ private RuntimeException map(RuntimeException e) {
if (e.getCause() instanceof SSLHandshakeException) {
return new SignatureException(
"Unable to perform SSL handshake with remote server. Some possible causes (could be others, see underlying error): \n" +
diff --git a/src/main/java/no/digipost/signature/client/core/internal/ClientHelper.java b/src/main/java/no/digipost/signature/client/core/internal/ClientHelper.java
index ead50461..af960b2b 100644
--- a/src/main/java/no/digipost/signature/client/core/internal/ClientHelper.java
+++ b/src/main/java/no/digipost/signature/client/core/internal/ClientHelper.java
@@ -20,55 +20,59 @@
import no.digipost.signature.client.core.exceptions.InvalidStatusQueryTokenException;
import no.digipost.signature.client.core.exceptions.JobCannotBeCancelledException;
import no.digipost.signature.client.core.exceptions.NotCancellableException;
-import no.digipost.signature.client.core.exceptions.RuntimeIOException;
import no.digipost.signature.client.core.exceptions.SignatureException;
import no.digipost.signature.client.core.exceptions.TooEagerPollingException;
import no.digipost.signature.client.core.exceptions.UnexpectedResponseException;
import no.digipost.signature.client.core.internal.http.ResponseStatus;
import no.digipost.signature.client.core.internal.http.SignatureHttpClient;
+import no.digipost.signature.client.core.internal.http.StatusCode;
+import no.digipost.signature.client.core.internal.http.StatusCodeFamily;
+import no.digipost.signature.client.core.internal.xml.Marshalling;
import no.digipost.signature.client.direct.WithSignerUrl;
import org.apache.commons.lang3.StringUtils;
-import org.glassfish.jersey.media.multipart.BodyPart;
-import org.glassfish.jersey.media.multipart.MultiPart;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.entity.mime.ByteArrayBody;
+import org.apache.hc.client5.http.entity.mime.InputStreamBody;
+import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
+import org.apache.hc.client5.http.entity.mime.MultipartPartBuilder;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.HeaderGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.Invocation;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.StatusType;
-
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.UncheckedIOException;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.ZonedDateTime;
-import java.util.Arrays;
-import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
-import java.util.function.UnaryOperator;
import static java.time.format.DateTimeFormatter.ISO_DATE_TIME;
-import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
-import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
-import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
-import static javax.ws.rs.core.MediaType.APPLICATION_XML_TYPE;
-import static javax.ws.rs.core.Response.Status.CONFLICT;
-import static javax.ws.rs.core.Response.Status.FORBIDDEN;
-import static javax.ws.rs.core.Response.Status.NOT_FOUND;
-import static javax.ws.rs.core.Response.Status.NO_CONTENT;
-import static javax.ws.rs.core.Response.Status.OK;
-import static javax.ws.rs.core.Response.Status.TOO_MANY_REQUESTS;
-import static javax.ws.rs.core.Response.Status.Family.SUCCESSFUL;
import static no.digipost.signature.client.core.internal.ActualSender.getActualSender;
import static no.digipost.signature.client.core.internal.ErrorCodes.BROKER_NOT_AUTHORIZED;
import static no.digipost.signature.client.core.internal.ErrorCodes.SIGNING_CEREMONY_NOT_COMPLETED;
import static no.digipost.signature.client.core.internal.Target.DIRECT;
import static no.digipost.signature.client.core.internal.Target.PORTAL;
+import static no.digipost.signature.client.core.internal.http.StatusCodeFamily.SUCCESSFUL;
+import static org.apache.hc.core5.http.ContentType.APPLICATION_OCTET_STREAM;
+import static org.apache.hc.core5.http.ContentType.APPLICATION_XML;
+import static org.apache.hc.core5.http.ContentType.MULTIPART_MIXED;
+import static org.apache.hc.core5.http.HttpHeaders.ACCEPT;
+import static org.apache.hc.core5.http.HttpHeaders.CONTENT_TYPE;
public class ClientHelper {
@@ -90,74 +94,140 @@ public ClientHelper(SignatureHttpClient httpClient, Optional globalSende
public XMLDirectSignatureJobResponse sendSignatureJobRequest(XMLDirectSignatureJobRequest signatureJobRequest, DocumentBundle documentBundle, Optional sender) {
final Sender actualSender = getActualSender(sender, globalSender);
- final BodyPart signatureJobBodyPart = new BodyPart(signatureJobRequest, APPLICATION_XML_TYPE);
- final BodyPart documentBundleBodyPart = new BodyPart(documentBundle.getInputStream(), APPLICATION_OCTET_STREAM_TYPE);
-
- return call(() -> new UsingBodyParts(signatureJobBodyPart, documentBundleBodyPart)
- .postAsMultiPart(DIRECT.path(actualSender), XMLDirectSignatureJobResponse.class));
- }
-
- public XMLDirectSignerResponse requestNewRedirectUrl(WithSignerUrl url) {
- try (Response response = postEntity(url.getSignerUrl(), new XMLDirectSignerUpdateRequest().withRedirectUrl(new XMLEmptyElement()))) {
- return parseResponse(response, XMLDirectSignerResponse.class);
- }
+ return multipartSignatureJobRequest(signatureJobRequest, documentBundle, actualSender, DIRECT, XMLDirectSignatureJobResponse.class);
}
public XMLPortalSignatureJobResponse sendPortalSignatureJobRequest(XMLPortalSignatureJobRequest signatureJobRequest, DocumentBundle documentBundle, Optional sender) {
final Sender actualSender = getActualSender(sender, globalSender);
- final BodyPart signatureJobBodyPart = new BodyPart(signatureJobRequest, APPLICATION_XML_TYPE);
- final BodyPart documentBundleBodyPart = new BodyPart(documentBundle.getInputStream(), APPLICATION_OCTET_STREAM_TYPE);
+ return multipartSignatureJobRequest(signatureJobRequest, documentBundle, actualSender, PORTAL, XMLPortalSignatureJobResponse.class);
+ }
+
+ private RESPONSE multipartSignatureJobRequest(REQUEST signatureJobRequest, DocumentBundle documentBundle, Sender actualSender, Target target, Class responseClass) {
+ return call(() -> {
+ try {
+ MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
+ multipartEntityBuilder.setContentType(MULTIPART_MIXED);
+
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ Marshalling.marshal(signatureJobRequest, os);
+ multipartEntityBuilder.addPart(MultipartPartBuilder.create()
+ .setBody(new ByteArrayBody(os.toByteArray(), APPLICATION_XML, ""))
+ .addHeader(CONTENT_TYPE, APPLICATION_XML.getMimeType())
+ .build());
+ }
+ multipartEntityBuilder.addPart(MultipartPartBuilder.create()
+ .setBody(new InputStreamBody(documentBundle.getInputStream(), APPLICATION_OCTET_STREAM, ""))
+ .addHeader(CONTENT_TYPE, APPLICATION_OCTET_STREAM.getMimeType()).build());
+
+ try (HttpEntity multiPart = multipartEntityBuilder.build()) {
+ ClassicHttpRequest request = ClassicRequestBuilder
+ .post(httpClient.constructUrl(uri -> uri.appendPath(target.path(actualSender))))
+ .addHeader(ACCEPT, APPLICATION_XML.getMimeType())
+ .build();
+
+ request.setEntity(multiPart);
+
+ return httpClient.httpClient().execute(request, response -> parseResponse(response, responseClass));
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
+ }
- return call(() -> new UsingBodyParts(signatureJobBodyPart, documentBundleBodyPart)
- .postAsMultiPart(PORTAL.path(actualSender), XMLPortalSignatureJobResponse.class));
+ public XMLDirectSignerResponse requestNewRedirectUrl(WithSignerUrl url) {
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ Marshalling.marshal(new XMLDirectSignerUpdateRequest().withRedirectUrl(new XMLEmptyElement()), os);
+ ClassicHttpRequest request = new HttpPost(url.getSignerUrl());
+ request.addHeader(ACCEPT, APPLICATION_XML.getMimeType());
+
+ return httpClient.httpClient().execute(request, response -> parseResponse(response, XMLDirectSignerResponse.class));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
public XMLDirectSignatureJobStatusResponse sendSignatureJobStatusRequest(final URI statusUrl) {
return call(() -> {
- Invocation.Builder request = httpClient.target(statusUrl).request().accept(APPLICATION_XML_TYPE);
-
- try (Response response = request.get()) {
- ResponseStatus.resolve(response.getStatus()).expect(SUCCESSFUL).orThrow(status -> {
- if (status == FORBIDDEN) {
- XMLError error = extractError(response);
- if (ErrorCodes.INVALID_STATUS_QUERY_TOKEN.sameAs(error.getErrorCode())) {
- return new InvalidStatusQueryTokenException(statusUrl, error.getErrorMessage());
- }
- } else if (status == NOT_FOUND) {
- XMLError error = extractError(response);
- if (SIGNING_CEREMONY_NOT_COMPLETED.sameAs(error.getErrorCode())) {
- return new CantQueryStatusException(status, error.getErrorMessage());
+ ClassicHttpRequest request = new HttpGet(statusUrl);
+ request.addHeader(ACCEPT, APPLICATION_XML.getMimeType());
+
+ try {
+ return httpClient.httpClient().execute(request, response -> {
+ ResponseStatus.resolve(response.getCode()).expect(StatusCodeFamily.SUCCESSFUL).orThrow(status -> {
+ if (status.value() == HttpStatus.SC_FORBIDDEN) {
+ XMLError error = extractError(response);
+ if (ErrorCodes.INVALID_STATUS_QUERY_TOKEN.sameAs(error.getErrorCode())) {
+ return new InvalidStatusQueryTokenException(statusUrl, error.getErrorMessage());
+ }
+ } else if (status.value() == HttpStatus.SC_NOT_FOUND) {
+ XMLError error = extractError(response);
+ if (SIGNING_CEREMONY_NOT_COMPLETED.sameAs(error.getErrorCode())) {
+ return new CantQueryStatusException(status, error.getErrorMessage());
+ }
}
- }
- return exceptionForGeneralError(response);
+ return exceptionForGeneralError(response);
+ });
+ return parseResponse(response, XMLDirectSignatureJobStatusResponse.class);
});
- return response.readEntity(XMLDirectSignatureJobStatusResponse.class);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
});
}
- public ResponseInputStream getDataStream(URI uri, MediaType ... acceptedResponses) {
- return getDataStream(ignoredRoot -> httpClient.target(uri), acceptedResponses);
+ public ResponseInputStream getDataStream(String path, ContentType ... acceptedResponses) {
+ return getDataStream(httpClient.constructUrl(uri -> uri.appendPath(path)));
}
- public ResponseInputStream getDataStream(UnaryOperator targetResolver, MediaType ... acceptedResponses) {
+ public ResponseInputStream getDataStream(URI absoluteUri, ContentType ... acceptedResponses) {
+ if (!absoluteUri.isAbsolute()) {
+ throw new IllegalArgumentException("'" + absoluteUri + "' is not an absolute URL");
+ }
return call(() -> {
- Response response = targetResolver.apply(httpClient.signatureServiceRoot()).request().accept(acceptedResponses).get();
- InputStream inputStream = parseResponse(response, InputStream.class);
- return new ResponseInputStream(inputStream, response.getLength());
+ HeaderGroup acceptHeader = new HeaderGroup();
+ for (ContentType acceptedType : acceptedResponses) {
+ acceptHeader.addHeader(new BasicHeader(ACCEPT, acceptedType.getMimeType()));
+ }
+
+ ClassicHttpRequest request = ClassicRequestBuilder.get(absoluteUri)
+ .addHeader(acceptHeader.getCondensedHeader(ACCEPT))
+ .build();
+
+ ClassicHttpResponse response = null;
+ try {
+ response = httpClient.httpClient().execute(null, request);
+ StatusCode statusCode = StatusCode.from(response.getCode());
+ if (!statusCode.is(SUCCESSFUL)) {
+ throw exceptionForGeneralError(response);
+ }
+ return new ResponseInputStream(response.getEntity().getContent(), response.getEntity().getContentLength());
+ } catch (Exception e) {
+ if (response != null) {
+ try {
+ response.close();
+ } catch (IOException closingException) {
+ e.addSuppressed(closingException);
+ }
+ }
+ throw e instanceof RuntimeException
+ ? (RuntimeException) e
+ : new RuntimeException(request + ": " + e.getClass().getSimpleName() + " '" + e.getMessage() + "'", e);
+ }
});
}
public void cancel(final Cancellable cancellable) {
call(() -> {
if (cancellable.getCancellationUrl() != null) {
- URI url = cancellable.getCancellationUrl().getUrl();
- try (Response response = postEmptyEntity(url)) {
- ResponseStatus.resolve(response.getStatus())
- .throwIf(CONFLICT, status -> new JobCannotBeCancelledException(status, extractError(response)))
- .expect(SUCCESSFUL)
- .orThrow(status -> exceptionForGeneralError(response));
+ try(ClassicHttpResponse response = postEmptyEntity(cancellable.getCancellationUrl().getUrl())) {
+ ResponseStatus.resolve(response.getCode())
+ .throwIf(HttpStatus.SC_CONFLICT, status -> new JobCannotBeCancelledException(status, extractError(response)))
+ .expect(StatusCodeFamily.SUCCESSFUL)
+ .orThrow(status -> exceptionForGeneralError(response));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
} else {
throw new NotCancellableException();
@@ -175,22 +245,28 @@ public JobStatusResponse getDirectStatusCha
private JobStatusResponse getStatusChange(final Optional sender, final Target target, final Class responseClass) {
return call(() -> {
+
Sender actualSender = getActualSender(sender, globalSender);
- Invocation.Builder request = httpClient.signatureServiceRoot().path(target.path(actualSender))
- .queryParam(POLLING_QUEUE_QUERY_PARAMETER, actualSender.getPollingQueue().value)
- .request()
- .accept(APPLICATION_XML_TYPE);
- try (Response response = request.get()) {
- StatusType status = ResponseStatus.resolve(response.getStatus())
- .throwIf(TOO_MANY_REQUESTS, s -> new TooEagerPollingException())
- .expect(SUCCESSFUL).orThrow(unexpectedStatus -> exceptionForGeneralError(response));
- return new JobStatusResponse<>(status == NO_CONTENT ? null : response.readEntity(responseClass), getNextPermittedPollTime(response));
+ URI jobStatusUrl = httpClient.constructUrl(uri -> uri
+ .appendPath(target.path(actualSender))
+ .addParameter(POLLING_QUEUE_QUERY_PARAMETER, actualSender.getPollingQueue().value));
+
+ try {
+ ClassicHttpRequest get = ClassicRequestBuilder.get(jobStatusUrl).addHeader(ACCEPT, APPLICATION_XML.getMimeType()).build();
+ return httpClient.httpClient().execute(get, response -> {
+ StatusCode status = ResponseStatus.resolve(response.getCode())
+ .throwIf(HttpStatus.SC_TOO_MANY_REQUESTS, s -> new TooEagerPollingException())
+ .expect(StatusCodeFamily.SUCCESSFUL).orThrow(unexpectedStatus -> exceptionForGeneralError(response));
+ return new JobStatusResponse<>(status.value() == HttpStatus.SC_NO_CONTENT ? null : Marshalling.unmarshal(response.getEntity().getContent(), responseClass), getNextPermittedPollTime(response));
+ });
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
});
}
- private static Instant getNextPermittedPollTime(Response response) {
- return ZonedDateTime.parse(response.getHeaderString(NEXT_PERMITTED_POLL_TIME_HEADER), ISO_DATE_TIME).toInstant();
+ private static Instant getNextPermittedPollTime(ClassicHttpResponse response) throws ProtocolException {
+ return ZonedDateTime.parse(response.getHeader(NEXT_PERMITTED_POLL_TIME_HEADER).getValue(), ISO_DATE_TIME).toInstant();
}
public void confirm(final Confirmable confirmable) {
@@ -198,8 +274,10 @@ public void confirm(final Confirmable confirmable) {
if (confirmable.getConfirmationReference() != null) {
URI url = confirmable.getConfirmationReference().getConfirmationUrl();
LOG.info("Sends confirmation for '{}' to URL {}", confirmable, url);
- try (Response response = postEmptyEntity(url)) {
- ResponseStatus.resolve(response.getStatus()).expect(SUCCESSFUL).orThrow(status -> exceptionForGeneralError(response));
+ try (ClassicHttpResponse response = postEmptyEntity(url)) {
+ ResponseStatus.resolve(response.getCode()).expect(StatusCodeFamily.SUCCESSFUL).orThrow(status -> exceptionForGeneralError(response));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
} else {
LOG.info("Does not need to send confirmation for '{}'", confirmable);
@@ -219,8 +297,10 @@ public void deleteDocuments(DeleteDocumentsUrl deleteDocumentsUrl) {
call(() -> {
if (deleteDocumentsUrl != null) {
URI url = deleteDocumentsUrl.getUrl();
- try (Response response = delete(url)) {
- ResponseStatus.resolve(response.getStatus()).expect(SUCCESSFUL).orThrow(status -> exceptionForGeneralError(response));
+ try (ClassicHttpResponse response = delete(url)) {
+ ResponseStatus.resolve(response.getCode()).expect(StatusCodeFamily.SUCCESSFUL).orThrow(status -> exceptionForGeneralError(response));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
} else {
throw new DocumentsNotDeletableException();
@@ -228,90 +308,80 @@ public void deleteDocuments(DeleteDocumentsUrl deleteDocumentsUrl) {
});
}
+ private ClassicHttpResponse postEmptyEntity(URI uri) {
+ try {
+ ClassicHttpRequest request = ClassicRequestBuilder
+ .post(uri)
+ .addHeader(ACCEPT, APPLICATION_XML.getMimeType())
+ .build();
- private class UsingBodyParts {
-
- private final List parts;
-
- UsingBodyParts(BodyPart... parts) {
- this.parts = Arrays.asList(parts);
+ return httpClient.httpClient().execute(null, request);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
-
- T postAsMultiPart(String path, Class responseType) {
- try (MultiPart multiPart = new MultiPart()) {
- for (BodyPart bodyPart : parts) {
- multiPart.bodyPart(bodyPart);
- }
-
- Invocation.Builder request = httpClient.signatureServiceRoot().path(path)
- .request()
- .header(CONTENT_TYPE, multiPart.getMediaType())
- .accept(APPLICATION_XML_TYPE);
- try (Response response = request.post(Entity.entity(multiPart, multiPart.getMediaType()))) {
- return parseResponse(response, responseType);
- }
- } catch (IOException e) {
- throw new RuntimeIOException(e);
- }
- }
- }
-
- private Response postEmptyEntity(URI uri) {
- return postEntity(uri, null);
- }
-
- private Response postEntity(URI uri, Object entity) {
- Invocation.Builder requestBuilder = httpClient.target(uri)
- .request()
- .accept(APPLICATION_XML_TYPE);
- return (entity == null ? requestBuilder.header(CONTENT_LENGTH, 0) : requestBuilder)
- .post(Entity.entity(entity, APPLICATION_XML_TYPE));
}
+ private ClassicHttpResponse delete(URI uri) {
+ try {
+ ClassicHttpRequest request = ClassicRequestBuilder
+ .delete(uri)
+ .addHeader(ACCEPT, APPLICATION_XML.getMimeType())
+ .build();
- private Response delete(URI uri) {
- return httpClient.target(uri)
- .request()
- .accept(APPLICATION_XML_TYPE)
- .delete();
+ return httpClient.httpClient().execute(null, request);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
- private static T parseResponse(Response response, Class responseType) {
- ResponseStatus.resolve(response.getStatus()).expect(SUCCESSFUL).orThrow(unexpectedStatus -> exceptionForGeneralError(response));
- return response.readEntity(responseType);
+ private static T parseResponse(ClassicHttpResponse response, Class responseType) {
+ ResponseStatus.resolve(response.getCode()).expect(StatusCodeFamily.SUCCESSFUL).orThrow(unexpectedStatus -> exceptionForGeneralError(response));
+ try(InputStream body = response.getEntity().getContent()) {
+ return Marshalling.unmarshal(body, responseType);
+ } catch (IOException e) {
+ throw new UncheckedIOException("Could not parse response.", e);
+ }
}
- private static SignatureException exceptionForGeneralError(Response response) {
+ private static SignatureException exceptionForGeneralError(ClassicHttpResponse response) {
XMLError error = extractError(response);
if (BROKER_NOT_AUTHORIZED.sameAs(error.getErrorCode())) {
return new BrokerNotAuthorizedException(error);
}
- return new UnexpectedResponseException(error, ResponseStatus.resolve(response.getStatus()).get(), OK);
+ return new UnexpectedResponseException(error, ResponseStatus.resolve(response.getCode()).get(), StatusCode.from(HttpStatus.SC_OK));
}
- private static XMLError extractError(Response response) {
- XMLError error;
- Optional responseContentType = Optional.ofNullable(response.getHeaderString(HttpHeaders.CONTENT_TYPE));
- if (responseContentType.isPresent() && MediaType.valueOf(responseContentType.get()).equals(APPLICATION_XML_TYPE)) {
- try {
- response.bufferEntity();
- error = response.readEntity(XMLError.class);
- } catch (Exception e) {
+ private static XMLError extractError(ClassicHttpResponse response) {
+ try {
+ XMLError error;
+ Optional contentType = Optional.ofNullable(response.getHeader(CONTENT_TYPE)).map(NameValuePair::getValue).map(ContentType::parse);
+ if (contentType.filter(APPLICATION_XML::isSameMimeType).isPresent()) {
+ try(InputStream body = response.getEntity().getContent()) {
+ error = Marshalling.unmarshal(body, XMLError.class);
+ } catch (IOException e) {
+ throw new UncheckedIOException("Could not extract error from body.", e);
+ }
+ } else {
+ String errorAsString;
+ try(InputStream body = response.getEntity().getContent()) {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ for (int length; (length = body.read(buffer)) != -1; ) {
+ result.write(buffer, 0, length);
+ }
+ errorAsString = result.toString(StandardCharsets.UTF_8.name());
+ } catch (IOException e) {
+ throw new UncheckedIOException("Could not read body as string.", e);
+ }
throw new UnexpectedResponseException(
- HttpHeaders.CONTENT_TYPE + " " + responseContentType.orElse("unknown") + ": " +
- Optional.ofNullable(response.readEntity(String.class)).filter(StringUtils::isNoneBlank).orElse(""),
- e, ResponseStatus.resolve(response.getStatus()).get(), OK);
+ HttpHeaders.CONTENT_TYPE + " " + contentType.map(ContentType::getMimeType).orElse("unknown") + ": " +
+ Optional.ofNullable(errorAsString).filter(StringUtils::isNoneBlank).orElse(""),
+ ResponseStatus.resolve(response.getCode()).get(), StatusCode.from(HttpStatus.SC_OK));
}
- } else {
- throw new UnexpectedResponseException(
- HttpHeaders.CONTENT_TYPE + " " + responseContentType.orElse("unknown") + ": " +
- Optional.ofNullable(response.readEntity(String.class)).filter(StringUtils::isNoneBlank).orElse(""),
- ResponseStatus.resolve(response.getStatus()).get(), OK);
- }
- if (error == null) {
- throw new UnexpectedResponseException(null, ResponseStatus.resolve(response.getStatus()).get(), OK);
+ return error;
+ } catch (ProtocolException e) {
+ throw new RuntimeException(e);
}
- return error;
}
}
diff --git a/src/main/java/no/digipost/signature/client/core/internal/http/AddRequestHeaderFilter.java b/src/main/java/no/digipost/signature/client/core/internal/http/AddRequestHeaderFilter.java
deleted file mode 100644
index 8132be46..00000000
--- a/src/main/java/no/digipost/signature/client/core/internal/http/AddRequestHeaderFilter.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package no.digipost.signature.client.core.internal.http;
-
-import javax.annotation.Priority;
-import javax.ws.rs.client.ClientRequestContext;
-import javax.ws.rs.client.ClientRequestFilter;
-import java.io.IOException;
-
-import static javax.ws.rs.Priorities.HEADER_DECORATOR;
-
-
-@Priority(HEADER_DECORATOR)
-public class AddRequestHeaderFilter implements ClientRequestFilter {
-
- private final String headerName;
- private final String value;
-
- public AddRequestHeaderFilter(String headerName, String value) {
- this.headerName = headerName;
- this.value = value;
- }
-
- @Override
- public void filter(ClientRequestContext clientRequestContext) throws IOException {
- clientRequestContext.getHeaders().add(headerName, value);
- }
-
-}
diff --git a/src/main/java/no/digipost/signature/client/core/internal/http/HttpIntegrationConfiguration.java b/src/main/java/no/digipost/signature/client/core/internal/http/HttpIntegrationConfiguration.java
index e13a9af7..cca0b88d 100644
--- a/src/main/java/no/digipost/signature/client/core/internal/http/HttpIntegrationConfiguration.java
+++ b/src/main/java/no/digipost/signature/client/core/internal/http/HttpIntegrationConfiguration.java
@@ -1,17 +1,12 @@
package no.digipost.signature.client.core.internal.http;
-import javax.net.ssl.SSLContext;
-import javax.ws.rs.core.Configuration;
+import org.apache.hc.client5.http.classic.HttpClient;
import java.net.URI;
public interface HttpIntegrationConfiguration {
- String PRE_INIT_CLIENT = "no.posten.signering.client.preInit";
-
- Configuration getJaxrsConfiguration();
-
- SSLContext getSSLContext();
+ HttpClient httpClient();
URI getServiceRoot();
diff --git a/src/main/java/no/digipost/signature/client/core/internal/http/ResponseStatus.java b/src/main/java/no/digipost/signature/client/core/internal/http/ResponseStatus.java
index fb30e0ce..95d9c1f9 100644
--- a/src/main/java/no/digipost/signature/client/core/internal/http/ResponseStatus.java
+++ b/src/main/java/no/digipost/signature/client/core/internal/http/ResponseStatus.java
@@ -2,11 +2,6 @@
import no.digipost.signature.client.core.exceptions.UnexpectedResponseException;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.Response.Status.Family;
-import javax.ws.rs.core.Response.StatusType;
-
-import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -14,37 +9,30 @@
public final class ResponseStatus {
- public static ResponseStatus resolve(int code) {
- StatusType status = Status.fromStatusCode(code);
- if (status == null) {
- status = Custom.fromStatusCode(code);
- }
- if (status == null) {
- status = unknown(code);
- }
- return new ResponseStatus(status, s -> true);
+ public static ResponseStatus resolve(int statusCode) {
+ return new ResponseStatus(new StatusCode(statusCode), s -> true);
}
- private final StatusType status;
- private final Predicate statusExpectation;
+ private final StatusCode statusCode;
+ private final Predicate statusExpectation;
- private ResponseStatus(StatusType status, Predicate expectation) {
- this.status = status;
+ private ResponseStatus(StatusCode statusCode, Predicate expectation) {
+ this.statusCode = statusCode;
this.statusExpectation = expectation;
}
- public ResponseStatus expect(Status.Family expectedStatusFamily) {
- return expect(s -> s.getFamily() == expectedStatusFamily);
+ public ResponseStatus expect(StatusCodeFamily expectedStatusFamily) {
+ return expect(s -> s.is(expectedStatusFamily));
}
- public ResponseStatus expectOneOf(Status.Family ... expectedStatusFamilies) {
- return expectOneOf(Stream.of(expectedStatusFamilies), (family, status) -> status.getFamily() == family);
+ public ResponseStatus expectOneOf(StatusCodeFamily ... expectedStatusFamilies) {
+ return expectOneOf(Stream.of(expectedStatusFamilies), (family, statusCode) -> statusCode.is(family));
}
- private ResponseStatus expectOneOf(Stream expecteds, BiPredicate expectedEvaluator) {
- Predicate oneOfExpectedsAndAlsoExistingExpectation =
+ private ResponseStatus expectOneOf(Stream expecteds, BiPredicate expectedEvaluator) {
+ Predicate oneOfExpectedsAndAlsoExistingExpectation =
expecteds
- .map(expected -> (Predicate) status -> expectedEvaluator.test(expected, status))
+ .map(expected -> (Predicate) status -> expectedEvaluator.test(expected, status))
.reduce(Predicate::or)
.map(statusExpectation::and)
.orElse(statusExpectation);
@@ -52,153 +40,35 @@ private ResponseStatus expectOneOf(Stream expecteds, BiPredicate expectation) {
- return new ResponseStatus(status, this.statusExpectation.and(expectation));
+ public ResponseStatus expect(Predicate super StatusCode> expectation) {
+ return new ResponseStatus(statusCode, this.statusExpectation.and(expectation));
}
- public ResponseStatus throwIf(Status status, Function exceptionSupplier) throws X {
- return throwIf(s -> s.equals(status), exceptionSupplier);
+ public ResponseStatus throwIf(int status, Function exceptionSupplier) throws X {
+ return throwIf(s -> s.equals(StatusCode.from(status)), exceptionSupplier);
}
- public ResponseStatus throwIf(Predicate super StatusType> illegalStatus, Function exceptionSupplier) throws X {
- if (illegalStatus.test(status)) {
- throw exceptionSupplier.apply(status);
+ public ResponseStatus throwIf(Predicate super StatusCode> illegalStatus, Function exceptionSupplier) throws X {
+ if (illegalStatus.test(statusCode)) {
+ throw exceptionSupplier.apply(statusCode);
} else {
return this;
}
}
- public StatusType get() {
- return orThrow(s -> new UnexpectedResponseException(status));
+ public StatusCode get() {
+ return orThrow(s -> new UnexpectedResponseException(statusCode));
}
- public StatusType orThrow(Function exceptionSuppplier) throws X {
- return throwIf(statusExpectation.negate(), exceptionSuppplier).status;
+ public StatusCode orThrow(Function exceptionSupplier) throws X {
+ return throwIf(statusExpectation.negate(), exceptionSupplier).statusCode;
}
@Override
public String toString() {
- return status.toString() + (statusExpectation.test(status) ? "" : " (unexpected)");
- }
-
-
-
-
- /**
- * Status codes not part of the JAX-RS {@link Status} enum.
- */
- public enum Custom implements StatusType {
-
- /**
- * 422 Unprocessable Entity, see
- * https://tools.ietf.org/html/rfc4918#section-11.2
- */
- UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"),
-
- ;
-
- /**
- * Convert a numerical status code into the corresponding CustomStatus.
- *
- * @param code the numerical status code.
- * @return the matching Status or null is no matching Status is defined.
- */
- public static ResponseStatus.Custom fromStatusCode(int code) {
- for (Custom s : Custom.values()) {
- if (s.code == code) {
- return s;
- }
- }
- return null;
- }
-
-
-
- private int code;
- private String reason;
- private Family family;
-
- Custom(int code, String reasonPhrase) {
- this.code = code;
- this.reason = reasonPhrase;
- this.family = Family.familyOf(code);
- }
-
- @Override
- public int getStatusCode() {
- return code;
- }
-
- @Override
- public Family getFamily() {
- return family;
- }
-
- @Override
- public String getReasonPhrase() {
- return reason;
- }
-
- @Override
- public String toString() {
- return code + " " + reason;
- }
- }
-
-
-
- static StatusType unknown(int code) {
- return new Unknown(code);
- }
-
- public static final class Unknown implements StatusType {
-
- final int code;
- final Family family;
- final String reason;
-
- private Unknown(int code) {
- this.code = code;
- this.family = Family.familyOf(code);
- this.reason = "(" + family + ", unrecognized status code)";
- }
-
-
- @Override
- public int getStatusCode() {
- return code;
- }
-
- @Override
- public String getReasonPhrase() {
- return reason;
- }
-
- @Override
- public Family getFamily() {
- return family;
- }
-
- @Override
- public String toString() {
- return code + " " + reason;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof ResponseStatus.Unknown) {
- ResponseStatus.Unknown that = (ResponseStatus.Unknown) obj;
- return Objects.equals(this.code, that.code);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(code);
- }
+ return statusCode.toString() + (statusExpectation.test(statusCode) ? "" : " (unexpected)");
}
}
diff --git a/src/main/java/no/digipost/signature/client/core/internal/http/SignatureApiTrustStrategy.java b/src/main/java/no/digipost/signature/client/core/internal/http/SignatureApiTrustStrategy.java
index a3af6747..3ce6501a 100644
--- a/src/main/java/no/digipost/signature/client/core/internal/http/SignatureApiTrustStrategy.java
+++ b/src/main/java/no/digipost/signature/client/core/internal/http/SignatureApiTrustStrategy.java
@@ -3,7 +3,7 @@
import no.digipost.signature.client.core.exceptions.SecurityException;
import no.digipost.signature.client.security.CertificateChainValidation;
import no.digipost.signature.client.security.CertificateChainValidation.Result;
-import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.hc.core5.ssl.TrustStrategy;
import java.security.cert.X509Certificate;
@@ -35,7 +35,7 @@ public boolean isTrusted(X509Certificate[] chain, String authType) {
case TRUSTED_AND_SKIP_FURTHER_VALIDATION: return true;
case TRUSTED: return false;
case UNTRUSTED: default:
- String subjectDN = chain[0].getSubjectDN().getName();
+ String subjectDN = chain[0].getSubjectX500Principal().getName();
throw new SecurityException(
"Untrusted server certificate, according to " + certificateChainValidation + ". " +
"Make sure the server URI is correct. Actual certificate: " + subjectDN + ". " +
diff --git a/src/main/java/no/digipost/signature/client/core/internal/http/SignatureHttpClient.java b/src/main/java/no/digipost/signature/client/core/internal/http/SignatureHttpClient.java
index 3a6761cc..618e8836 100644
--- a/src/main/java/no/digipost/signature/client/core/internal/http/SignatureHttpClient.java
+++ b/src/main/java/no/digipost/signature/client/core/internal/http/SignatureHttpClient.java
@@ -1,13 +1,28 @@
package no.digipost.signature.client.core.internal.http;
-import javax.ws.rs.client.WebTarget;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.core5.net.URIBuilder;
import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.function.UnaryOperator;
public interface SignatureHttpClient {
- WebTarget signatureServiceRoot();
+ URI signatureServiceRoot();
- WebTarget target(URI uri);
+ HttpClient httpClient();
+
+ default URI constructUrl(UnaryOperator uri) {
+ URI serviceRoot = signatureServiceRoot();
+ URIBuilder uriBuilder = uri.apply(new URIBuilder());
+ try {
+ return uriBuilder.build();
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException(
+ "Invalid URL constructed for service at " + serviceRoot + ": " + uriBuilder + ". " +
+ "Reason: " + e.getClass().getSimpleName() + " '" + e.getMessage() + "'", e);
+ }
+ }
}
diff --git a/src/main/java/no/digipost/signature/client/core/internal/http/SignatureHttpClientFactory.java b/src/main/java/no/digipost/signature/client/core/internal/http/SignatureHttpClientFactory.java
index e0a638ae..7b017caa 100644
--- a/src/main/java/no/digipost/signature/client/core/internal/http/SignatureHttpClientFactory.java
+++ b/src/main/java/no/digipost/signature/client/core/internal/http/SignatureHttpClientFactory.java
@@ -1,76 +1,34 @@
package no.digipost.signature.client.core.internal.http;
-import org.apache.http.conn.ssl.NoopHostnameVerifier;
-import org.glassfish.jersey.client.JerseyClient;
-import org.glassfish.jersey.client.JerseyClientBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.Configuration;
+import org.apache.hc.client5.http.classic.HttpClient;
import java.net.URI;
-import static java.lang.Boolean.parseBoolean;
-import static no.digipost.signature.client.core.internal.http.HttpIntegrationConfiguration.PRE_INIT_CLIENT;
-
public class SignatureHttpClientFactory {
- private static final Logger LOG = LoggerFactory.getLogger(SignatureHttpClientFactory.class);
-
public static SignatureHttpClient create(HttpIntegrationConfiguration config) {
- Configuration jaxrsConfiguration = config.getJaxrsConfiguration();
- JerseyClientBuilder jerseyBuilder = (JerseyClientBuilder) JerseyClientBuilder.newBuilder();
- JerseyClient jerseyClient = jerseyBuilder
- .withConfig(jaxrsConfiguration)
- .sslContext(config.getSSLContext())
- .hostnameVerifier(NoopHostnameVerifier.INSTANCE)
- .build();
-
- Object configuredPreInit = jaxrsConfiguration.getProperty(PRE_INIT_CLIENT);
- if (configuredPreInit == null || parseBoolean(String.valueOf(configuredPreInit))) {
- try {
- jerseyClient.preInitialize();
- } catch (Exception e) {
- throw new IllegalStateException(
- "Unable to pre-initialize Jersey Client, because " + e.getClass().getSimpleName() + " '" + e.getMessage() + "'. " +
- "This step is taken to ensure everything the client needs is available already on instantiation, " +
- "in particular the InjectionManager facilities used internally by Jersey. By default, the Signature API Client " +
- "should include the Jersey HK2 implementation, but if you need to control this yourself, consider excluding " +
- "org.glassfish.jersey.inject:jersey-hk2 when depending on the signature-api-client-java, and make sure to make the " +
- "InjectionManagerFactory of your choice discoverable by Jersey.", e);
- }
- } else {
- LOG.warn(
- "Pre-initializing the Signature API Jersey Client is disabled, because of the property " +
- PRE_INIT_CLIENT + "=" + configuredPreInit + " is set in the JAX-RS configuration. " +
- "There is a chance that requests done by Jersey Client will break, and we don't yet know about it. " +
- "If this is intended configured behavior, this warning may be ignored.");
- }
- return new DefaultClient(jerseyClient, config.getServiceRoot());
+ return new DefaultClient(config.httpClient(), config.getServiceRoot());
}
private static final class DefaultClient implements SignatureHttpClient {
- private final Client jerseyClient;
- private final WebTarget signatureServiceRoot;
+ private final HttpClient httpClient;
+ private final URI signatureServiceRoot;
- DefaultClient(Client jerseyClient, URI root) {
- this.jerseyClient = jerseyClient;
- this.signatureServiceRoot = jerseyClient.target(root);
+ DefaultClient(HttpClient httpClient, URI root) {
+ this.httpClient = httpClient;
+ this.signatureServiceRoot = root;
}
@Override
- public WebTarget target(URI uri) {
- return jerseyClient.target(uri);
+ public URI signatureServiceRoot() {
+ return signatureServiceRoot;
}
- @Override
- public WebTarget signatureServiceRoot() {
- return signatureServiceRoot;
+ public HttpClient httpClient() {
+ return httpClient;
}
}
diff --git a/src/main/java/no/digipost/signature/client/core/internal/http/StatusCode.java b/src/main/java/no/digipost/signature/client/core/internal/http/StatusCode.java
new file mode 100644
index 00000000..d7d4409c
--- /dev/null
+++ b/src/main/java/no/digipost/signature/client/core/internal/http/StatusCode.java
@@ -0,0 +1,53 @@
+package no.digipost.signature.client.core.internal.http;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+public final class StatusCode {
+
+ private final int value;
+
+ public static StatusCode from(int value) {
+ return new StatusCode(value);
+ }
+
+ public StatusCode(int value) {
+ this.value = value;
+ }
+
+ public boolean is(StatusCodeFamily family) {
+ return this.family() == family;
+ }
+
+ public boolean isOneOf(StatusCodeFamily first, StatusCodeFamily ... rest) {
+ return isOneOf(EnumSet.of(first, rest));
+ }
+
+ public boolean isOneOf(Set families) {
+ return families.contains(this.family());
+ }
+
+ public StatusCodeFamily family() {
+ return StatusCodeFamily.of(value);
+ }
+
+ public int value() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ return "status code " + value + " (" + family() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof StatusCode && this.value == ((StatusCode) obj).value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Integer.hashCode(value);
+ }
+
+}
diff --git a/src/main/java/no/digipost/signature/client/core/internal/http/StatusCodeFamily.java b/src/main/java/no/digipost/signature/client/core/internal/http/StatusCodeFamily.java
new file mode 100644
index 00000000..be3dc0e5
--- /dev/null
+++ b/src/main/java/no/digipost/signature/client/core/internal/http/StatusCodeFamily.java
@@ -0,0 +1,22 @@
+package no.digipost.signature.client.core.internal.http;
+
+import org.apache.hc.core5.http.message.StatusLine;
+
+public enum StatusCodeFamily {
+ INFORMATIONAL, SUCCESSFUL, REDIRECTION, CLIENT_ERROR, SERVER_ERROR, OTHER;
+
+ public static StatusCodeFamily of(StatusLine apacheHttpClientStatus) {
+ return of(apacheHttpClientStatus.getStatusCode());
+ }
+
+ public static StatusCodeFamily of(int statusCode) {
+ switch (statusCode / 100) {
+ case 1: return INFORMATIONAL;
+ case 2: return SUCCESSFUL;
+ case 3: return REDIRECTION;
+ case 4: return CLIENT_ERROR;
+ case 5: return SERVER_ERROR;
+ default: return OTHER;
+ }
+ }
+}
diff --git a/src/main/java/no/digipost/signature/client/core/internal/xml/JaxbMessageReaderWriterProvider.java b/src/main/java/no/digipost/signature/client/core/internal/xml/JaxbMessageReaderWriterProvider.java
deleted file mode 100644
index 10f7dce6..00000000
--- a/src/main/java/no/digipost/signature/client/core/internal/xml/JaxbMessageReaderWriterProvider.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package no.digipost.signature.client.core.internal.xml;
-
-import org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.Produces;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.ext.Provider;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-
-import static no.digipost.signature.client.core.internal.xml.Marshalling.marshal;
-import static no.digipost.signature.client.core.internal.xml.Marshalling.unmarshal;
-
-@Provider
-@Produces(MediaType.APPLICATION_XML)
-@Consumes(MediaType.APPLICATION_XML)
-public class JaxbMessageReaderWriterProvider extends AbstractMessageReaderWriterProvider