View Javadoc
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 //		if (logger.isDebugEnabled()) {
133 //			logger.debug("headers(): " + authorizationHeader + ", " + userSessionId);
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 }