Java tutorial
/* * Copyright 2014 the original author or authors. * * 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 ru.anr.base.facade.web.api; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; import java.nio.charset.Charset; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import java.util.Base64; import java.util.List; import javax.net.ssl.SSLContext; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ssl.AllowAllHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContextBuilder; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.HttpClients; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.HttpComponentsClientHttpRequestFactory; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; import ru.anr.base.ApplicationException; import ru.anr.base.BaseParent; import ru.anr.base.UriUtils; /** * Some lite configured rest-client for test purposes. * * * @author Alexey Romanchuk * @created Nov 13, 2014 * */ public class RestClient extends BaseParent { /** * Logger */ private static final Logger logger = LoggerFactory.getLogger(RestClient.class); /** * A reference to Spring {@link RestTemplate} engine */ private RestTemplate rest; /** * Default port */ private int port = 8080; /** * Default host */ private String host = "localhost"; /** * Http schema (http, https) */ private String schema = "http"; /** * 'Content-Type' header value (what we send) */ private MediaType contentType = MediaType.APPLICATION_JSON; /** * 'Accept' header value (what we expect to receive) */ private MediaType accept = MediaType.APPLICATION_JSON; /** * Clearing cookie value (remove session) */ public void clearCookies() { store.clear(); } /** * Default constructor */ public RestClient() { this("http"); } /** * Constructor with schema * * @param schema * schema Default constructor */ public RestClient(String schema) { super(); setSchema(schema); rest = initRest(new RestTemplate()); } /** * Constructor with OAuth2 resource * * @param resource * OAuth2 protected resource */ public RestClient(OAuth2ProtectedResourceDetails resource) { super(); rest = initRest(new OAuth2RestTemplate(resource)); } /** * Building a base url string (server location), excluding a printing of * standard http ports. * * @return String with server location with schema, host and port */ public String getBaseUrl() { return UriUtils.getBaseUrl(schema, host, port); } /** * Get final URI of http resource * * @param path * Can be a relative path (for instance, '/ping') or a full one ( * {@link #getBaseUrl()} is not used) like * http://localhost:9090/ping * @return A full path to http resource (included schema, host, port, * relative path) */ public String getUri(String path) { return UriUtils.getUri(schema, host, port, path); } /** * Cookie storage */ private final CookieStore store = new BasicCookieStore(); /** * Special initialization of {@link RestTemplate} - used to apply some * settings for existing RestTemplates. * * @param template * {@link RestTemplate} or its * @return Updated RestTemplate */ public RestTemplate initRest(RestTemplate template) { // 1. Set up ssl settings HttpClient client = "https".equals(schema) ? buildSSLClient() : HttpClients.custom().setDefaultCookieStore(store).build(); template.setRequestFactory(new HttpComponentsClientHttpRequestFactory(client) { @Override protected void postProcessHttpRequest(HttpUriRequest request) { super.postProcessHttpRequest(request); } }); // 2. Error handler template.setErrorHandler(new DefaultResponseErrorHandler()); return template; } /** * Getting {@link OAuth2RestTemplate} * * @return A rest template */ public OAuth2RestTemplate oauth2() { Assert.isTrue((rest instanceof OAuth2RestTemplate), "No oauth2 rest configured"); return (OAuth2RestTemplate) rest; } /** * Configuring an apache client to support untrusted ssl connections. This * can be useful for test purposes only. * * @return Apache {@link HttpClient} */ private HttpClient buildSSLClient() { TrustStrategy acceptingTrustStrategy = new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] certificate, String authType) { return true; } }; try { SSLContextBuilder sslBuilder = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy); SSLContext sslContext = sslBuilder.useTLS().build(); SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(sslContext, new AllowAllHostnameVerifier()); return HttpClients.custom().setSSLSocketFactory(sf).build(); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException ex) { throw new ApplicationException(ex); } } /** * Basic authorization header */ private String basicCredentials; /** * Setting Basic Authorization header to apply * * @param user * A user * @param password * A password */ public void setBasicCredentials(String user, String password) { String s = user + ":" + password; this.basicCredentials = "Basic " + utf8(Base64.getEncoder().encode(utf8(s))); } /** * Cleaning up the Basic Authorization header */ public void cleanBasicCredentials() { this.basicCredentials = null; } /** * Applying for default headers * * @return {@link HttpHeaders} object */ protected HttpHeaders applyHeaders() { HttpHeaders hh = new HttpHeaders(); if (contentType != null) { hh.setContentType(contentType); } if (accept != null) { hh.setAccept(list(accept)); hh.setAcceptCharset(list(Charset.forName("utf-8"))); } if (basicCredentials != null) { hh.add("Authorization", basicCredentials); } return hh; } /** * POST method. * * @param path * Relative or absolute path * @param body * Request body (as expected by * {@link #setContentType(MediaType)}), default "application/json * @return Response with a body and state */ public ResponseEntity<String> post(String path, String body) { return exchange(path, HttpMethod.POST, body, String.class); } /** * POST for a form * * @param path * Path to resource * @param formData * Form params * @return Http status for response */ public ResponseEntity<Void> post(String path, MultiValueMap<String, String> formData) { setContentType(MediaType.APPLICATION_FORM_URLENCODED); return exchange(getUri(path), HttpMethod.POST, formData, (Class<Void>) null); } /** * Performing GET query with redirect to some location * * @param path * Path for query * @return Redirected url */ public String getRedirect(String path) { ResponseEntity<Void> response = exchange(getUri(path), HttpMethod.GET, null, Void.class); URI location = response.getHeaders().getLocation(); try { return URLDecoder.decode(location.toString(), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("Could not decode URL", e); } } /** * PUT method. * * @param path * Relative or absolute path * @param body * Request body (as expected by * {@link #setContentType(MediaType)}), default "application/json * @return Response with a body and state */ public ResponseEntity<String> put(String path, String body) { return exchange(path, HttpMethod.PUT, body, String.class); } /** * PUT method. * * @param path * Relative or absolute path * @return Response with a body and state */ public ResponseEntity<String> delete(String path) { return exchange(path, HttpMethod.DELETE, null, String.class); } /** * GET method. * * @param path * Relative or absolute path * @param uriVariables * Request variable according to Spring * {@link org.springframework.web.util.UriTemplate#expand(Object...)} * rules. * @return Response with a body and state */ public ResponseEntity<String> get(String path, Object... uriVariables) { return exchange(path, HttpMethod.GET, null, String.class, uriVariables); } /** * General representation for all rest operations * * @param path * Relative or absolute path to resource * @param method * http method to use * @param body * Request body (for PUT/POST) * @param uriVariables * uri params (part of url in GET queries) * @return Response * * @param clazz * Response entity class * @param <T> * Reponse entity type * @param <S> * Request entity type */ private <S, T> ResponseEntity<T> exchange(String path, HttpMethod method, S body, Class<T> clazz, Object... uriVariables) { HttpHeaders hh = applyHeaders(); ResponseEntity<T> rs = rest.exchange(getUri(path), method, new HttpEntity<S>(body, hh), clazz, uriVariables); logger.debug("Cookie: {}", store.getCookies()); logger.debug("Http response: {}", rs); return rs; } // ///////////////////////////////////////////////////////////////////////// // /// getters/setters // ///////////////////////////////////////////////////////////////////////// /** * @return the port */ public int getPort() { return port; } /** * @param port * the port to set */ public void setPort(int port) { this.port = port; } /** * @return the schema */ public String getSchema() { return schema; } /** * @param schema * the schema to set */ public void setSchema(String schema) { this.schema = schema; } /** * @return the rest * * @param <S> * Interface type */ @SuppressWarnings("unchecked") public <S extends RestOperations> S getRest() { return (S) rest; } /** * @return the host */ public String getHost() { return host; } /** * @param host * the host to set */ public void setHost(String host) { this.host = host; } /** * @return the contentType */ public MediaType getContentType() { return contentType; } /** * @param contentType * the contentType to set */ public void setContentType(MediaType contentType) { this.contentType = contentType; } /** * @return the accept */ public MediaType getAccept() { return accept; } /** * @param accept * the accept to set */ public void setAccept(MediaType accept) { this.accept = accept; } /** * @return the cookies */ public List<Cookie> getCookies() { return store.getCookies(); } /** * @param rest * the rest to set */ public void setRest(RestTemplate rest) { this.rest = rest; } /** * @param cookies * the cookies to set */ public void setCookies(List<Cookie> cookies) { cookies.forEach(c -> store.addCookie(c)); } }