1 package net.avcompris.commons3.client.impl;
2
3 import static com.google.common.base.Preconditions.checkArgument;
4 import static com.google.common.base.Preconditions.checkNotNull;
5 import static org.apache.commons.lang3.CharEncoding.UTF_8;
6 import static org.apache.commons.lang3.StringUtils.isBlank;
7 import static org.springframework.http.HttpHeaders.AUTHORIZATION;
8 import static org.springframework.http.HttpHeaders.COOKIE;
9
10 import java.nio.charset.Charset;
11
12 import javax.annotation.Nullable;
13
14 import org.apache.commons.logging.Log;
15 import org.springframework.http.HttpEntity;
16 import org.springframework.http.HttpHeaders;
17 import org.springframework.http.HttpMethod;
18 import org.springframework.http.MediaType;
19 import org.springframework.http.ResponseEntity;
20 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
21 import org.springframework.web.client.HttpClientErrorException;
22 import org.springframework.web.client.HttpServerErrorException;
23 import org.springframework.web.client.RestTemplate;
24
25 import com.fasterxml.jackson.databind.ObjectMapper;
26 import com.fasterxml.jackson.databind.module.SimpleModule;
27 import com.google.common.collect.Lists;
28
29 import net.avcompris.commons3.api.exception.ServiceException;
30 import net.avcompris.commons3.api.exception.UnexpectedException;
31 import net.avcompris.commons3.client.DataBeanDeserializer;
32 import net.avcompris.commons3.client.SessionPropagator;
33 import net.avcompris.commons3.utils.LogFactory;
34
35 public abstract class AbstractClient {
36
37 protected final String baseURL;
38 protected final SessionPropagator sessionPropagator;
39
40 private static final Log logger = LogFactory.getLog(AbstractClient.class);
41
42 protected AbstractClient(final String baseURL, @Nullable final SessionPropagator sessionPropagator) {
43
44 this.baseURL = checkNotNull(baseURL, "baseURL");
45 this.sessionPropagator = sessionPropagator;
46
47 checkArgument(!isBlank(baseURL), "baseURL should not be empty");
48 }
49
50 protected static <U extends V, V> RestTemplate createRestTemplate() {
51
52 final SimpleModule module = new SimpleModule();
53
54 return createRestTemplate(module);
55 }
56
57 protected static <U extends V, V> RestTemplate createRestTemplate(final Class<?> responseClass) {
58
59 checkNotNull(responseClass, "responseClass");
60
61 final SimpleModule module = new SimpleModule();
62
63 addResponseDeserializer(module, responseClass);
64
65 return createRestTemplate(module);
66 }
67
68 protected static <U, V extends U> RestTemplate createRestTemplate(final Class<?> responseClass,
69 final Class<U> subClass1, final Class<V> subImplClass1
70 ) {
71
72 checkNotNull(responseClass, "responseClass");
73 checkNotNull(subClass1, "subClass1");
74 checkNotNull(subImplClass1, "subImplClass1");
75
76 final SimpleModule module = new SimpleModule();
77
78 addResponseDeserializer(module, responseClass);
79
80 final DataBeanDeserializer<V> deserializer = addResponseDeserializer(module, subImplClass1);
81
82 module.addDeserializer(subClass1, deserializer);
83
84 return createRestTemplate(module);
85 }
86
87 private static RestTemplate createRestTemplate(final SimpleModule module) {
88
89 final RestTemplate restTemplate = new RestTemplate();
90
91 final ObjectMapper objectMapper = new ObjectMapper();
92
93 objectMapper.registerModule(module);
94
95 final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(
96 objectMapper);
97
98 mappingJackson2HttpMessageConverter.setDefaultCharset(Charset.forName(UTF_8));
99
100 mappingJackson2HttpMessageConverter.setSupportedMediaTypes(
101 Lists.newArrayList(MediaType.APPLICATION_JSON, MediaType.parseMediaType("application/*+json")));
102
103 restTemplate.getMessageConverters()
104 .removeIf(messageConverter -> messageConverter.getClass() == MappingJackson2HttpMessageConverter.class);
105
106 restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
107
108 return restTemplate;
109 }
110
111 private static <T> DataBeanDeserializer<T> addResponseDeserializer(final SimpleModule module,
112 final Class<T> responseClass) {
113
114 final DataBeanDeserializer<T> deserializer = new DataBeanDeserializer<T>(responseClass);
115
116 module.addDeserializer(responseClass, deserializer);
117
118 return deserializer;
119 }
120
121 protected static HttpHeaders headers(final String correlationId, final SessionPropagator sessionPropagator) {
122
123 checkNotNull(correlationId, "correlationId");
124 checkNotNull(sessionPropagator, "sessionPropagator");
125
126 return headers(correlationId, sessionPropagator.getAuthorizationHeader(), sessionPropagator.getUserSessionId());
127 }
128
129 protected static HttpHeaders headers(@Nullable final String correlationId,
130 @Nullable final String authorizationHeader, @Nullable final String userSessionId) {
131
132
133
134
135
136 final HttpHeaders headers = new HttpHeaders();
137
138 if (correlationId != null) {
139 headers.add("Correlation-ID", correlationId);
140 }
141
142 if (authorizationHeader != null) {
143 headers.add(AUTHORIZATION, authorizationHeader);
144 }
145
146 if (userSessionId != null) {
147 headers.add(COOKIE, "user_session_id=" + userSessionId);
148 }
149
150 return headers;
151 }
152
153 @FunctionalInterface
154 protected static interface Action<T> {
155
156 T execute();
157 }
158
159 @FunctionalInterface
160 protected static interface ActionVoid {
161
162 void execute();
163 }
164
165 protected static <T> T wrap(final Action<T> action) throws ServiceException {
166
167 checkNotNull(action, "action");
168
169 try {
170
171 return action.execute();
172
173 } catch (final HttpServerErrorException | HttpClientErrorException e) {
174
175 final String body = e.getResponseBodyAsString();
176
177 logger.error(body, e);
178
179 throw new UnexpectedException(e);
180 }
181 }
182
183 protected static void wrap(final ActionVoid action) throws ServiceException {
184
185 checkNotNull(action, "action");
186
187 try {
188
189 action.execute();
190
191 } catch (final HttpServerErrorException | HttpClientErrorException e) {
192
193 final String body = e.getResponseBodyAsString();
194
195 logger.error(body, e);
196
197 throw new UnexpectedException(e);
198 }
199 }
200
201 protected final <T, U> ResponseEntity<T> exchange(
202 final RestTemplate restTemplate,
203 final String uri,
204 final HttpMethod method,
205 final HttpEntity<U> request,
206 final Class<T> clazz) {
207
208 checkNotNull(restTemplate, "restTemplate");
209 checkNotNull(uri, "uri");
210 checkNotNull(method, "method");
211 checkNotNull(request, "request");
212 checkNotNull(clazz, "clazz");
213
214 if (logger.isInfoEnabled()) {
215 logger.info("exchange(), method: " + method + ", uri: " + uri + ", class: " + clazz.getSimpleName());
216 }
217
218 if (logger.isDebugEnabled()) {
219 logger.debug("exchange(), request.body: " + request.getBody());
220 }
221
222 return restTemplate.exchange(uri, method, request, clazz);
223 }
224
225 protected final <U> void postForLocation(
226 final RestTemplate restTemplate,
227 final String uri,
228 final HttpEntity<U> request) {
229
230 checkNotNull(restTemplate, "restTemplate");
231 checkNotNull(uri, "uri");
232 checkNotNull(request, "request");
233
234 if (logger.isInfoEnabled()) {
235 logger.info("postForLocation(), uri: " + uri);
236 }
237
238 if (logger.isDebugEnabled()) {
239 logger.debug("postForLocation(), request.body: " + request.getBody());
240 }
241
242 restTemplate.postForLocation(uri, request);
243 }
244 }