a6b3105618602096be1358d351b7e670aab2b3c1
[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 {@link HttpComponentsHttpInvokerRequestExecutor} by two functionalities:
35 *
36 * <ol>
37 * <li>Records time stamps when sending the request and when receiving the response.</li>
38 * <li>Allows custom configuration of the {@link PoolingHttpClientConnectionManager}
39 * (see <a href="https://dev.e-taxonomy.eu/redmine/issues/8812">https://dev.e-taxonomy.eu/redmine/issues/8812</a>).
40 * </ol>
41 *
42 * @author a.kohlbecker
43 * @since Jan 17, 2020
44 */
45 public class TimestampingHttpInvokerRequestExecutor extends HttpComponentsHttpInvokerRequestExecutor {
46
47 private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 100;
48
49 private static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5;
50
51 /**
52 * Default in PoolingHttpClientConnectionManager is 2000 milliseconds.
53 *
54 * see https://stackoverflow.com/questions/10558791/apache-httpclient-interim-error-nohttpresponseexception
55 */
56 private static final int VALIDATE_AFTER_INACTIVITY = 2000;
57
58 public TimestampingHttpInvokerRequestExecutor() {
59 super(createDefaultHttpClient());
60 }
61
62 @Override
63 protected HttpResponse executeHttpPost(HttpInvokerClientConfiguration config, HttpClient httpClient,
64 HttpPost httpPost) throws IOException {
65
66 setExecutionRequestClientTimestamp(httpPost.getURI());
67
68 HttpResponse respone = super.executeHttpPost(config, httpClient, httpPost);
69
70 setExecutionResponseHttpHeaderTimestamp(respone);
71
72 return respone;
73 }
74
75 private void setExecutionRequestClientTimestamp(URI requestURI){
76
77 LocalDateTime date = LocalDateTime.now();
78 String dateTimeStr = date.format(DateTimeFormatter.ISO_DATE_TIME);
79 RemoteExecutionTimestampsUtil.setLastRequestClientTime(dateTimeStr);
80 if(requestURI != null){
81 RemoteExecutionTimestampsUtil.setLastServiceMethod(requestURI.toString());
82 }
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 }