org.apache.geode.management.internal.web.http.support.HttpRequester.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.geode.management.internal.web.http.support.HttpRequester.java

Source

/*
 * 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.
 */

package org.apache.geode.management.internal.web.http.support;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import org.apache.geode.internal.GemFireVersion;
import org.apache.geode.management.internal.web.http.converter.SerializableObjectHttpMessageConverter;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.NotAuthorizedException;

/**
 * The HttpRequester class is a Adapter/facade for the Spring RestTemplate class for abstracting
 * HTTP requests and operations.
 * <p/>
 * 
 * @see org.springframework.http.client.SimpleClientHttpRequestFactory
 * @see org.springframework.web.client.RestTemplate
 * @since GemFire 8.0
 */
@SuppressWarnings("unused")
public class HttpRequester {
    private final RestTemplate restTemplate;
    private Properties securityProperties;

    protected static final String USER_AGENT_HTTP_REQUEST_HEADER_VALUE = "GemFire-Shell/v"
            + GemFireVersion.getGemFireVersion();

    // a list of acceptable content/media types supported by Gfsh
    private final List<MediaType> acceptableMediaTypes = Arrays.asList(MediaType.APPLICATION_JSON,
            MediaType.TEXT_PLAIN, MediaType.APPLICATION_OCTET_STREAM);

    public HttpRequester() {
        this(null, null);
    }

    public HttpRequester(Properties securityProperties) {
        this(securityProperties, null);
    }

    HttpRequester(Properties securityProperties, RestTemplate restTemplate) {
        final SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        this.securityProperties = securityProperties;
        if (restTemplate == null) {
            this.restTemplate = new RestTemplate(clientHttpRequestFactory);
        } else {
            this.restTemplate = restTemplate;
        }

        // add our custom HttpMessageConverter for serializing DTO Objects into the HTTP request message
        // body and de-serializing HTTP response message body content back into DTO Objects
        List<HttpMessageConverter<?>> converters = this.restTemplate.getMessageConverters();
        // remove the MappingJacksonHttpConverter
        for (int i = converters.size() - 1; i >= 0; i--) {
            HttpMessageConverter converter = converters.get(i);
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                converters.remove(converter);
            }
        }
        converters.add(new SerializableObjectHttpMessageConverter());

        this.restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(final ClientHttpResponse response) throws IOException {
                String body = IOUtils.toString(response.getBody(), StandardCharsets.UTF_8);
                final String message = String.format("The HTTP request failed with: %1$d - %2$s.",
                        response.getRawStatusCode(), body);

                if (response.getRawStatusCode() == 401) {
                    throw new AuthenticationFailedException(message);
                } else if (response.getRawStatusCode() == 403) {
                    throw new NotAuthorizedException(message);
                } else {
                    throw new RuntimeException(message);
                }
            }
        });
    }

    /**
     * Gets an instance of the Spring RestTemplate to perform the HTTP operations.
     * <p/>
     * 
     * @return an instance of the Spring RestTemplate for performing HTTP operations.
     * @see org.springframework.web.client.RestTemplate
     */
    public RestTemplate getRestTemplate() {
        return restTemplate;
    }

    public <T> T get(URI url, Class<T> responseType) {
        return exchange(url, HttpMethod.GET, null, null, responseType);
    }

    public <T> T post(URI url, MediaType mediaType, Object content, Class<T> responseType) {
        return exchange(url, HttpMethod.POST, mediaType, content, responseType);
    }

    <T> T exchange(URI url, HttpMethod method, MediaType mediaType, Object content, Class<T> responseType) {
        HttpHeaders headers = new HttpHeaders();
        addHeaderValues(headers);

        HttpEntity<Object> httpEntity = new HttpEntity<>(content, headers);

        final ResponseEntity<T> response = restTemplate.exchange(url, method, httpEntity, responseType);
        return response.getBody();
    }

    public Object executeWithResponseExtractor(URI url) {
        return restTemplate.execute(url, HttpMethod.POST, this::addHeaderValues, this::extractResponse);
    }

    void addHeaderValues(ClientHttpRequest request) {
        addHeaderValues(request.getHeaders());
    }

    Object extractResponse(ClientHttpResponse response) throws IOException {
        MediaType mediaType = response.getHeaders().getContentType();
        if (mediaType.equals(MediaType.APPLICATION_JSON)) {
            return org.apache.commons.io.IOUtils.toString(response.getBody(), "UTF-8");
        } else {
            Path tempFile = Files.createTempFile("fileDownload", "");
            if (tempFile.toFile().exists()) {
                FileUtils.deleteQuietly(tempFile.toFile());
            }
            Files.copy(response.getBody(), tempFile);
            return tempFile;
        }
    }

    void addHeaderValues(HttpHeaders headers) {
        // update the headers
        headers.add(HttpHeaders.USER_AGENT, USER_AGENT_HTTP_REQUEST_HEADER_VALUE);
        headers.setAccept(acceptableMediaTypes);

        if (this.securityProperties != null) {
            for (String key : securityProperties.stringPropertyNames()) {
                headers.add(key, securityProperties.getProperty(key));
            }
        }
    }

    /**
     * build the url using the path and query params
     * 
     * @param path : the part after the baseUrl
     * @param queryParams this needs to be an even number of strings in the form of paramName,
     *        paramValue....
     */
    public static URI createURI(String baseUrl, String path, String... queryParams) {
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl).path(path);

        if (queryParams != null) {
            if (queryParams.length % 2 != 0) {
                throw new IllegalArgumentException("invalid queryParams count");
            }
            for (int i = 0; i < queryParams.length; i += 2) {
                builder.queryParam(queryParams[i], queryParams[i + 1]);
            }
        }
        return builder.build().encode().toUri();
    }
}