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

com.gooddata.sdk.service.AbstractService Maven / Gradle / Ivy

There is a newer version: 3.11.1+api3
Show newest version
/*
 * (C) 2023 GoodData Corporation.
 * This source code is licensed under the BSD-style license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */
package com.gooddata.sdk.service;

import static com.gooddata.sdk.common.util.Validate.notNull;
import static java.lang.String.format;
import static org.springframework.http.HttpMethod.GET;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gooddata.sdk.common.GoodDataException;
import com.gooddata.sdk.common.GoodDataRestException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.HttpMessageConverterExtractor;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

/**
 * Parent for GoodData services providing helpers for REST API calls and polling.
 */
public abstract class AbstractService {

    protected final RestTemplate restTemplate;

    private final GoodDataSettings settings;

    protected final ObjectMapper mapper = new ObjectMapper();

    private final ResponseExtractor reusableResponseExtractor = ReusableClientHttpResponse::new;

    /**
     * Sets RESTful HTTP Spring template. Should be called from constructor of concrete service extending
     * this abstract one.
     *
     * @param restTemplate RESTful HTTP Spring template
     * @param settings settings
     */
    public AbstractService(final RestTemplate restTemplate, final GoodDataSettings settings) {
        this.restTemplate = notNull(restTemplate, "restTemplate");
        this.settings = notNull(settings, "settings");
    }

    final  R poll(final PollHandler handler, long timeout, final TimeUnit unit) {
        notNull(handler, "handler");
        final long start = System.currentTimeMillis();
        while (true) {
            if (pollOnce(handler)) {
                return handler.getResult();
            }
            if (unit != null && start + unit.toMillis(timeout) < System.currentTimeMillis()) {
                throw new GoodDataException("timeout");
            }

            try {
                Thread.sleep(settings.getPollSleep());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new GoodDataException("interrupted");
            }
        }
    }

    final 

boolean pollOnce(final PollHandler handler) { notNull(handler, "handler"); final ClientHttpResponse response; try { response = restTemplate.execute(handler.getPolling(), GET, null, reusableResponseExtractor); } catch (GoodDataRestException e) { handler.handlePollException(e); throw new GoodDataException("Handler " + handler.getClass().getName() + " didn't handle exception", e); } try { if (handler.isFinished(response)) { final P data = extractData(response, handler.getPollClass()); handler.handlePollResult(data); } else if (HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())) { throw new GoodDataException( format("Polling returned client error HTTP status %s", response.getStatusCode().value()) ); } } catch (IOException e) { throw new GoodDataException("I/O error occurred during HTTP response extraction", e); } return handler.isDone(); } protected final T extractData(ClientHttpResponse response, Class cls) throws IOException { notNull(response, "response"); notNull(cls, "cls"); if (Void.class.isAssignableFrom(cls)) { return null; } return new HttpMessageConverterExtractor<>(cls, restTemplate.getMessageConverters()).extractData(response); } private static class ReusableClientHttpResponse implements ClientHttpResponse { private byte[] body; private final HttpStatus statusCode; private final int rawStatusCode; private final String statusText; private final HttpHeaders headers; public ReusableClientHttpResponse(ClientHttpResponse response) { try { final InputStream bodyStream = response.getBody(); if (bodyStream != null) { body = FileCopyUtils.copyToByteArray(bodyStream); } statusCode = response.getStatusCode(); rawStatusCode = response.getRawStatusCode(); statusText = response.getStatusText(); headers = response.getHeaders(); } catch (IOException e) { throw new RuntimeException("Unable to read from HTTP response", e); } finally { if (response != null) { response.close(); } } } @Override public HttpStatus getStatusCode() { return statusCode; } @Override public int getRawStatusCode() { return rawStatusCode; } @Override public String getStatusText() { return statusText; } @Override public HttpHeaders getHeaders() { return headers; } @Override public InputStream getBody() { return body != null ? new ByteArrayInputStream(body) : StreamUtils.emptyInput(); } @Override public void close() { //already closed } } protected static class OutputStreamResponseExtractor implements ResponseExtractor { private final OutputStream output; public OutputStreamResponseExtractor(OutputStream output) { this.output = output; } @Override public Integer extractData(ClientHttpResponse response) throws IOException { return FileCopyUtils.copy(response.getBody(), output); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy