Java tutorial
/* * 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; } } }