
com.abiquo.apiclient.RestClient Maven / Gradle / Ivy
Show all versions of api-java-client Show documentation
/**
* 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");
}
}