All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.mklinger.qetcher.client.impl.QetcherClientImpl Maven / Gradle / Ivy

There is a newer version: 2.0.42.rc
Show newest version
package de.mklinger.qetcher.client.impl;

import static de.mklinger.qetcher.client.impl.Parameters.*;

import java.net.URI;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.mklinger.commons.httpclient.BodyHandlers;
import de.mklinger.commons.httpclient.BodyProviders;
import de.mklinger.commons.httpclient.HttpClient;
import de.mklinger.commons.httpclient.HttpRequest;
import de.mklinger.commons.httpclient.HttpResponse;
import de.mklinger.commons.httpclient.HttpResponse.BodyHandler;
import de.mklinger.micro.annotations.VisibleForTesting;
import de.mklinger.qetcher.client.InputConversionFile;
import de.mklinger.qetcher.client.InputJob;
import de.mklinger.qetcher.client.QetcherClientVersion;
import de.mklinger.qetcher.client.impl.lookup.ServiceUriSupplier;
import de.mklinger.qetcher.client.model.v1.AvailableConversion;
import de.mklinger.qetcher.client.model.v1.AvailableConversions;
import de.mklinger.qetcher.client.model.v1.AvailableNode;
import de.mklinger.qetcher.client.model.v1.AvailableNodes;
import de.mklinger.qetcher.client.model.v1.Builders;
import de.mklinger.qetcher.client.model.v1.ConversionFile;
import de.mklinger.qetcher.client.model.v1.ConversionFiles;
import de.mklinger.qetcher.client.model.v1.FileExtensionInfos;
import de.mklinger.qetcher.client.model.v1.Job;
import de.mklinger.qetcher.client.model.v1.JobPatch;
import de.mklinger.qetcher.client.model.v1.Jobs;
import de.mklinger.qetcher.client.model.v1.MediaTypeInfo;
import de.mklinger.qetcher.client.model.v1.MediaTypeInfos;

