2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.so.client;
23 import java.net.ConnectException;
24 import java.net.MalformedURLException;
25 import java.net.SocketTimeoutException;
28 import java.security.GeneralSecurityException;
29 import java.util.ArrayList;
30 import java.util.Base64;
31 import java.util.HashMap;
32 import java.util.List;
34 import java.util.Map.Entry;
35 import java.util.Optional;
36 import java.util.concurrent.TimeUnit;
37 import java.util.function.Predicate;
38 import javax.ws.rs.client.Client;
39 import javax.ws.rs.client.ClientBuilder;
40 import javax.ws.rs.client.Invocation.Builder;
41 import javax.ws.rs.client.WebTarget;
42 import javax.ws.rs.core.GenericType;
43 import javax.ws.rs.core.MediaType;
44 import javax.ws.rs.core.Response;
45 import javax.ws.rs.core.Response.Status;
46 import javax.ws.rs.core.UriBuilder;
47 import org.onap.so.client.policy.CommonObjectMapperProvider;
48 import org.onap.so.logging.jaxrs.filter.JaxRsClientLogging;
49 import org.onap.so.logging.jaxrs.filter.PayloadLoggingFilter;
50 import org.onap.so.utils.CryptoUtils;
51 import org.onap.so.utils.TargetEntity;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
55 import net.jodah.failsafe.Failsafe;
56 import net.jodah.failsafe.RetryPolicy;
59 public abstract class RestClient {
60 private static final String APPLICATION_MERGE_PATCH_JSON = "application/merge-patch+json";
62 public static final String ECOMP_COMPONENT_NAME = "MSO";
64 private static final int MAX_PAYLOAD_SIZE = 1024 * 1024;
65 private WebTarget webTarget;
67 protected final Map<String, String> headerMap;
68 protected final Logger logger = LoggerFactory.getLogger(RestClient.class);
70 protected Optional<URI> path;
71 protected String accept;
72 protected String contentType;
73 protected String requestId = "";
74 protected JaxRsClientLogging jaxRsClientLogging;
75 protected RestProperties props;
77 protected RestClient(RestProperties props, Optional<URI> path) {
79 headerMap = new HashMap<>();
81 host = props.getEndpoint();
82 } catch (MalformedURLException e) {
84 throw new RuntimeException(e);
90 protected RestClient(RestProperties props, Optional<URI> path, String accept, String contentType) {
93 this.contentType = contentType;
97 protected RestClient(URL host, String contentType) {
98 headerMap = new HashMap<>();
99 this.path = Optional.empty();
101 this.contentType = contentType;
102 this.props = new DefaultProperties(host);
106 * Override method to return false to disable logging.
108 * @return true - to enable logging, false otherwise
110 protected boolean enableLogging() {
115 * Override method to return custom value for max payload size.
117 * @return Default value for MAX_PAYLOAD_SIZE = 1024 * 1024
119 protected int getMaxPayloadSize() {
120 return MAX_PAYLOAD_SIZE;
123 protected Builder getBuilder() {
125 if (webTarget == null) {
126 initializeClient(getClient());
128 Builder builder = webTarget.request();
129 initializeHeaderMap(headerMap);
130 for (Entry<String, String> entry : headerMap.entrySet()) {
131 builder.header(entry.getKey(), entry.getValue());
136 protected WebTarget getWebTarget() {
137 return this.webTarget;
140 protected abstract void initializeHeaderMap(Map<String, String> headerMap);
142 protected Optional<ResponseExceptionMapper> addResponseExceptionMapper() {
143 return Optional.of(new ResponseExceptionMapperImpl());
146 protected CommonObjectMapperProvider getCommonObjectMapperProvider() {
147 return new CommonObjectMapperProvider();
151 * Adds a basic authentication header to the request.
153 * @param auth the encrypted credentials
154 * @param key the key for decrypting the credentials
156 protected void addBasicAuthHeader(String auth, String key) {
158 byte[] decryptedAuth = CryptoUtils.decrypt(auth, key).getBytes();
159 String authHeaderValue = "Basic " + Base64.getEncoder().encodeToString(decryptedAuth);
160 headerMap.put("Authorization", authHeaderValue);
161 } catch (GeneralSecurityException e) {
162 logger.error(e.getMessage(), e);
166 protected String getAccept() {
170 protected String getContentType() {
174 protected String getMergeContentType() {
175 return APPLICATION_MERGE_PATCH_JSON;
178 protected Client getClient() {
179 return ClientBuilder.newBuilder().build();
182 protected abstract TargetEntity getTargetEntity();
184 protected void initializeClient(Client client) {
185 if (this.enableLogging()) {
186 client.register(new PayloadLoggingFilter(this.getMaxPayloadSize()));
188 CommonObjectMapperProvider provider = this.getCommonObjectMapperProvider();
189 client.register(new JacksonJsonProvider(provider.getMapper()));
191 jaxRsClientLogging = new JaxRsClientLogging();
192 jaxRsClientLogging.setTargetService(getTargetEntity());
193 client.register(jaxRsClientLogging);
195 if (!path.isPresent()) {
196 webTarget = client.target(host.toString());
198 webTarget = client.target(UriBuilder.fromUri(host + path.get().toString()));
200 if (getAccept() == null || getAccept().isEmpty()) {
201 this.accept = MediaType.APPLICATION_JSON;
203 if (getContentType() == null || getContentType().isEmpty()) {
204 this.contentType = MediaType.APPLICATION_JSON;
208 protected List<Predicate<Throwable>> retryOn() {
210 List<Predicate<Throwable>> result = new ArrayList<>();
213 return e.getCause() instanceof SocketTimeoutException;
216 return e.getCause() instanceof ConnectException;
221 public Response get() {
222 return method("GET", null);
225 public Response post(Object obj) {
226 return method("POST", obj);
229 public Response patch(Object obj) {
230 return method("PATCH", obj);
233 public Response put(Object obj) {
234 return method("PUT", obj);
237 public Response delete() {
238 return method("DELETE", null);
241 public Response delete(Object obj) {
242 return method("DELETE", obj);
245 public <T> Optional<T> get(Class<T> resultClass) {
246 return format(method("GET", null), resultClass);
249 public <T> Optional<T> get(GenericType<T> resultClass) {
250 return format(method("GET", null), resultClass);
253 public <T> T post(Object obj, Class<T> resultClass) {
254 return format(method("POST", obj), resultClass).orElse(null);
257 public <T> T patch(Object obj, Class<T> resultClass) {
258 return format(method("PATCH", obj), resultClass).orElse(null);
261 public <T> T put(Object obj, Class<T> resultClass) {
262 return format(method("PUT", obj), resultClass).orElse(null);
265 public <T> T put(Object obj, GenericType<T> resultClass) {
266 return format(method("PUT", obj), resultClass).orElse(null);
269 public <T> T delete(Class<T> resultClass) {
270 return format(method("DELETE", null), resultClass).orElse(null);
273 public <T> T delete(Object obj, Class<T> resultClass) {
274 return format(method("DELETE", obj), resultClass).orElse(null);
277 public Response method(String method, Object entity) {
278 RetryPolicy policy = new RetryPolicy();
280 List<Predicate<Throwable>> items = retryOn();
282 Predicate<Throwable> pred = items.stream().reduce(Predicate::or).orElse(x -> false);
284 policy.retryOn(error -> pred.test(error));
286 policy.withDelay(this.props.getDelayBetweenRetries(), TimeUnit.MILLISECONDS)
287 .withMaxRetries(this.props.getRetries());
289 return Failsafe.with(policy).get(buildRequest(method, entity));
292 protected RestRequest buildRequest(String method, Object entity) {
293 return new RestRequest(this, method, entity);
296 private <T> Optional<T> format(Response response, Class<T> resultClass) {
297 if (this.props.mapNotFoundToEmpty() && response.getStatus() == Status.NOT_FOUND.getStatusCode()) {
298 return Optional.empty();
300 return Optional.of(response.readEntity(resultClass));
303 private <T> Optional<T> format(Response response, GenericType<T> resultClass) {
304 if (this.props.mapNotFoundToEmpty() && response.getStatus() == Status.NOT_FOUND.getStatusCode()) {
305 return Optional.empty();
307 return Optional.of(response.readEntity(resultClass));