From 2e62099ae4ab1a46d7127fbf37f78edbcf6088de Mon Sep 17 00:00:00 2001 From: Syed Shahzeb Date: Fri, 9 Jan 2026 15:26:37 +0100 Subject: [PATCH] Rewrite CloseableHttpResponse to ClassicHttpResponse and use executeOpen When using CloseableHttpResponse and execute() with a response handler, the http connection was closed after the response handler terminated. Returning the http response was not feasible because the http connection was closed and thus so was the InputStream with the content of the response. Using executeOpen() keeps the connection open and makes it possible to return the response object for later use. The InputStream remains open and unconsumed. The caller, howevever, is now responsible for closing the InputStream and by extension also the Http connection. This is easily done as pointed out in the examples with a try-with-resources. --- .../digipost/api/client/DigipostClient.java | 6 +-- .../api/client/archive/ArchiveApi.java | 4 +- .../client/delivery/MessageDeliveryApi.java | 14 ++--- .../api/client/internal/ApiServiceImpl.java | 54 +++++++------------ .../internal/delivery/ArchiveDeliverer.java | 3 +- .../internal/delivery/MessageDeliverer.java | 12 ++--- .../http/response/HttpResponseUtils.java | 4 +- .../shareddocuments/SharedDocumentsApi.java | 4 +- .../GithubPagesArchiveExamples.java | 7 ++- .../GithubPagesReceiveExamples.java | 4 +- 10 files changed, 52 insertions(+), 60 deletions(-) diff --git a/src/main/java/no/digipost/api/client/DigipostClient.java b/src/main/java/no/digipost/api/client/DigipostClient.java index f0cc2b35..7faaf5fd 100644 --- a/src/main/java/no/digipost/api/client/DigipostClient.java +++ b/src/main/java/no/digipost/api/client/DigipostClient.java @@ -57,8 +57,8 @@ import no.digipost.api.client.tag.TagApi; import no.digipost.api.client.util.JAXBContextUtils; import no.digipost.http.client.HttpClientFactory; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -145,7 +145,7 @@ public OngoingDelivery.ForPrintOnly createPrintOnlyMessage(final Message printMe } public IdentificationResult identifyRecipient(final Identification identification) { - try (CloseableHttpResponse response = messageApi.identifyRecipient(identification)) { + try (ClassicHttpResponse response = messageApi.identifyRecipient(identification)) { checkResponse(response, eventLogger); return JAXBContextUtils.unmarshal(jaxbContext, response.getEntity().getContent(), IdentificationResult.class); } catch (IOException e) { @@ -330,7 +330,7 @@ public SharedDocumentContent getSharedDocumentContent(URI uri) { return sharedDocumentsApi.getSharedDocumentContent(uri); } - public CloseableHttpResponse stopSharing(SenderId senderId, URI uri) { + public ClassicHttpResponse stopSharing(SenderId senderId, URI uri) { return sharedDocumentsApi.stopSharing(senderId, uri); } diff --git a/src/main/java/no/digipost/api/client/archive/ArchiveApi.java b/src/main/java/no/digipost/api/client/archive/ArchiveApi.java index d1c3c987..0432087c 100644 --- a/src/main/java/no/digipost/api/client/archive/ArchiveApi.java +++ b/src/main/java/no/digipost/api/client/archive/ArchiveApi.java @@ -20,7 +20,7 @@ import no.digipost.api.client.representations.archive.ArchiveDocument; import no.digipost.api.client.representations.archive.ArchiveDocumentContent; import no.digipost.api.client.representations.archive.Archives; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import java.io.ByteArrayInputStream; @@ -32,7 +32,7 @@ public interface ArchiveApi { Archives getArchives(SenderId senderId); - CloseableHttpResponse sendMultipartArchive(HttpEntity build); + ClassicHttpResponse sendMultipartArchive(HttpEntity build); Archive getArchiveDocuments(URI uri); diff --git a/src/main/java/no/digipost/api/client/delivery/MessageDeliveryApi.java b/src/main/java/no/digipost/api/client/delivery/MessageDeliveryApi.java index e0119a2e..92aa9ee0 100644 --- a/src/main/java/no/digipost/api/client/delivery/MessageDeliveryApi.java +++ b/src/main/java/no/digipost/api/client/delivery/MessageDeliveryApi.java @@ -25,7 +25,7 @@ import no.digipost.api.client.representations.accounts.UserAccount; import no.digipost.api.client.representations.accounts.UserInformation; import no.digipost.api.client.representations.sender.SenderInformation; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import java.net.URI; @@ -49,19 +49,19 @@ public interface MessageDeliveryApi { /** * Oppretter og sender en multipartforsendelse */ - CloseableHttpResponse sendMultipartMessage(HttpEntity multipart); + ClassicHttpResponse sendMultipartMessage(HttpEntity multipart); /** * Legger til ytterligere data til et dokument. * Det er en forutsetning at dokumentet har datatype fra tidligere. */ - CloseableHttpResponse addData(AddDataLink document, AdditionalData data); + ClassicHttpResponse addData(AddDataLink document, AdditionalData data); Recipients search(String searchString); Autocomplete searchSuggest(String searchString); - CloseableHttpResponse identifyRecipient(Identification identification); + ClassicHttpResponse identifyRecipient(Identification identification); /** * Sjekker hvis spesifisert mottaker er Digipost-bruker. @@ -69,15 +69,15 @@ public interface MessageDeliveryApi { * Nøkkelen brukes for å kryptere dokument-innhold for dokumenter som * skal prekrypteres. */ - CloseableHttpResponse identifyAndGetEncryptionKey(Identification identification); + ClassicHttpResponse identifyAndGetEncryptionKey(Identification identification); - CloseableHttpResponse getEncryptionKey(URI location); + ClassicHttpResponse getEncryptionKey(URI location); /** * Henter public krypteringsnøkkel i x509 format for forsendelser som skal sendes til print. */ - CloseableHttpResponse getEncryptionCertificateForPrint(); + ClassicHttpResponse getEncryptionCertificateForPrint(); /** * Henter informasjon om en faktisk avsender av en melding, altså diff --git a/src/main/java/no/digipost/api/client/internal/ApiServiceImpl.java b/src/main/java/no/digipost/api/client/internal/ApiServiceImpl.java index 3c0c7e3e..fa160b47 100644 --- a/src/main/java/no/digipost/api/client/internal/ApiServiceImpl.java +++ b/src/main/java/no/digipost/api/client/internal/ApiServiceImpl.java @@ -76,15 +76,14 @@ import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpPut; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; 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.Header; 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.io.HttpClientResponseHandler; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; @@ -164,7 +163,7 @@ public EntryPoint getEntryPoint() { @Override - public CloseableHttpResponse sendMultipartMessage(HttpEntity multipart) { + public ClassicHttpResponse sendMultipartMessage(HttpEntity multipart) { MultipartNoLengthCheckHttpEntity multipartLengthCheckHttpEntity = new MultipartNoLengthCheckHttpEntity(multipart); EntryPoint entryPoint = getEntryPoint(); @@ -179,7 +178,7 @@ public CloseableHttpResponse sendMultipartMessage(HttpEntity multipart) { } @Override - public CloseableHttpResponse sendMultipartArchive(HttpEntity multipart) { + public ClassicHttpResponse sendMultipartArchive(HttpEntity multipart) { MultipartNoLengthCheckHttpEntity multipartLengthCheckHttpEntity = new MultipartNoLengthCheckHttpEntity(multipart); EntryPoint entryPoint = getEntryPoint(); @@ -213,20 +212,20 @@ public InputStream getArchiveDocumentContentStream(URI uri) { } @Override - public CloseableHttpResponse identifyAndGetEncryptionKey(Identification identification) { + public ClassicHttpResponse identifyAndGetEncryptionKey(Identification identification) { EntryPoint entryPoint = getEntryPoint(); return sendDigipostMedia(identification, entryPoint.getIdentificationWithEncryptionKeyUri().getPath()); } @Override - public CloseableHttpResponse getEncryptionKey(URI location) { + public ClassicHttpResponse getEncryptionKey(URI location) { HttpGet httpGet = new HttpGet(location); httpGet.setHeader(Accept_DIGIPOST_MEDIA_TYPE_V8); return send(httpGet); } @Override - public CloseableHttpResponse getEncryptionCertificateForPrint() { + public ClassicHttpResponse getEncryptionCertificateForPrint() { EntryPoint entryPoint = getEntryPoint(); HttpGet httpGet = new HttpGet(digipostUrl.resolve(entryPoint.getPrintEncryptionCertificate().getPath())); @@ -235,7 +234,7 @@ public CloseableHttpResponse getEncryptionCertificateForPrint() { } @Override - public CloseableHttpResponse addData(AddDataLink addDataLink, AdditionalData data) { + public ClassicHttpResponse addData(AddDataLink addDataLink, AdditionalData data) { return sendDigipostMedia(data, addDataLink.getPath()); } @@ -300,7 +299,7 @@ public Autocomplete searchSuggest(String searchString) { } @Override - public CloseableHttpResponse identifyRecipient(Identification identification) { + public ClassicHttpResponse identifyRecipient(Identification identification) { return sendDigipostMedia(identification, getEntryPoint().getIdentificationUri().getPath()); } @@ -309,7 +308,7 @@ private EntryPoint fetchEntryPoint(Optional senderId) throws IOExcepti httpGet.setHeader(Accept_DIGIPOST_MEDIA_TYPE_V8); final HttpCoreContext httpCoreContext = HttpCoreContext.create(); httpCoreContext.setAttribute(ResponseSignatureInterceptor.NOT_SIGNED_RESPONSE, true); - try (CloseableHttpResponse response = send(httpGet, httpCoreContext)) { + try (ClassicHttpResponse response = send(httpGet, httpCoreContext)) { if (response.getCode() == HttpStatus.SC_OK) { return unmarshal(jaxbContext, response.getEntity().getContent(), EntryPoint.class); @@ -391,7 +390,7 @@ public Archive addUniqueUUIDToArchiveDocument(SenderId senderId, UUID uuid, UUID newuuid, document.getFileName(), document.getFileType(), document.getContentType() ); - try (CloseableHttpResponse response = sendDigipostMedia(nyttDokument, addUniqeUUIDUri.getPath())) { + try (ClassicHttpResponse response = sendDigipostMedia(nyttDokument, addUniqeUUIDUri.getPath())) { checkResponse(response, eventLogger); archive.getDocuments().addAll(unmarshal(jaxbContext, response.getEntity().getContent(), Archive.class).getDocuments()); @@ -450,7 +449,7 @@ public UserAccount createOrActivateUserAccount(SenderId senderId, UserInformatio @Override public Batch createBatch(UUID batchUUID) { final URI createBatch = getEntryPoint().getCreateBatch(); - try (CloseableHttpResponse response = sendDigipostMedia(new Batch(batchUUID.toString()), createBatch.toString())) { + try (ClassicHttpResponse response = sendDigipostMedia(new Batch(batchUUID.toString()), createBatch.toString())) { checkResponse(response, eventLogger); return JAXBContextUtils.unmarshal(jaxbContext, response.getEntity().getContent(), Batch.class); } catch (IOException e) { @@ -478,7 +477,7 @@ public void cancelBatch(Batch batch) { @Override public void addTag(Tag tag) { URI uri = getEntryPoint().getAddTagUri(); - try (CloseableHttpResponse response = sendDigipostMedia(tag, uri.getPath())) { + try (ClassicHttpResponse response = sendDigipostMedia(tag, uri.getPath())) { checkResponse(response, eventLogger); } catch (IOException e) { throw new DigipostClientException(ErrorCode.GENERAL_ERROR, e); @@ -488,7 +487,7 @@ public void addTag(Tag tag) { @Override public void removeTag(Tag tag) { URI uri = getEntryPoint().getRemoveTagUri(); - try (CloseableHttpResponse response = sendDigipostMedia(tag, uri.getPath())) { + try (ClassicHttpResponse response = sendDigipostMedia(tag, uri.getPath())) { checkResponse(response, eventLogger); } catch (IOException e) { throw new DigipostClientException(ErrorCode.GENERAL_ERROR, e); @@ -522,7 +521,7 @@ public SharedDocumentContent getSharedDocumentContent(URI uri) { } @Override - public CloseableHttpResponse stopSharing(SenderId senderId, URI uri) { + public ClassicHttpResponse stopSharing(SenderId senderId, URI uri) { DataType dataType = new ShareDocumentsRequestSharingStopped(); AdditionalData data = AdditionalData.Builder .newAdditionalData(dataType) @@ -570,7 +569,7 @@ private R request(ClassicHttpRequest request, Class entityType, Header .. R responseStream = (R) safelyOfferEntityStreamExternally(send(request), eventLogger); return responseStream; } else { - try (CloseableHttpResponse response = send(request)) { + try (ClassicHttpResponse response = send(request)) { checkResponse(response, eventLogger); return unmarshal(response.getEntity().getContent(), entityType); } catch (IOException e) { @@ -580,24 +579,20 @@ private R request(ClassicHttpRequest request, Class entityType, Header .. } - private CloseableHttpResponse send(ClassicHttpRequest request){ + private ClassicHttpResponse send(ClassicHttpRequest request) { return send(request, null); } - private CloseableHttpResponse send(ClassicHttpRequest request, HttpContext context){ + private ClassicHttpResponse send(ClassicHttpRequest request, HttpContext context) { try { request.setHeader(X_Digipost_UserId, brokerId.stringValue()); - if (context == null) { - return httpClient.execute(request, responseHandler()); - } else { - return httpClient.execute(request, context, responseHandler()); - } + return httpClient.executeOpen(null, request, context); } catch (IOException e) { throw asUnchecked(e); } } - private CloseableHttpResponse sendDigipostMedia(Object data, String uri) { + private ClassicHttpResponse sendDigipostMedia(Object data, String uri) { HttpPost httpPost = new HttpPost(digipostUrl.resolve(uri)); httpPost.setHeader(Accept_DIGIPOST_MEDIA_TYPE_V8); httpPost.setHeader(Content_Type_DIGIPOST_MEDIA_TYPE_V8); @@ -606,15 +601,4 @@ private CloseableHttpResponse sendDigipostMedia(Object data, String uri) { httpPost.setEntity(new ByteArrayEntity(bao.toByteArray(), ContentType.create(DIGIPOST_MEDIA_TYPE_V8))); return send(httpPost); } - - private HttpClientResponseHandler responseHandler() { - return response -> { - if (response instanceof CloseableHttpResponse) { - return (CloseableHttpResponse) response; - } else { - throw new DigipostClientException(ErrorCode.GENERAL_ERROR, - "Expected response to be instance of CloseableHttpResponse, but got " + response.getClass().getName()); - } - }; - } } diff --git a/src/main/java/no/digipost/api/client/internal/delivery/ArchiveDeliverer.java b/src/main/java/no/digipost/api/client/internal/delivery/ArchiveDeliverer.java index 669478b4..32b3a524 100644 --- a/src/main/java/no/digipost/api/client/internal/delivery/ArchiveDeliverer.java +++ b/src/main/java/no/digipost/api/client/internal/delivery/ArchiveDeliverer.java @@ -30,6 +30,7 @@ import org.apache.hc.client5.http.entity.mime.HttpMultipartMode; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,7 +100,7 @@ public Archive sendMultipartMessage(Archive archive, Map .addField("Content-Disposition", "attachment;" + " filename=\"" + document.uuid.toString() + "\"").build()); } eventLogger.log("*** STARTER INTERAKSJON MED API: Arkiverer filer ***"); - try (CloseableHttpResponse response = apiService.sendMultipartArchive(multipartEntity.build())) { + try (ClassicHttpResponse response = apiService.sendMultipartArchive(multipartEntity.build())) { checkResponse(response, eventLogger); eventLogger.log("Arkivdokumentet ble sendt. Status: [" + response + "]"); diff --git a/src/main/java/no/digipost/api/client/internal/delivery/MessageDeliverer.java b/src/main/java/no/digipost/api/client/internal/delivery/MessageDeliverer.java index 616ef5b8..860bfcca 100644 --- a/src/main/java/no/digipost/api/client/internal/delivery/MessageDeliverer.java +++ b/src/main/java/no/digipost/api/client/internal/delivery/MessageDeliverer.java @@ -44,7 +44,7 @@ import org.apache.hc.client5.http.entity.mime.FormBodyPartBuilder; import org.apache.hc.client5.http.entity.mime.HttpMultipartMode; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -155,7 +155,7 @@ public MessageDelivery sendMultipartMessage(Message message, Map 0) { eventLogger.log("*** STARTER INTERAKSJON MED API: HENT KRYPTERINGSNØKKEL FOR PRINT ***"); - try (CloseableHttpResponse response = apiService.getEncryptionCertificateForPrint()) { + try (ClassicHttpResponse response = apiService.getEncryptionCertificateForPrint()) { checkResponse(response, eventLogger); EncryptionCertificate encryptionCertificate = unmarshal(jaxbContext, response.getEntity().getContent(), EncryptionCertificate.class); cachedPrintCertificate = encryptionCertificate.getX509Certificate(); diff --git a/src/main/java/no/digipost/api/client/internal/http/response/HttpResponseUtils.java b/src/main/java/no/digipost/api/client/internal/http/response/HttpResponseUtils.java index 22452a37..dd1e1fbf 100644 --- a/src/main/java/no/digipost/api/client/internal/http/response/HttpResponseUtils.java +++ b/src/main/java/no/digipost/api/client/internal/http/response/HttpResponseUtils.java @@ -50,14 +50,14 @@ public final class HttpResponseUtils { * * @return the stream containing the entity of the response. */ - public static InputStream safelyOfferEntityStreamExternally(CloseableHttpResponse response, EventLogger eventLogger) { + public static InputStream safelyOfferEntityStreamExternally(ClassicHttpResponse response, EventLogger eventLogger) { HttpEntity entity = null; try { checkResponse(response, eventLogger); entity = response.getEntity(); return entity.getContent(); } catch (IOException | RuntimeException e) { - try (CloseableHttpResponse autoClosed = response) { + try (ClassicHttpResponse autoClosed = response) { EntityUtils.consume(entity); } catch (IOException | RuntimeException entityConsumptionException) { e.addSuppressed(entityConsumptionException); diff --git a/src/main/java/no/digipost/api/client/shareddocuments/SharedDocumentsApi.java b/src/main/java/no/digipost/api/client/shareddocuments/SharedDocumentsApi.java index 8ebfdd82..860d8be5 100644 --- a/src/main/java/no/digipost/api/client/shareddocuments/SharedDocumentsApi.java +++ b/src/main/java/no/digipost/api/client/shareddocuments/SharedDocumentsApi.java @@ -18,7 +18,7 @@ import no.digipost.api.client.SenderId; import no.digipost.api.client.representations.shareddocuments.ShareDocumentsRequestState; import no.digipost.api.client.representations.shareddocuments.SharedDocumentContent; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import java.io.InputStream; import java.net.URI; @@ -58,6 +58,6 @@ public interface SharedDocumentsApi { * * @param uri The URI of the StopSharing relation returned as part of the ShareDocumentsRequestState */ - CloseableHttpResponse stopSharing(SenderId senderId, URI uri); + ClassicHttpResponse stopSharing(SenderId senderId, URI uri); } diff --git a/src/test/java/no/digipost/api/client/eksempelkode/GithubPagesArchiveExamples.java b/src/test/java/no/digipost/api/client/eksempelkode/GithubPagesArchiveExamples.java index bd3e68e9..3aaf7ba1 100644 --- a/src/test/java/no/digipost/api/client/eksempelkode/GithubPagesArchiveExamples.java +++ b/src/test/java/no/digipost/api/client/eksempelkode/GithubPagesArchiveExamples.java @@ -116,7 +116,12 @@ public void get_documents_by_referenceid_with_content() { URI getDocumentContentStreamURI = firstDocument.getDocumentContentStream(); ArchiveDocumentContent content = client.getArchiveDocumentContent(getDocumentContentURI); - InputStream contentStream = client.getArchiveDocumentContentStream(getDocumentContentStreamURI); + try (InputStream contentStream = client.getArchiveDocumentContentStream(getDocumentContentStreamURI)) { + // use contentStream + } catch (IOException e) { + // handle exception + throw new RuntimeException(e); + } } public void get_documents_by_uuid() { diff --git a/src/test/java/no/digipost/api/client/eksempelkode/GithubPagesReceiveExamples.java b/src/test/java/no/digipost/api/client/eksempelkode/GithubPagesReceiveExamples.java index acee4e96..c2b7dbfe 100644 --- a/src/test/java/no/digipost/api/client/eksempelkode/GithubPagesReceiveExamples.java +++ b/src/test/java/no/digipost/api/client/eksempelkode/GithubPagesReceiveExamples.java @@ -55,7 +55,9 @@ public void download_document_content() throws IOException { InboxDocument documentMetadata = inbox.documents.get(0); System.out.println("Content type is: " + documentMetadata.getContentType()); - InputStream documentContent = client.getInboxDocumentContent(documentMetadata); + try (InputStream documentContent = client.getInboxDocumentContent(documentMetadata)) { + // use documentContent + } } public void delete_document() throws IOException {