/**
 * Qetcher client implementation that uses
 * {@link de.mklinger.commons.httpclient.HttpClient mklinger httpclient}
 * as underlying HTTP client implementation.
 *
 * 

* This implementation supports asynchronous HTTP/2 with JDK8 and above. *

* * @author Marc Klinger - mklinger[at]mklinger[dot]de */ public class QetcherClientImpl extends AbstractQetcherClient { private static final String USER_AGENT = "User-Agent"; private static final String USER_AGENT_VALUE = "qetcher-client/" + QetcherClientVersion.getVersion(); private static final Logger LOG = LoggerFactory.getLogger(QetcherClientImpl.class); private final HttpClient httpClient; public QetcherClientImpl(final QetcherClientBuilderImpl builder, final ServiceUriSupplier serviceUriSupplier) { super(serviceUriSupplier); this.httpClient = newHttpClient(builder); } @VisibleForTesting protected HttpClient newHttpClient(final QetcherClientBuilderImpl builder) { final HttpClient.Builder httpClientBuilder = HttpClient.newBuilder(); if (builder.getTrustStore() != null) { httpClientBuilder.trustStore(builder.getTrustStore()); } if (builder.getKeyStore() != null) { httpClientBuilder.keyStore(builder.getKeyStore(), builder.getKeyPassword()); } return httpClientBuilder .name("QetcherClient") .followRedirects(true) .build(); } @Override public void close() { httpClient.close(); } @Override public CompletableFuture uploadFile(final InputConversionFile inputFile) { final HttpRequest.Builder rb = newRequestBuilder(getFileUploadUri()) .method(getFileUploadMethod(), inputFile.getBodyProvider()); rb.header(FROM_MEDIA_TYPE_HEADER, inputFile.getMediaType().toString()); if (inputFile.getDeleteTimeout() != null) { rb.header(DELETE_TIMEOUT_HEADER, inputFile.getDeleteTimeout().toString()); } if (inputFile.getFilename() != null && !inputFile.getFilename().isEmpty()) { rb.header(FILENAME_HEADER, inputFile.getFilename()); } return sendForBytes(rb.build()) .thenApply(response -> transformResponse(response, ConversionFile.class)); } @Override public CompletableFuture getFile(final String fileId) { final HttpRequest request = newRequestBuilder(getFileUri(fileId)) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, ConversionFile.class)); } @Override public CompletableFuture> getFiles() { final HttpRequest request = newRequestBuilder(getFilesUri()) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, ConversionFiles.class)) .thenApply(ConversionFiles::getConversionFiles); } @Override public CompletableFuture deleteFile(final String fileId) { final HttpRequest request = newRequestBuilder(getFileUri(fileId)) .DELETE(BodyProviders.noBody()) .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, Void.class)); } @Override public CompletableFuture downloadAsFile(final String fileId, final Path file, final OpenOption... openOptions) { final HttpRequest request = newRequestBuilder(getFileContentsUri(fileId)) .GET() .build(); if (LOG.isDebugEnabled()) { LOG.debug("{} {}", request.method(), request.uri()); } return httpClient .sendAsync(request, asSuccessStatusFile(file, openOptions)) .thenApply(HttpResponse::body); } private BodyHandler asSuccessStatusFile(final Path file, final OpenOption... openOptions) { return (status, headers) -> { requireSuccessStatusCode(status, Optional.empty(), Optional.empty()); return BodyHandlers.asFile(file, openOptions).apply(status, headers); }; } @Override public CompletableFuture downloadAsByteArray(String fileId) { final HttpRequest request = newRequestBuilder(getFileContentsUri(fileId)) .GET() .build(); if (LOG.isDebugEnabled()) { LOG.debug("{} {}", request.method(), request.uri()); } return httpClient .sendAsync(request, asSuccessStatusByteArray()) .thenApply(HttpResponse::body); } private BodyHandler asSuccessStatusByteArray() { return (status, headers) -> { requireSuccessStatusCode(status, Optional.empty(), Optional.empty()); return BodyHandlers.asByteArray().apply(status, headers); }; } @Override public CompletableFuture createJob(final InputJob inputJob) { if (inputJob.getInputConversionFile() != null) { return createJobWithUpload(inputJob); } else { return createJobForExistingFile(inputJob); } } private CompletableFuture createJobWithUpload(final InputJob inputJob) { final HttpRequest.Builder rb = newRequestBuilder(getCreateJobForNewFileUri()) .method(getFileUploadMethod(), inputJob.getInputConversionFile().getBodyProvider()); rb.header(FROM_MEDIA_TYPE_HEADER, inputJob.getFromMediaType().toString()); rb.header(TO_MEDIA_TYPE_HEADER, inputJob.getToMediaType().toString()); if (inputJob.getInputConversionFile().getFilename() != null && !inputJob.getInputConversionFile().getFilename().isEmpty()) { rb.header(FILENAME_HEADER, inputJob.getInputConversionFile().getFilename()); } if (inputJob.getDeleteTimeout() != null) { rb.header(DELETE_TIMEOUT_HEADER, inputJob.getDeleteTimeout().toString()); } if (inputJob.getCancelTimeout() != null) { rb.header(CANCEL_TIMEOUT_HEADER, inputJob.getCancelTimeout().toString()); } if (inputJob.getReferrer() != null) { rb.header(REFERRER_HEADER, inputJob.getReferrer()); } return sendForBytes(rb.build()) .thenApply(response -> transformResponse(response, Job.class)); } private CompletableFuture createJobForExistingFile(final InputJob inputJob) { final HttpRequest.Builder rb = newRequestBuilder(getCreateJobForExistingFileUri()) .method(getCreateJobForExistingFileMethod(), BodyProviders.noBody()); rb.header(CONVERSION_FILE_ID_HEADER, inputJob.getConversionFileIds() .stream() .collect(Collectors.joining(","))); rb.header(TO_MEDIA_TYPE_HEADER, inputJob.getToMediaType().toString()); if (inputJob.getFromMediaType() != null) { // From media type is optional here. If not given explicitly, the media type // from the first input file is used rb.header(FROM_MEDIA_TYPE_HEADER, inputJob.getFromMediaType().toString()); } if (inputJob.getDeleteTimeout() != null) { rb.header(DELETE_TIMEOUT_HEADER, inputJob.getDeleteTimeout().toString()); } if (inputJob.getCancelTimeout() != null) { rb.header(CANCEL_TIMEOUT_HEADER, inputJob.getCancelTimeout().toString()); } if (inputJob.getReferrer() != null) { rb.header(REFERRER_HEADER, inputJob.getReferrer()); } return sendForBytes(rb.build()) .thenApply(response -> transformResponse(response, Job.class)); } @Override public CompletableFuture getJob(final String jobId) { final HttpRequest request = newRequestBuilder(getJobUri(jobId)) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, Job.class)); } @Override public CompletableFuture> getJobs() { final HttpRequest request = newRequestBuilder(getJobsUri()) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, Jobs.class)) .thenApply(Jobs::getJobs); } @Override public CompletableFuture deleteJob(final String jobId) { final HttpRequest request = newRequestBuilder(getJobUri(jobId)) .DELETE(BodyProviders.noBody()) .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, Void.class)); } @Override public CompletableFuture cancelJob(String jobId) { final JobPatch jobPatch = Builders.jobPatch() .cancel() .build(); final HttpRequest request = newRequestBuilder(getJobUri(jobId)) .method("PATCH", transformRequest(jobPatch)) .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, Void.class)); } @Override public CompletableFuture> getAvailableConversions() { final HttpRequest request = newRequestBuilder(getConversionsUri()) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, AvailableConversions.class)) .thenApply(AvailableConversions::getAvailableConversions); } @Override public CompletableFuture> getAvailableNodes() { final URI uri = getAvailableNodesUri(); final HttpRequest request = newRequestBuilder(uri) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, AvailableNodes.class)) .thenApply(AvailableNodes::getAvailableNodes); } @Override public CompletableFuture> getMediaTypes() { final URI uri = getMediaTypesUri(); final HttpRequest request = newRequestBuilder(uri) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, MediaTypeInfos.class)) .thenApply(MediaTypeInfos::getMediaTypeInfos); } @Override public CompletableFuture getFileExtensions() { final URI uri = getFileExtensionsUri(); final HttpRequest request = newRequestBuilder(uri) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, FileExtensionInfos.class)); } @Override public CompletableFuture getMediaTypeForFilename(final String filename) { final URI uri = getMediaTypeForFilenameUri(filename); final HttpRequest request = newRequestBuilder(uri) .GET() .build(); return sendForBytes(request) .thenApply(response -> transformResponse(response, MediaTypeInfo.class)); } private HttpRequest.Builder newRequestBuilder(final URI uri) { return HttpRequest.newBuilder() .uri(uri) .header(USER_AGENT, USER_AGENT_VALUE); } private CompletableFuture> sendForBytes(final HttpRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("{} {}", request.method(), request.uri()); } return httpClient.sendAsync(request, BodyHandlers.asByteArray()); } private T transformResponse(final HttpResponse response, final Class type) { return transformResponse( response.statusCode(), response.headers().firstValue("Content-Type"), response.body(), type); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy