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

com.abiquo.apiclient.RestClient Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2008 Abiquo Holdings S.L.
 *
 * Licensed 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.
 */
package com.abiquo.apiclient;

import static com.abiquo.apiclient.domain.Links.editOrSelf;
import static com.abiquo.apiclient.domain.PageIterator.flatten;
import static com.abiquo.apiclient.domain.options.BaseOptions.urlEncode;
import static com.abiquo.apiclient.util.LogUtils.logRequest;
import static com.abiquo.apiclient.util.LogUtils.logResponse;
import static com.google.common.collect.Maps.transformValues;
import static java.util.Objects.requireNonNull;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;

import com.abiquo.apiclient.ApiClient.SSLConfiguration;
import com.abiquo.apiclient.auth.Authentication;
import com.abiquo.apiclient.domain.exception.AbiquoException;
import com.abiquo.apiclient.domain.exception.AuthorizationException;
import com.abiquo.apiclient.domain.exception.DataBaseException;
import com.abiquo.apiclient.domain.exception.HttpException;
import com.abiquo.apiclient.interceptors.AuthenticationInterceptor;
import com.abiquo.apiclient.json.Json;
import com.abiquo.model.rest.RESTLink;
import com.abiquo.model.transport.AcceptedRequestDto;
import com.abiquo.model.transport.SingleResourceTransportDto;
import com.abiquo.model.transport.WrapperDto;
import com.abiquo.model.transport.error.ErrorsDto;
import com.abiquo.model.transport.error.LimitExceededErrorDto;
import com.abiquo.server.core.asynctask.AsyncTaskDto;
import com.abiquo.server.core.cloud.VirtualApplianceDto;
import com.abiquo.server.core.cloud.VirtualApplianceState;
import com.abiquo.server.core.cloud.VirtualMachineDto;
import com.abiquo.server.core.cloud.VirtualMachineState;
import com.abiquo.server.core.task.TaskDto;
import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.net.HttpHeaders;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.Uninterruptibles;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.MultipartBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Request.Builder;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

public class RestClient
{
    private final OkHttpClient client;

    private final Json json;

    private final String baseURL;

    private final String apiVersion;

    private final RequestBody emptyRequestBody;

    // Package protected. To be used only by the ApiClient
    RestClient(final Authentication authentication, final String baseURL, final String apiVersion,
        final SSLConfiguration sslConfiguration)
    {
        this.json = new Json();
        this.baseURL = requireNonNull(baseURL, "baseURL cannot be null");
        this.apiVersion = requireNonNull(apiVersion, "apiVersion cannot be null");
        this.emptyRequestBody = RequestBody.create(null, new byte[0]);

        client = new OkHttpClient();
        client.setReadTimeout(0, TimeUnit.MILLISECONDS);
        client.setWriteTimeout(0, TimeUnit.MILLISECONDS);
        client.setConnectTimeout(0, TimeUnit.MILLISECONDS);
        client.networkInterceptors().add(new AuthenticationInterceptor(authentication, this));

        if (sslConfiguration != null)
        {
            client.setHostnameVerifier(sslConfiguration.hostnameVerifier());
            client.setSslSocketFactory(sslConfiguration.sslContext().getSocketFactory());
        }
    }

