adapt master to develop
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / eu / etaxonomy / taxeditor / service / TimestampingHttpInvokerRequestExecutor.java
1 /**
2 * Copyright (C) 2020 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.taxeditor.service;
10
11 import java.io.IOException;
12 import java.net.URI;
13 import java.time.LocalDateTime;
14 import java.time.format.DateTimeFormatter;
15
16 import org.apache.http.Header;
17 import org.apache.http.HttpResponse;
18 import org.apache.http.client.HttpClient;
19 import org.apache.http.client.methods.HttpPost;
20 import org.apache.http.config.Registry;
21 import org.apache.http.config.RegistryBuilder;
22 import org.apache.http.conn.socket.ConnectionSocketFactory;
23 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
24 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
25 import org.apache.http.impl.NoConnectionReuseStrategy;
26 import org.apache.http.impl.client.HttpClientBuilder;
27 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
28 import org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor;
29 import org.springframework.remoting.httpinvoker.HttpInvokerClientConfiguration;
30
31 import eu.etaxonomy.taxeditor.remoting.RemoteExecutionTimestampsUtil;
32
33 /**
34 * HttpInvokerRequestExecutor which extends the spring
35 * {@link HttpComponentsHttpInvokerRequestExecutor} by two functionalities:
36 *
37 * <ol>
38 * <li>Records time stamps when sending the request and when receiving the response.</li>
39 * <li>Allows custom configuration of the {@link PoolingHttpClientConnectionManager}
40 * (see <a href="https://dev.e-taxonomy.eu/redmine/issues/8812">https://dev.e-taxonomy.eu/redmine/issues/8812</a>).
41 * </ol>
42 *
43 * @author a.kohlbecker
44 * @since Jan 17, 2020
45 */
46 public class TimestampingHttpInvokerRequestExecutor extends HttpComponentsHttpInvokerRequestExecutor {
47
48 private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 100;
49
50 private static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5;
51
52 /**
53 * Default in PoolingHttpClientConnectionManager is 2000 milliseconds.
54 *
55 * see https://stackoverflow.com/questions/10558791/apache-httpclient-interim-error-nohttpresponseexception
56 */
57 private static final int VALIDATE_AFTER_INACTIVITY = 2000;
58
59 public TimestampingHttpInvokerRequestExecutor() {
60 super(createDefaultHttpClient());
61 }
62
63 @Override
64 protected HttpResponse executeHttpPost(HttpInvokerClientConfiguration config, HttpClient httpClient,
65 HttpPost httpPost) throws IOException {
66
67 setExecutionRequestClientTimestamp(httpPost.getURI());
68
69 HttpResponse response = super.executeHttpPost(config, httpClient, httpPost);
70
71 setExecutionResponseHttpHeaderTimestamp(response);
72
73 return response;
74 }
75
76 private void setExecutionRequestClientTimestamp(URI requestURI){
77
78 LocalDateTime date = LocalDateTime.now();
79 String dateTimeStr = date.format(DateTimeFormatter.ISO_DATE_TIME);
80 RemoteExecutionTimestampsUtil.setLastRequestClientTime(dateTimeStr);
81 if(requestURI != null){
82 RemoteExecutionTimestampsUtil.setLastService(requestURI.toString());
83 }
84 }
85
86 private void setExecutionResponseHttpHeaderTimestamp(HttpResponse respone){
87 Header dateHeader = respone.getFirstHeader("Date");
88 if(dateHeader != null){
89 RemoteExecutionTimestampsUtil.setLastResponseHttpHeaderTime(dateHeader.getValue());
90 }
91 }
92
93 private static HttpClient createDefaultHttpClient() {
94 Registry<ConnectionSocketFactory> schemeRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
95 .register("http", PlainConnectionSocketFactory.getSocketFactory())
96 .register("https", SSLConnectionSocketFactory.getSocketFactory())
97 .build();
98
99 PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(schemeRegistry);
100 connectionManager.setMaxTotal(DEFAULT_MAX_TOTAL_CONNECTIONS);
101 connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);
102 connectionManager.setValidateAfterInactivity(VALIDATE_AFTER_INACTIVITY);
103
104 return HttpClientBuilder.create()
105 .setConnectionManager(connectionManager)
106 .setConnectionReuseStrategy(new NoConnectionReuseStrategy()) // see #8812
107 .build();
108 }
109 }