package io.blobkeeper.client.service; /* * Copyright (C) 2015-2017 by Denis M. Gabaydulin * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.util.concurrent.AbstractService; import io.blobkeeper.common.domain.api.*; import org.asynchttpclient.*; import org.asynchttpclient.channel.DefaultKeepAliveStrategy; import org.asynchttpclient.request.body.multipart.FilePart; import org.asynchttpclient.request.body.multipart.StringPart; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Map; import java.util.concurrent.ExecutionException; import static com.google.common.base.Preconditions.checkArgument; import static io.blobkeeper.common.util.MetadataUtils.AUTH_TOKEN_HEADER; public class BlobKeeperClientImpl extends AbstractService implements BlobKeeperClient { private static final Logger log = LoggerFactory.getLogger(BlobKeeperClientImpl.class); private final ObjectMapper objectMapper; private final URL baseUrl; private final AsyncHttpClient httpClient; public BlobKeeperClientImpl(ObjectMapper objectMapper, URL baseUrl, AsyncHttpClientConfig config) { this.objectMapper = objectMapper; this.baseUrl = baseUrl; httpClient = new DefaultAsyncHttpClient(config); } public BlobKeeperClientImpl(ObjectMapper objectMapper, URL baseUrl) { this(objectMapper, baseUrl, new DefaultAsyncHttpClientConfig.Builder() .setFollowRedirect(true) .setKeepAlive(true) .setKeepAliveStrategy(new DefaultKeepAliveStrategy()) .setConnectionTtl(5000) .setRequestTimeout(5000) .setMaxRequestRetry(3) .setMaxConnections(8192) .setSoLinger(-1) .setTcpNoDelay(true) .build()); } @Override public Response getFile(long id, int type) { return getFile(id, type, null); } @Override public Response getFile(long id, int type, String authToken) { try { BoundRequestBuilder requestBuilder = httpClient.prepareGet(baseUrl + "/" + id + "/" + type); if (authToken != null) { requestBuilder.addHeader(AUTH_TOKEN_HEADER, authToken); } return requestBuilder.execute().get(); } catch (InterruptedException | ExecutionException e) { log.error("Can't execute query", e); throw new IllegalArgumentException(e); } } @Override public Response addFile(@NotNull File file, @NotNull Map<String, String> headers) { BoundRequestBuilder requestBuilder = httpClient.preparePost(baseUrl.toString()); return executePost(requestBuilder, file, headers); } @Override public Response addFile(long id, int type, @NotNull File file, @NotNull Map<String, String> headers) { BoundRequestBuilder requestBuilder = httpClient.preparePost(baseUrl + "/" + id + "/" + type); return executePost(requestBuilder, file, headers); } @Override public Response deleteFile(long id, @NotNull String apiToken) { try { return deleteFileAsync(id, apiToken).get(); } catch (InterruptedException | ExecutionException e) { log.error("Can't execute query", e); throw new IllegalArgumentException(e); } } @Override public ListenableFuture<Response> deleteFileAsync(long id, @NotNull String apiToken) { return httpClient.prepareDelete(baseUrl + "/" + id + "?token=" + apiToken).execute(); } @Override public Response isMaster() { try { return httpClient.prepareGet(baseUrl + UriType.MASTER.getUri()).execute().get(); } catch (InterruptedException | ExecutionException e) { log.error("Can't execute query", e); throw new IllegalArgumentException(e); } } @Override public Response setMaster(@NotNull SetMasterApiRequest request) { BoundRequestBuilder postRequestBuilder = httpClient.preparePost(baseUrl.toString() + UriType.SET_MASTER.getUri()); return executePost(postRequestBuilder, request); } @Override public Response removeMaster(@NotNull EmptyRequest request) { BoundRequestBuilder postRequestBuilder = httpClient.preparePost(baseUrl.toString() + UriType.REMOVE_MASTER.getUri()); return executePost(postRequestBuilder, request); } @Override public Response refreshDisks(@NotNull RefreshDiskRequest request) { BoundRequestBuilder postRequestBuilder = httpClient.preparePost(baseUrl.toString() + UriType.REFRESH.getUri()); return executePost(postRequestBuilder, request); } @Override public Response repair(@NotNull RepairDiskRequest request) { BoundRequestBuilder postRequestBuilder = httpClient.preparePost(baseUrl.toString() + UriType.REPAIR.getUri()); return executePost(postRequestBuilder, request); } @Override public Response balance(@NotNull RebalancingDiskRequest request) { BoundRequestBuilder postRequestBuilder = httpClient.preparePost(baseUrl.toString() + UriType.BALANCE.getUri()); return executePost(postRequestBuilder, request); } @Override protected void doStart() { notifyStarted(); } @Override protected void doStop() { try { httpClient.close(); } catch (IOException e) { log.error("Can't close http client", e); } notifyStopped(); } private Response executePost(BoundRequestBuilder postRequestBuilder, ApiRequest request) { try { postRequestBuilder.addBodyPart(new StringPart("json", objectMapper.writeValueAsString(request))); return httpClient.executeRequest(postRequestBuilder.build()).get(); } catch (InterruptedException | ExecutionException | IOException e) { log.error("Can't execute query", e); throw new IllegalArgumentException(e); } } private Response executePost( BoundRequestBuilder postRequestBuilder, File file, Map<String, String> headers ) { checkArgument(headers.containsKey("X-Metadata-Content-Type"), "X-Metadata-Content-Type header is required!"); postRequestBuilder.addBodyPart(new FilePart("file", file)); for (String headerName : headers.keySet()) { postRequestBuilder.addHeader(headerName, headers.get(headerName)); } try { return httpClient.executeRequest(postRequestBuilder.build()).get(); } catch (InterruptedException | ExecutionException e) { log.error("Can't execute query", e); throw new IllegalArgumentException(e); } } }