    /**
     * Changes made to the returned client will affect all the subsequent requests.
     * 

* If you want to create a new client without affecting the existing one, use the * {@link OkHttpClient#clone()} method on the returned client. */ public OkHttpClient rawClient() { return client; } /** * Returns the {@link Json} instance used to serialize and deserialize the request and response * bodies. */ public Json json() { return json; } public T edit(final T dto) { RESTLink link = requireNonNull(dto.getEditLink(), "The given object does not have an edit link"); @SuppressWarnings("unchecked") Class clazz = (Class) dto.getClass(); return put(link.getHref(), link.getType(), link.getType(), dto, clazz); } public void delete(final SingleResourceTransportDto dto) { RESTLink link = requireNonNull(editOrSelf(dto), "The given object does not have an edit/self link"); delete(link.getHref()); } public T refresh(final T dto) { RESTLink link = editOrSelf(dto); @SuppressWarnings("unchecked") Class clazz = (Class) dto.getClass(); requireNonNull(link, "The given object does not have an edit/self link"); return get(link.getHref(), link.getType(), clazz); } public > Stream list( final RESTLink link, final Class clazz) { return flatten(this, get(link, clazz)); } public > Stream list( final String uri, final String accept, final Class returnClass) { return flatten(this, get(uri, accept, returnClass)); } public > Stream list( final String uri, final String accept, final TypeToken returnType) { return flatten(this, get(uri, accept, returnType)); } public > Stream list( final String uri, final Map queryParams, final String accept, final Class returnClass) { return flatten(this, get(uri, queryParams, accept, returnClass)); } public > Stream list( final String uri, final String accept, final Class returnClass, final Map headers) { return flatten(this, get(uri, accept, returnClass, headers)); } public > Stream list( final String uri, final Map queryParams, final String accept, final TypeToken returnType) { return get(uri, queryParams, accept, returnType).getCollection().stream(); } public T get(final RESTLink link, final Class clazz) { return get(link.getHref(), link.getType(), clazz); } public T get(final String uri, final String accept, final Class returnClass) { try { Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).get().build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T get(final String uri, final String accept, final TypeToken returnType) { try { Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).get().build(); return execute(request, returnType); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T get(final String uri, final String accept, final Class returnClass, final Map headers) { try { Builder requestBuilder = new Request.Builder().url(absolute(uri)); headers.entrySet().stream() .forEach(h -> requestBuilder.addHeader(h.getKey(), h.getValue())); Request request = requestBuilder.addHeader(HttpHeaders.ACCEPT, withVersion(accept)).get().build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T get(final String uri, final Map queryParams, final String accept, final Class returnClass) { try { Request request = new Request.Builder().url(absolute(uri) + "?" + queryLine(queryParams)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).get().build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T get(final String uri, final Map queryParams, final String accept, final TypeToken returnType) { try { Request request = new Request.Builder().url(absolute(uri) + "?" + queryLine(queryParams)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).get().build(); return execute(request, returnType); } catch (IOException ex) { throw Throwables.propagate(ex); } } public String getAsString(final String uri, final String accept) { try { Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).get().build(); return execute(request, String.class); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T delete(final SingleResourceTransportDto dto, final String accept, final Class returnClass) { RESTLink link = requireNonNull(editOrSelf(dto), "The given object does not have an edit/self link"); try { Request request = new Request.Builder().url(absolute(link.getHref())) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).delete().build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public void delete(final String uri) { delete(uri, Collections.emptyMap()); } public void delete(final String uri, final Map queryParams) { try { Request request; if (queryParams.isEmpty()) { request = new Request.Builder().url(absolute(uri)).delete().build(); } else { request = new Request.Builder().url(absolute(uri) + "?" + queryLine(queryParams)) .delete().build(); } execute(request, (Class< ? >) null); } catch (IOException ex) { // TODO ABICLOUDPREMIUM-14750 review Runtime in this class throw new RuntimeException(ex); } } public T post(final String uri, final Map queryParams, final String accept, final String contentType, final SingleResourceTransportDto body, final Class returnClass) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), json.write(body)); Request request = new Request.Builder().url(absolute(uri) + "?" + queryLine(queryParams)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).post(requestBody).build(); return execute(request, returnClass); } catch (IOException ex) { // TODO ABICLOUDPREMIUM-14750 review Runtime in this class throw new RuntimeException(ex); } } public T post(final String uri, final String accept, final String contentType, final SingleResourceTransportDto body, final Class returnClass) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), json.write(body)); Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).post(requestBody).build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T post(final String uri, final String accept, final String contentType, final SingleResourceTransportDto body, final Class returnClass, final Map headers) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), json.write(body)); Builder requestBuilder = new Request.Builder().url(absolute(uri)); headers.entrySet().stream() .forEach(h -> requestBuilder.addHeader(h.getKey(), h.getValue())); Request request = requestBuilder.addHeader(HttpHeaders.ACCEPT, withVersion(accept)) .post(requestBody).build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T post(final String uri, final String accept, final String contentType, final SingleResourceTransportDto body, final TypeToken returnType) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), json.write(body)); Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).post(requestBody).build(); return execute(request, returnType); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T post(final String uri, final String accept, final Class returnClass) { try { Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).post(emptyRequestBody).build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T post(final String uri, final String accept, final String contentType, final String body, final Class returnClass) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), body); Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).post(requestBody).build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public Response multipartPost(final String url, final String bodyName, final String body, final String fileName, final File file) { OkHttpClient multipartClient = client.clone(); try { RequestBody jsonPart = RequestBody.create(MediaType.parse("text/plain; charset=UTF-8"), body); RequestBody filePart = RequestBody .create(MediaType.parse("application/octet-stream; charset=ISO-8859-1"), file); RequestBody requestBody = new MultipartBuilder().type(MultipartBuilder.FORM) .addPart(Headers.of("Content-Disposition", "form-data; name=\"" + bodyName + "\""), jsonPart) // .addFormDataPart(fileName, file.getName(), filePart) // .build(); Request request = new Request.Builder().url(absolute(url)).post(requestBody) // .build(); Response response = multipartClient.newCall(request).execute(); checkResponse(response, response.body().string()); return response; } catch (IOException ex) { throw Throwables.propagate(ex); } } public T post(final String uri, final String accept, final TypeToken returnType) { try { Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).post(emptyRequestBody).build(); return execute(request, returnType); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T post(final String uri, final Map queryParams, final String accept, final TypeToken returnType) { try { Request request = new Request.Builder() .url(absolute(uri) + "?" + queryLine(queryParams)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).post(emptyRequestBody).build(); return execute(request, returnType); } catch (IOException ex) { throw new UncheckedIOException(ex); } } public T put(final String uri, final Map queryParams, final String accept, final String contentType, final SingleResourceTransportDto body, final Class returnClass) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), json.write(body)); Request request = new Request.Builder().url(absolute(uri) + "?" + queryLine(queryParams)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).put(requestBody).build(); return execute(request, returnClass); } catch (IOException ex) { // TODO ABICLOUDPREMIUM-14750 review RuntimeException in this class throw new RuntimeException(ex); } } public T put(final String uri, final String accept, final String contentType, final SingleResourceTransportDto body, final Class returnClass) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), json.write(body)); Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).put(requestBody).build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public void putAction(final String uri, final String contentType, final SingleResourceTransportDto body) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), json.write(body)); Request request = new Request.Builder().url(absolute(uri)).put(requestBody).build(); execute(request, (Class< ? >) null); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T put(final String uri, final String accept, final Class returnClass) { try { Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).put(emptyRequestBody).build(); return execute(request, returnClass); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T put(final String uri, final String accept, final TypeToken returnType) { try { Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).put(emptyRequestBody).build(); return execute(request, returnType); } catch (IOException ex) { throw Throwables.propagate(ex); } } public T put(final String uri, final String accept, final String contentType, final SingleResourceTransportDto body, final TypeToken returnType) { try { RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), json.write(body)); Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).put(requestBody).build(); return execute(request, returnType); } catch (IOException ex) { throw Throwables.propagate(ex); } } public void put(final String uri, final String accept, final String contentType, final SingleResourceTransportDto body) { try { String rawBody = json.write(body); RequestBody requestBody = RequestBody.create(MediaType.parse(withVersion(contentType)), rawBody); Request request = new Request.Builder().url(absolute(uri)) .addHeader(HttpHeaders.ACCEPT, withVersion(accept)).put(requestBody).build(); execute(request, (Class< ? >) null); } catch (IOException ex) { throw Throwables.propagate(ex); } } private String absolute(final String path) { try { new URL(path); } catch (MalformedURLException e) { return baseURL + (path.startsWith("/") ? path : "/" + path); } return path; } private String withVersion(final String mediaType) { return mediaType.contains("version=") ? mediaType : mediaType + "; version=" + apiVersion; } private String queryLine(final Map queryParams) { Map queryParamsSorted = new TreeMap<>(queryParams); return Joiner.on('&').withKeyValueSeparator("=") .join(transformValues(queryParamsSorted, input -> urlEncode(input.toString()))); } public TaskDto waitForTask(final AcceptedRequestDto< ? > acceptedRequest, final int pollInterval, final int maxWait, final TimeUnit timeUnit) { RESTLink status = acceptedRequest.getStatusLink(); return waitForTask(status, pollInterval, maxWait, timeUnit); } public TaskDto waitForTask(final TaskDto taskDto, final int pollInterval, final int maxWait, final TimeUnit timeUnit) { return waitForTask(taskDto.searchLink("self"), pollInterval, maxWait, timeUnit); } private TaskDto waitForTask(final RESTLink restLink, final int pollInterval, final int maxWait, final TimeUnit timeUnit) { final Stopwatch watch = Stopwatch.createStarted(); while (watch.elapsed(timeUnit) < maxWait) { TaskDto updatedTask = get(restLink.getHref(), TaskDto.MEDIA_TYPE, TaskDto.class); switch (updatedTask.getState()) { case FINISHED_SUCCESSFULLY: case FINISHED_UNSUCCESSFULLY: case ABORTED: case ACK_ERROR: case CANCELLED: return updatedTask; case PENDING: case QUEUEING: case STARTED: // Do nothing and keep waiting break; } Uninterruptibles.sleepUninterruptibly(pollInterval, timeUnit); } throw new RuntimeException("Task did not complete in the configured timeout"); } public VirtualMachineDto waitUntilUnlocked(final VirtualMachineDto vm, final int pollInterval, final int maxWait, final TimeUnit timeUnit) { Stopwatch watch = Stopwatch.createStarted(); while (watch.elapsed(timeUnit) < maxWait) { VirtualMachineDto refreshed = refresh(vm); if (!VirtualMachineState.LOCKED.equals(refreshed.getState())) { return refreshed; } Uninterruptibles.sleepUninterruptibly(pollInterval, timeUnit); } throw new RuntimeException( "Virtual machine did not reach the desired state in the configured timeout"); } public VirtualApplianceDto waitUntilUnlocked(final VirtualApplianceDto vapp, final int pollInterval, final int maxWait, final TimeUnit timeUnit) { Stopwatch watch = Stopwatch.createStarted(); while (watch.elapsed(timeUnit) < maxWait) { VirtualApplianceDto refreshed = refresh(vapp); if (!VirtualApplianceState.LOCKED.equals(refreshed.getState())) { return refreshed; } Uninterruptibles.sleepUninterruptibly(pollInterval, timeUnit); } throw new RuntimeException( "Virtual appliance did not reach the desired state in the configured timeout"); } private T execute(final Request request, final Class resultClass) throws IOException { logRequest(request); Response response = client.newCall(request).execute(); String responseBody = response.body().string(); logResponse(response, responseBody); try { checkResponse(response, responseBody); } catch (DataBaseException dbEx) { // retry return execute(request, resultClass); } if (!Strings.isNullOrEmpty(responseBody) && resultClass != null) { if (String.class.equals(resultClass)) { return resultClass.cast(responseBody); } else { return json.read(responseBody, resultClass); } } return null; } private T execute(final Request request, final TypeToken returnType) throws IOException { logRequest(request); Response response = client.newCall(request).execute(); String responseBody = response.body().string(); logResponse(response, responseBody); try { checkResponse(response, responseBody); } catch (DataBaseException dbEx) { // retry return execute(request, returnType); } return !Strings.isNullOrEmpty(responseBody) && returnType != null ? json.read(responseBody, returnType) : null; } private void checkResponse(final Response response, final String responseBody) { int responseCode = response.code(); if (responseCode == 401 || responseCode == 403) { throw new AuthorizationException(responseCode, response.message()); } else if (responseCode >= 400) { if (responseBody == null) { throw new HttpException(responseCode, response.message()); } try { if (response.header("Content-Type") .equals("application/vnd.abiquo.limiterror+json")) { LimitExceededErrorDto error = json.read(responseBody, LimitExceededErrorDto.class); throw new AbiquoException(responseCode, error); } else { ErrorsDto errors = json.read(responseBody, ErrorsDto.class); if (errors.getCollection().stream() .anyMatch(err -> "DB-0".equals(err.getCode()))) { throw new DataBaseException(responseCode, errors); } throw new AbiquoException(responseCode, errors); } } catch (Exception ex) { Throwables.propagateIfInstanceOf(ex, AbiquoException.class); throw new HttpException(responseCode, response.message() + ". Body: " + responseBody); } } } /** * Waits for task finalization * * @param taskDto task to wait for * @param pollInterval amount of time to sleep between task retrieve attempts * @param maxWait maximum amount of time to wait for task finalization * @param timeUnit time unit in which pollInterval and maxWait are expressed * @return the finished task * @throws TimeoutException if the specified maxWait time has been reached and task remains in * progress */ public AsyncTaskDto waitForAsyncTaskFinalization(final AsyncTaskDto taskDto, final long pollInterval, final long maxWait, final TimeUnit timeUnit) throws TimeoutException { String selfHref = taskDto.searchLink("self").getHref(); Stopwatch watch = Stopwatch.createStarted(); while (watch.elapsed(timeUnit) < maxWait) { AsyncTaskDto task = get(selfHref, AsyncTaskDto.MEDIA_TYPE, AsyncTaskDto.class); if (task.isFinished()) { return task; } Uninterruptibles.sleepUninterruptibly(pollInterval, timeUnit); } throw new TimeoutException("Task did not complete in the configured timeout"); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy