class DataBridgeWebClient {

    @SuppressFBWarnings(value = "VA_FORMAT_STRING_USES_NEWLINE", justification = "We will use the standard carriage return character.")
    public BusinessObjectDataStorageFilesCreateResponse addStorageFiles(BusinessObjectDataKey businessObjectDataKey, UploaderInputManifestDto manifest,
        S3FileTransferRequestParamsDto s3FileTransferRequestParamsDto, String storageName)
        throws IOException, JAXBException, URISyntaxException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException
    {
        LOGGER.info("Adding storage files to the business object data ...");

        BusinessObjectDataStorageFilesCreateRequest request = new BusinessObjectDataStorageFilesCreateRequest();
        request.setNamespace(businessObjectDataKey.getNamespace());
        request.setBusinessObjectDefinitionName(businessObjectDataKey.getBusinessObjectDefinitionName());
        request.setBusinessObjectFormatUsage(businessObjectDataKey.getBusinessObjectFormatUsage());
        request.setBusinessObjectFormatFileType(businessObjectDataKey.getBusinessObjectFormatFileType());
        request.setBusinessObjectFormatVersion(businessObjectDataKey.getBusinessObjectFormatVersion());
        request.setPartitionValue(businessObjectDataKey.getPartitionValue());
        request.setSubPartitionValues(businessObjectDataKey.getSubPartitionValues());
        request.setBusinessObjectDataVersion(businessObjectDataKey.getBusinessObjectDataVersion());
        request.setStorageName(storageName);

        List<StorageFile> storageFiles = new ArrayList<>();
        request.setStorageFiles(storageFiles);

        String localPath = s3FileTransferRequestParamsDto.getLocalPath();
        String s3KeyPrefix = s3FileTransferRequestParamsDto.getS3KeyPrefix();
        List<ManifestFile> localFiles = manifest.getManifestFiles();

        for (ManifestFile manifestFile : localFiles)
        {
            StorageFile storageFile = new StorageFile();
            storageFiles.add(storageFile);
            // Since the S3 key prefix represents a directory it is expected to contain a trailing '/' character.
            storageFile.setFilePath((s3KeyPrefix + manifestFile.getFileName()).replaceAll("\\\\", "/"));
            storageFile.setFileSizeBytes(Paths.get(localPath, manifestFile.getFileName()).toFile().length());
            storageFile.setRowCount(manifestFile.getRowCount());
        }

        // Create a JAXB context and marshaller
        JAXBContext requestContext = JAXBContext.newInstance(BusinessObjectDataStorageFilesCreateRequest.class);
        Marshaller requestMarshaller = requestContext.createMarshaller();
        requestMarshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name());
        requestMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        StringWriter sw = new StringWriter();
        requestMarshaller.marshal(request, sw);

        BusinessObjectDataStorageFilesCreateResponse businessObjectDataStorageFilesCreateResponse;
        try (CloseableHttpClient client = httpClientHelper
            .createHttpClient(regServerAccessParamsDto.isTrustSelfSignedCertificate(), regServerAccessParamsDto.isDisableHostnameVerification()))
        {
            URI uri = new URIBuilder().setScheme(getUriScheme()).setHost(regServerAccessParamsDto.getRegServerHost())
                .setPort(regServerAccessParamsDto.getRegServerPort()).setPath(HERD_APP_REST_URI_PREFIX + "/businessObjectDataStorageFiles").build();
            HttpPost post = new HttpPost(uri);

            post.addHeader("Content-Type", DEFAULT_CONTENT_TYPE);
            post.addHeader("Accepts", DEFAULT_ACCEPT);

            // If SSL is enabled, set the client authentication header.
            if (regServerAccessParamsDto.isUseSsl())
            {
                post.addHeader(getAuthorizationHeader());
            }

            post.setEntity(new StringEntity(sw.toString()));

            LOGGER.info(String.format("    HTTP POST URI: %s", post.getURI().toString()));
            LOGGER.info(String.format("    HTTP POST Entity Content:%n%s", sw.toString()));

            // getBusinessObjectDataStorageFilesCreateResponse() might return a null. That happens when the web client gets status code 200 back from
            // the service (add storage files is a success), but it fails to retrieve or deserialize the actual HTTP response.
            // Please note that processXmlHttpResponse() is responsible for logging the exception info as a warning.
            businessObjectDataStorageFilesCreateResponse = getBusinessObjectDataStorageFilesCreateResponse(httpClientOperations.execute(client, post));
        }

        LOGGER.info("Successfully added storage files to the registered business object data.");

        return businessObjectDataStorageFilesCreateResponse;
    }

}