com.alexshabanov.springrestapi.restapitest.RestOperationsTestClient.java Source code

Java tutorial

Introduction

Here is the source code for com.alexshabanov.springrestapi.restapitest.RestOperationsTestClient.java

Source

/*
 * Copyright 2012 Alexander Shabanov - http://alexshabanov.com.
 * 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.alexshabanov.springrestapi.restapitest;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.util.Assert;
import org.springframework.web.client.*;

import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URI;
import java.util.Collection;
import java.util.List;

/**
 * Delegates HTTP requests to the REST API test support by adapting the RestTemplate's HTTP objects to
 * the servlet api's domain model and vice versa.
 */
public final class RestOperationsTestClient extends RestTemplate {

    @Inject
    private DefaultRestTestSupport testSupport;

    @Override
    protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
            ResponseExtractor<T> responseExtractor) throws RestClientException {
        try {
            final ClientHttpRequest clientHttpRequest = createRequest(url, method);

            if (requestCallback != null) {
                requestCallback.doWithRequest(clientHttpRequest);
            }

            final MockHttpServletResponse mockHttpServletResponse = testSupport
                    .handle(toMockHttpServletRequest(url, method, clientHttpRequest));

            final ClientHttpResponse clientHttpResponse = new ClientHttpResponseAdapter(mockHttpServletResponse);

            // translate error statuses
            if (getErrorHandler().hasError(clientHttpResponse)) {
                getErrorHandler().handleError(clientHttpResponse);
            }

            return responseExtractor == null ? null : responseExtractor.extractData(clientHttpResponse);
        } catch (IOException e) {
            throw new ResourceAccessException("Can't access the resource provided: " + url, e);
        }
    }

    // converts RestTemplate's ClientHttpRequest to MockHttpServletRequest
    private MockHttpServletRequest toMockHttpServletRequest(URI url, HttpMethod method,
            ClientHttpRequest clientHttpRequest) throws IOException {
        final MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(method.name(),
                url.getPath());

        // copy headers
        final HttpHeaders headers = clientHttpRequest.getHeaders();
        for (final String headerKey : headers.toSingleValueMap().keySet()) {
            final List<String> headerValues = headers.get(headerKey);
            for (final String headerValue : headerValues) {
                mockHttpServletRequest.addHeader(headerKey, headerValue);
            }
        }

        // copy query parameters
        final String query = clientHttpRequest.getURI().getQuery();
        if (query != null) {
            mockHttpServletRequest.setQueryString(query);
            final String[] queryParameters = query.split("&");
            for (String keyValueParam : queryParameters) {
                final String[] components = keyValueParam.split("=");
                if (components.length == 1) {
                    continue; // optional parameter
                }

                Assert.isTrue(components.length == 2,
                        "Can't split query parameters " + keyValueParam + " by key-value pair");
                mockHttpServletRequest.setParameter(components[0], components[1]);
            }
        }

        // copy request body
        // TODO: another byte copying approach here
        // TODO: for now we rely to the fact that request body is always presented as byte array output stream
        final OutputStream requestBodyStream = clientHttpRequest.getBody();
        if (requestBodyStream instanceof ByteArrayOutputStream) {
            mockHttpServletRequest.setContent(((ByteArrayOutputStream) requestBodyStream).toByteArray());
        } else {
            throw new AssertionError("Ooops, client http request has non-ByteArrayOutputStream body");
        }

        return mockHttpServletRequest;
    }

    /**
     * Adapts {@link MockHttpServletResponse} object to the {@link ClientHttpResponse} object for further processing
     * by the {@link RestTemplate} data extractor.
     */
    private static final class ClientHttpResponseAdapter implements ClientHttpResponse {

        private final MockHttpServletResponse mockResponse;

        private ClientHttpResponseAdapter(MockHttpServletResponse response) {
            Assert.notNull(response);
            this.mockResponse = response;
        }

        @Override
        public HttpStatus getStatusCode() throws IOException {
            return HttpStatus.valueOf(mockResponse.getStatus());
        }

        @Override
        public int getRawStatusCode() throws IOException {
            return mockResponse.getStatus();
        }

        @Override
        public String getStatusText() throws IOException {
            return getStatusCode().getReasonPhrase();
        }

        @Override
        public void close() {
            // forbid to use response any longer
            mockResponse.setCommitted(true);
        }

        @Override
        public InputStream getBody() throws IOException {
            return new ByteArrayInputStream(mockResponse.getContentAsByteArray());
        }

        @Override
        public HttpHeaders getHeaders() {
            final HttpHeaders headers = new HttpHeaders();
            final Collection<String> names = mockResponse.getHeaderNames();
            for (final String name : names) {
                headers.set(name, mockResponse.getHeader(name));
            }
            return headers;
        }
    }
}