/* * (c) Copyright Reserved EVRYTHNG Limited 2016. All rights reserved. * Use of this material is subject to license. * Copying and unauthorised use of this material strictly prohibited. */ package com.evrythng.java.wrapper.core.api; import com.evrythng.java.wrapper.core.http.Status; import com.evrythng.java.wrapper.exception.BadRequestException; import com.evrythng.java.wrapper.exception.ConflictException; import com.evrythng.java.wrapper.exception.EvrythngClientException; import com.evrythng.java.wrapper.exception.EvrythngException; import com.evrythng.java.wrapper.exception.EvrythngUnexpectedException; import com.evrythng.java.wrapper.exception.ForbiddenException; import com.evrythng.java.wrapper.exception.InternalErrorException; import com.evrythng.java.wrapper.exception.MethodNotAllowedException; import com.evrythng.java.wrapper.exception.NotFoundException; import com.evrythng.java.wrapper.exception.RequestEntityTooLargeException; import com.evrythng.java.wrapper.exception.UnauthorizedException; import com.evrythng.java.wrapper.util.JSONUtils; import com.evrythng.thng.resource.model.exception.ErrorMessage; import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; /** * Class that contains static utility methods. * * @author Michel Yerly (my) */ public class Utils { private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class); private Utils() { throw new IllegalStateException("This class is static."); } /** * Converts http response into entity * * @param response {@link HttpResponse} instance * @param type {@link TypeReference} instance * @param <K> entity type * @return entity */ @SuppressWarnings("unchecked") public static <K> K convert(final HttpResponse response, final TypeReference<K> type) throws EvrythngException { K result; LOGGER.debug("Performing conversion: [type={}]", type.getType()); if (type.getType().equals(Void.class)) { return null; } if (type.getType().equals(InputStream.class)) { try { // Retrieve response content stream and buffer data as connection may be closed before input is handled: result = (K) IOUtils.toBufferedInputStream(entityStream(response)); } catch (Exception e) { throw new EvrythngClientException(String.format("Unable to retrieve response content stream: [type=%s, cause=%s]", type.getType(), e.getMessage()), e); } } else { if (type.getType().equals(HttpResponse.class)) { // We already have a HttpResponse, let's return it: result = (K) response; } else { // Retrieve response entity (as String so that it can be outputted in case of exception): String entity = entityString(response); if (type.getType().equals(String.class)) { result = (K) entity; } else { try { result = JSONUtils.read(entity, type); } catch (Exception e) { throw new EvrythngClientException(String.format("Unable to map response entity: [type=%s, entity=%s, cause=%s]", type.getType(), entity, e.getMessage()), e); } } } } return result; } /** * Reads entity content stream from the provided {@link HttpResponse}. * * @param response {@link HttpResponse} instance * @return the {@link HttpResponse} entity content as {@link InputStream} */ private static InputStream entityStream(final HttpResponse response) throws EvrythngClientException { LOGGER.debug("Reading response content stream..."); InputStream result; try { HttpEntity entity = response.getEntity(); result = entity.getContent(); } catch (Exception e) { // Convert to custom exception: throw new EvrythngClientException("Error while reading response content stream!", e); } return result; } /** * Reads entity content from the provided {@link HttpResponse}. * * @param response {@link HttpResponse} instance * @return the {@link HttpResponse} entity content as {@link String} */ private static String entityString(final HttpResponse response) throws EvrythngClientException { LOGGER.debug("Reading response entity..."); String result; try { result = IOUtils.toString(entityStream(response)); } catch (Exception e) { // Convert to custom exception: throw new EvrythngClientException(String.format("Error while reading response entity! [type=%s]", String.class), e); } return result; } /** * Asserts {@code expected} {@link Status} against the provided * {@link HttpResponse}. If {@code actual} response {@link Status} does not * match {@code expected} one, then response entity * will be mapped to an {@link ErrorMessage} instance and an exception will * be thrown. * * @param response the {@link HttpResponse} holding a valid status code * @param expected the expected response status code * @throws EvrythngException if provided {@code response} {@link Status} does not match * {@code expected} one */ public static void assertStatus(final HttpResponse response, final Status expected) throws EvrythngException { Status actual = Status.fromStatusCode(response.getStatusLine().getStatusCode()); if (actual == null) { throw new EvrythngUnexpectedException(new ErrorMessage(Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Unknown status code " + response.getStatusLine().getStatusCode())); } LOGGER.debug("Checking response status: [expected={}, actual={}]", expected.getStatusCode(), actual.getStatusCode()); if (actual != expected) { LOGGER.debug("Unexpected response status!"); // Map entity to ErrorMessage: String entity = entityString(response); ErrorMessage message; try { LOGGER.debug("Mapping response to ErrorMessage: [entity={}]", entity); // API should always return an ErrorMessage as JSON: message = JSONUtils.read(entity, new TypeReference<ErrorMessage>() { }); } catch (Exception e) { StringBuilder builder = new StringBuilder(); builder.append("Unable to retrieve ErrorMessage from response! "); builder.append(response.getStatusLine().getStatusCode()); builder.append(": "); builder.append(response.getStatusLine().getReasonPhrase()); builder.append("; Entity String: ["); builder.append(entity); builder.append("]"); throw new EvrythngClientException(builder.toString(), e); } // Handle unexpected status: switch (actual.getFamily()) { case CLIENT_ERROR: switch (actual) { case BAD_REQUEST: throw new BadRequestException(message); case UNAUTHORIZED: throw new UnauthorizedException(message); case FORBIDDEN: throw new ForbiddenException(message); case NOT_FOUND: throw new NotFoundException(message); case METHOD_NOT_ALLOWED: throw new MethodNotAllowedException(message); case CONFLICT: throw new ConflictException(message); case REQUEST_ENTITY_TOO_LARGE: throw new RequestEntityTooLargeException(message); default: throw new EvrythngUnexpectedException(message); } case SERVER_ERROR: throw new InternalErrorException(message); default: throw new EvrythngUnexpectedException(message); } } } }