Skip to content

Commit 0042714

Browse files
authored
Merge pull request #3525 from mapfish/backport/3524-to-3.31
[Backport 3.31] Close resources to stop Apache lib from pending
2 parents 543dfeb + 25a4d09 commit 0042714

10 files changed

+152
-91
lines changed

core/src/main/java/org/mapfish/print/http/ConfigFileResolvingHttpRequestFactory.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
* This request factory will attempt to load resources using {@link
1515
* org.mapfish.print.config.Configuration#loadFile(String)} and {@link
1616
* org.mapfish.print.config.Configuration#isAccessible(String)} to load the resources if the http
17-
* method is GET and will fallback to the normal/wrapped factory to make http requests.
17+
* method is GET and will fall back to the normal/wrapped factory to make http requests.
1818
*/
1919
public final class ConfigFileResolvingHttpRequestFactory implements MfClientHttpRequestFactory {
2020
private final Configuration config;
2121
@Nonnull private final Map<String, String> mdcContext;
2222
private final MfClientHttpRequestFactoryImpl httpRequestFactory;
2323
private final List<RequestConfigurator> callbacks = new CopyOnWriteArrayList<>();
2424

25+
/** Maximum number of attempts to try to fetch the same http request in case it is failing. */
2526
@Value("${httpRequest.fetchRetry.maxNumber}")
2627
private int httpRequestMaxNumberFetchRetry;
2728

@@ -54,7 +55,9 @@ public void register(@Nonnull final RequestConfigurator callback) {
5455
}
5556

5657
@Override
57-
public ClientHttpRequest createRequest(final URI uri, final HttpMethod httpMethod) {
58+
@Nonnull
59+
public ClientHttpRequest createRequest(
60+
@Nonnull final URI uri, @Nonnull final HttpMethod httpMethod) {
5861
return new ConfigFileResolvingRequest(this, uri, httpMethod);
5962
}
6063

core/src/main/java/org/mapfish/print/http/ConfigFileResolvingRequest.java

+43-33
Original file line numberDiff line numberDiff line change
@@ -141,73 +141,83 @@ private ClientHttpResponse doFileRequest() throws IOException {
141141
}
142142

143143
private ClientHttpResponse doHttpRequestWithRetry(final HttpHeaders headers) throws IOException {
144-
AtomicInteger counter = new AtomicInteger();
144+
final AtomicInteger counter = new AtomicInteger();
145145
do {
146146
logFetchingURIResource(headers);
147147
try {
148-
ClientHttpResponse response = attemptToFetchResponse(headers, counter);
148+
ClientHttpResponse response = attemptToFetchResponse(headers);
149149
if (response != null) {
150150
return response;
151+
} else {
152+
if (canRetry(counter)) {
153+
sleepWithExceptionHandling();
154+
LOGGER.debug("Retry fetching {}", this.getURI());
155+
} else {
156+
PrintException printException = new PrintException("Failed fetching " + getURI());
157+
LOGGER.debug("Throwing exception since it cannot retry.", printException);
158+
throw printException;
159+
}
160+
}
161+
} catch (final IOException | RuntimeException e) {
162+
boolean hasSlept = sleepIfPossible(e, counter);
163+
if (!hasSlept) {
164+
throw e;
151165
}
152-
} catch (final IOException e) {
153-
handleIOException(e, counter);
154166
}
155167
} while (true);
156168
}
157169

158170
private void logFetchingURIResource(final HttpHeaders headers) {
159171
// Display headers, one by line <name>: <value>
160172
LOGGER.debug(
161-
"Fetching URI resource {}, headers:\n{}",
173+
"Fetching {}, using headers:\n{}",
162174
this.getURI(),
163175
headers.entrySet().stream()
164176
.map(entry -> entry.getKey() + "=" + String.join(", ", entry.getValue()))
165177
.collect(Collectors.joining("\n")));
166178
}
167179

168-
private ClientHttpResponse attemptToFetchResponse(
169-
final HttpHeaders headers, final AtomicInteger counter) throws IOException {
180+
private ClientHttpResponse attemptToFetchResponse(final HttpHeaders headers) throws IOException {
170181
ClientHttpRequest requestUsed =
171182
this.request != null ? this.request : createRequestFromWrapped(headers);
172183
LOGGER.debug("Executing http request: {}", requestUsed.getURI());
173184
ClientHttpResponse response = executeCallbacksAndRequest(requestUsed);
174-
if (response.getRawStatusCode() < 500) {
175-
LOGGER.debug(
176-
"Fetching success URI resource {}, status code {}",
177-
getURI(),
178-
response.getRawStatusCode());
179-
return response;
180-
}
181-
LOGGER.debug(
182-
"Fetching failed URI resource {}, error code {}", getURI(), response.getRawStatusCode());
183-
if (canRetry(counter)) {
184-
sleepWithExceptionHandling();
185-
LOGGER.debug("Retry fetching URI resource {}", this.getURI());
186-
} else {
187-
throw new PrintException(
188-
String.format(
189-
"Fetching failed URI resource %s, error code %s",
190-
getURI(), response.getRawStatusCode()));
185+
final int minStatusCodeError = HttpStatus.INTERNAL_SERVER_ERROR.value();
186+
int rawStatusCode = minStatusCodeError;
187+
try {
188+
rawStatusCode = response.getRawStatusCode();
189+
if (rawStatusCode < minStatusCodeError) {
190+
LOGGER.debug("Successfully fetched {}, with status code {}", getURI(), rawStatusCode);
191+
return response;
192+
}
193+
LOGGER.debug("Failed fetching {}, with error code {}", getURI(), rawStatusCode);
194+
return null;
195+
} finally {
196+
if (rawStatusCode >= minStatusCodeError) {
197+
response.close();
198+
}
191199
}
192-
return null;
193200
}
194201

195-
private void handleIOException(final IOException e, final AtomicInteger counter)
196-
throws IOException {
197-
202+
private boolean sleepIfPossible(final Exception e, final AtomicInteger counter) {
198203
if (canRetry(counter)) {
199204
sleepWithExceptionHandling();
200-
LOGGER.debug("Retry fetching URI resource {}", this.getURI());
205+
LOGGER.debug("Retry fetching {} following exception", this.getURI(), e);
206+
return true;
201207
} else {
202-
LOGGER.debug("Fetching failed URI resource {}", getURI());
203-
throw e;
208+
LOGGER.debug(
209+
"Reached maximum number of {} allowed requests attempts for {}",
210+
getHttpRequestMaxNumberFetchRetry(),
211+
getURI());
212+
return false;
204213
}
205214
}
206215

207216
private void sleepWithExceptionHandling() {
217+
int sleepMs = configFileResolvingHttpRequestFactory.getHttpRequestFetchRetryIntervalMillis();
218+
LOGGER.debug("Sleeping {} ms before retrying.", sleepMs);
208219
try {
209-
TimeUnit.MILLISECONDS.sleep(
210-
configFileResolvingHttpRequestFactory.getHttpRequestFetchRetryIntervalMillis());
220+
TimeUnit.MILLISECONDS.sleep(sleepMs);
211221
} catch (InterruptedException e) {
212222
Thread.currentThread().interrupt();
213223
throw new PrintException("Interrupted while sleeping", e);

core/src/main/java/org/mapfish/print/http/HttpRequestFetcher.java

+13-9
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,22 @@ private CachedClientHttpResponse(final ClientHttpResponse originalResponse) thro
8787
this.headers = originalResponse.getHeaders();
8888
this.status = originalResponse.getRawStatusCode();
8989
this.statusText = originalResponse.getStatusText();
90-
this.cachedFile =
90+
this.cachedFile = createCachedFile(originalResponse.getBody());
91+
}
92+
93+
private File createCachedFile(final InputStream originalBody) throws IOException {
94+
File tempFile =
9195
File.createTempFile("cacheduri", null, HttpRequestFetcher.this.temporaryDirectory);
92-
LOGGER.debug("Caching URI resource to {}", this.cachedFile);
93-
try (OutputStream os = Files.newOutputStream(this.cachedFile.toPath())) {
94-
InputStream body = originalResponse.getBody();
96+
LOGGER.debug("Caching URI resource to {}", tempFile);
97+
try (OutputStream os = Files.newOutputStream(tempFile.toPath())) {
9598
LOGGER.debug(
96-
"Get from input stream {}, for response {}, body available: {}",
97-
body.getClass(),
98-
originalResponse.getClass(),
99-
body.available());
100-
IOUtils.copy(body, os);
99+
"Get from input stream {}, body available: {}",
100+
originalBody.getClass(),
101+
originalBody.available());
102+
IOUtils.copy(originalBody, os);
103+
originalBody.close();
101104
}
105+
return tempFile;
102106
}
103107

104108
@Override

core/src/main/java/org/mapfish/print/http/MfClientHttpRequestFactoryImpl.java

+50-10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Collections;
1313
import java.util.List;
1414
import java.util.Map;
15+
import java.util.concurrent.TimeUnit;
1516
import java.util.concurrent.atomic.AtomicInteger;
1617
import javax.annotation.Nonnull;
1718
import javax.annotation.Nullable;
@@ -31,6 +32,7 @@
3132
import org.apache.http.protocol.HTTP;
3233
import org.apache.http.protocol.HttpContext;
3334
import org.mapfish.print.config.Configuration;
35+
import org.mapfish.print.servlet.job.impl.ThreadPoolJobManager;
3436
import org.slf4j.Logger;
3537
import org.slf4j.LoggerFactory;
3638
import org.springframework.http.HttpHeaders;
@@ -52,30 +54,53 @@ public class MfClientHttpRequestFactoryImpl extends HttpComponentsClientHttpRequ
5254
* @param maxConnTotal Maximum total connections.
5355
* @param maxConnPerRoute Maximum connections per route.
5456
*/
55-
public MfClientHttpRequestFactoryImpl(final int maxConnTotal, final int maxConnPerRoute) {
56-
super(createHttpClient(maxConnTotal, maxConnPerRoute));
57+
public MfClientHttpRequestFactoryImpl(
58+
final int maxConnTotal,
59+
final int maxConnPerRoute,
60+
final ThreadPoolJobManager threadPoolJobManager) {
61+
super(createHttpClient(maxConnTotal, maxConnPerRoute, threadPoolJobManager));
5762
}
5863

5964
@Nullable
6065
static Configuration getCurrentConfiguration() {
6166
return CURRENT_CONFIGURATION.get();
6267
}
6368

64-
private static int getIntProperty(final String name) {
69+
/**
70+
* Return the number of milliseconds until the timeout Use the Automatic cancellation timeout if
71+
* not defined.
72+
*
73+
* @param name timeout idemtifier
74+
* @return the number of milliseconds until the timeout
75+
*/
76+
private static int getTimeoutValue(
77+
final String name, final ThreadPoolJobManager threadPoolJobManager) {
6578
final String value = System.getProperty(name);
6679
if (value == null) {
67-
return -1;
80+
long millis = TimeUnit.SECONDS.toMillis(threadPoolJobManager.getTimeout());
81+
if (millis > Integer.MAX_VALUE) {
82+
LOGGER.warn(
83+
"The value of {} is too large. The timeout will be set to the maximum value of {}",
84+
name,
85+
Integer.MAX_VALUE);
86+
return Integer.MAX_VALUE;
87+
} else {
88+
return Integer.parseInt(Long.toString(millis));
89+
}
6890
}
6991
return Integer.parseInt(value);
7092
}
7193

7294
private static CloseableHttpClient createHttpClient(
73-
final int maxConnTotal, final int maxConnPerRoute) {
95+
final int maxConnTotal,
96+
final int maxConnPerRoute,
97+
final ThreadPoolJobManager threadPoolJobManager) {
7498
final RequestConfig requestConfig =
7599
RequestConfig.custom()
76-
.setConnectionRequestTimeout(getIntProperty("http.connectionRequestTimeout"))
77-
.setConnectTimeout(getIntProperty("http.connectTimeout"))
78-
.setSocketTimeout(getIntProperty("http.socketTimeout"))
100+
.setConnectionRequestTimeout(
101+
getTimeoutValue("http.connectionRequestTimeout", threadPoolJobManager))
102+
.setConnectTimeout(getTimeoutValue("http.connectTimeout", threadPoolJobManager))
103+
.setSocketTimeout(getTimeoutValue("http.socketTimeout", threadPoolJobManager))
79104
.build();
80105

81106
final HttpClientBuilder httpClientBuilder =
@@ -89,11 +114,19 @@ private static CloseableHttpClient createHttpClient(
89114
.setMaxConnTotal(maxConnTotal)
90115
.setMaxConnPerRoute(maxConnPerRoute)
91116
.setUserAgent(UserAgentCreator.getUserAgent());
92-
return httpClientBuilder.build();
117+
CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
118+
LOGGER.debug(
119+
"Created CloseableHttpClient using connectionRequestTimeout: {} connectTimeout: {}"
120+
+ " socketTimeout: {}",
121+
getTimeoutValue("http.connectionRequestTimeout", threadPoolJobManager),
122+
getTimeoutValue("http.connectTimeout", threadPoolJobManager),
123+
getTimeoutValue("http.socketTimeout", threadPoolJobManager));
124+
return closeableHttpClient;
93125
}
94126

95127
// allow extension only for testing
96128
@Override
129+
@Nonnull
97130
public ConfigurableRequest createRequest(
98131
@Nonnull final URI uri, @Nonnull final HttpMethod httpMethod) {
99132
HttpRequestBase httpRequest = (HttpRequestBase) createHttpUriRequest(httpMethod, uri);
@@ -161,20 +194,24 @@ public HttpMethod getMethod() {
161194
return HttpMethod.valueOf(this.request.getMethod());
162195
}
163196

197+
@Nonnull
164198
@Override
165199
public String getMethodValue() {
166200
return this.request.getMethod();
167201
}
168202

203+
@Nonnull
169204
public URI getURI() {
170205
return this.request.getURI();
171206
}
172207

208+
@Nonnull
173209
@Override
174210
protected OutputStream getBodyInternal(@Nonnull final HttpHeaders headers) {
175211
return this.outputStream;
176212
}
177213

214+
@Nonnull
178215
@Override
179216
protected Response executeInternal(@Nonnull final HttpHeaders headers) throws IOException {
180217
CURRENT_CONFIGURATION.set(this.configuration);
@@ -207,7 +244,7 @@ protected Response executeInternal(@Nonnull final HttpHeaders headers) throws IO
207244
}
208245
}
209246

210-
static class Response extends AbstractClientHttpResponse {
247+
public static class Response extends AbstractClientHttpResponse {
211248
private static final Logger LOGGER = LoggerFactory.getLogger(Response.class);
212249
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
213250
private final HttpResponse response;
@@ -224,6 +261,7 @@ public int getRawStatusCode() {
224261
return this.response.getStatusLine().getStatusCode();
225262
}
226263

264+
@Nonnull
227265
@Override
228266
public String getStatusText() {
229267
return this.response.getStatusLine().getReasonPhrase();
@@ -247,6 +285,7 @@ public void close() {
247285
LOGGER.trace("Closed Http Response object: {}", this.id);
248286
}
249287

288+
@Nonnull
250289
@Override
251290
public synchronized InputStream getBody() throws IOException {
252291
if (this.inputStream == null) {
@@ -262,6 +301,7 @@ public synchronized InputStream getBody() throws IOException {
262301
return this.inputStream;
263302
}
264303

304+
@Nonnull
265305
@Override
266306
public HttpHeaders getHeaders() {
267307
final HttpHeaders translatedHeaders = new HttpHeaders();

core/src/main/java/org/mapfish/print/servlet/job/impl/ThreadPoolJobManager.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ public final void setTimeout(final long timeout) {
136136
this.timeout = timeout;
137137
}
138138

139+
public final long getTimeout() {
140+
return this.timeout;
141+
}
142+
139143
public final void setAbandonedTimeout(final long abandonedTimeout) {
140144
this.abandonedTimeout = abandonedTimeout;
141145
}
@@ -452,10 +456,13 @@ private boolean updateRegistry() {
452456
final SubmittedPrintJob printJob = submittedJobs.next();
453457
if (!printJob.getReportFuture().isDone()
454458
&& (isTimeoutExceeded(printJob) || isAbandoned(printJob))) {
455-
LOGGER.info("Canceling job after timeout {}", printJob.getEntry().getReferenceId());
459+
LOGGER.info(
460+
"About to attempt timeout based automatic cancellation of job {}",
461+
printJob.getEntry().getReferenceId());
456462
if (!printJob.getReportFuture().cancel(true)) {
457463
LOGGER.info(
458-
"Could not cancel job after timeout {}", printJob.getEntry().getReferenceId());
464+
"Automatic cancellation after timeout failed for job {}",
465+
printJob.getEntry().getReferenceId());
459466
}
460467
// remove all canceled tasks from the work queue (otherwise the queue comparator
461468
// might stumble on non-PrintJob entries)

core/src/main/resources/mapfish-spring-application-context.xml

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
<bean id="healthCheckRegistry" class="org.mapfish.print.metrics.HealthCheckingRegistry"/>
6363
<bean id="httpClientFactory" class="org.mapfish.print.http.MfClientHttpRequestFactoryImpl">
6464
<constructor-arg index="0" value="${maxConnectionsTotal}" />
65-
<constructor-arg index="1" value="${maxConnectionsPerRoute}" />
65+
<constructor-arg index="1" value="${maxConnectionsPerRoute}"/>
66+
<constructor-arg index="2" ref="jobManager"/>
6667
</bean>
6768
<bean id="metricNameStrategy" class="org.mapfish.print.metrics.MetricsNameStrategyFactory" factory-method="hostAndMethod" />
6869
<bean id="loggingMetricsConfigurator" class="org.mapfish.print.metrics.LoggingMetricsConfigurator" lazy-init="false"/>

0 commit comments

Comments
 (0)