RESTUtils.java

/*******************************************************************************
 * Copyright 2018 IIT-CNR
 *
 * 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 it.cnr.iit.utility;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;

import it.cnr.iit.utility.errorhandling.Reject;

import reactor.core.publisher.Mono;

/**
 * This class provides some static methods to perform synchronous or asynchronous
 * rest operations.
 *
 * @author Antonio La Marra, Alessandro Rosetti
 *
 */
public final class RESTUtils {

    private static final Logger log = Logger.getLogger( RESTUtils.class.getName() );

    private static final String MSG_ERR_POST = "Error posting to : {0}";

    private RESTUtils() {

    }

    public static Optional<ResponseEntity<Void>> post( String baseUri, String api, Object obj ) {
        return post( baseUri, api, obj, Void.class );
    }

    public static Optional<URI> parseUri( String str ) {
        try {
            URI uri = new URI( str );
            return Optional.of( uri );
        } catch( Exception e ) {
            log.severe( e.getLocalizedMessage() );
        }
        return Optional.empty();
    }

    public static Optional<String> joinUrl( String base, String api ) {
        try {
            return Optional.of( new URL( new URL( base ), api ).toString() );
        } catch( MalformedURLException e ) {
            return Optional.empty();
        }
    }

    public static <T, E> Optional<ResponseEntity<T>> post( String baseUri, String api, E obj, Class<T> responseClass ) {
        Optional<String> url = joinUrl( baseUri, api );
        Reject.ifAbsent( url );

        Optional<String> jsonString = JsonUtility.getJsonStringFromObject( obj, false );
        if( !jsonString.isPresent() ) {
            return Optional.empty();
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType( MediaType.APPLICATION_JSON );
        HttpEntity<String> entity = new HttpEntity<>( jsonString.get(), headers );
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<T> responseEntity = restTemplate.postForEntity( url.get(), entity, responseClass ); // NOSONAR

        checkResponesEntity( responseEntity );

        return Optional.of( responseEntity );
    }

    public static <E> CompletableFuture<ResponseEntity<Void>> asyncPost( String baseUri, String api, E obj ) {
        return asyncPost( baseUri, api, obj, Void.class );
    }

    public static <T, E> CompletableFuture<ResponseEntity<T>> asyncPost( String baseUri, String api, E obj, Class<T> clazz ) {
        Mono<ResponseEntity<T>> response = WebClient
            .create( baseUri )
            .post()
            .uri( api )
            .contentType( MediaType.APPLICATION_JSON )
            .body( BodyInserters.fromObject( obj ) )
            .exchange()
            .flatMap( r -> r.toEntity( clazz ) );

        return CompletableFuture.supplyAsync( getResponseEntity( response ) );
    }

    private static <T> Supplier<ResponseEntity<T>> getResponseEntity( Mono<ResponseEntity<T>> response ) {
        return () -> {
            ResponseEntity<T> responseEntity = response.block();
            checkResponesEntity( responseEntity );
            return responseEntity;
        };
    }

    private static <T> void checkResponesEntity( ResponseEntity<T> responseEntity ) {
        if( !responseEntity.getStatusCode().is2xxSuccessful() ) {
            String uri = responseEntity.getHeaders().getLocation().toString();
            log.log( Level.SEVERE, MSG_ERR_POST, uri );
        }
    }
}