Skip to content

Commit 8672045

Browse files
fix-1309 http connection enhancements
1 parent a5dc6b8 commit 8672045

File tree

10 files changed

+510
-7
lines changed

10 files changed

+510
-7
lines changed

geowebcache/core/src/main/java/org/geowebcache/config/GeoWebCacheConfiguration.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ public class GeoWebCacheConfiguration {
7272

7373
private Boolean wmtsCiteCompliant;
7474

75+
private HttpConnectionSettings httpConnectionSettings;
76+
7577
/** The persisted list of layers */
7678
private List<TileLayer> layers;
7779

@@ -286,4 +288,14 @@ public boolean isWmtsCiteCompliant() {
286288
public void setWmtsCiteCompliant(boolean wmtsCiteCompliant) {
287289
this.wmtsCiteCompliant = wmtsCiteCompliant;
288290
}
291+
292+
/** @return the HTTP connection settings */
293+
public HttpConnectionSettings getHttpConnectionSettings() {
294+
return httpConnectionSettings;
295+
}
296+
297+
/** @param httpConnectionSettings the HTTP connection settings to set */
298+
public void setHttpConnectionSettings(HttpConnectionSettings httpConnectionSettings) {
299+
this.httpConnectionSettings = httpConnectionSettings;
300+
}
289301
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
3+
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
4+
* later version.
5+
*
6+
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
7+
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8+
*
9+
* <p>You should have received a copy of the GNU Lesser General Public License along with this program. If not, see
10+
* <http://www.gnu.org/licenses/>.
11+
*
12+
* @author Lennart Juette, PTV AG (http://www.ptvag.com) 2010
13+
*/
14+
package org.geowebcache.config;
15+
16+
import java.util.Objects;
17+
18+
/**
19+
* Configuration class for global HTTP connection settings used by all WMS layers. This replaces the per-layer
20+
* concurrency parameter which was ineffective due to the static singleton connection manager.
21+
*/
22+
public class HttpConnectionSettings {
23+
24+
/** Default maximum total connections */
25+
public static final int DEFAULT_MAX_CONNECTIONS_TOTAL = 20;
26+
27+
/** Default maximum connections per route */
28+
public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 2;
29+
30+
/** Default connection timeout in seconds */
31+
public static final int DEFAULT_CONNECTION_TIMEOUT = 30;
32+
33+
/** Default socket timeout in seconds */
34+
public static final int DEFAULT_SOCKET_TIMEOUT = 60;
35+
36+
private Integer maxConnectionsTotal;
37+
private Integer maxConnectionsPerRoute;
38+
private Integer connectionTimeout;
39+
private Integer socketTimeout;
40+
41+
public HttpConnectionSettings() {
42+
// Default constructor for XStream
43+
}
44+
45+
public HttpConnectionSettings(
46+
Integer maxConnectionsTotal,
47+
Integer maxConnectionsPerRoute,
48+
Integer connectionTimeout,
49+
Integer socketTimeout) {
50+
this.maxConnectionsTotal = maxConnectionsTotal;
51+
this.maxConnectionsPerRoute = maxConnectionsPerRoute;
52+
this.connectionTimeout = connectionTimeout;
53+
this.socketTimeout = socketTimeout;
54+
}
55+
56+
/** @return the maximum total number of connections in the connection pool */
57+
public int getMaxConnectionsTotal() {
58+
return maxConnectionsTotal != null ? maxConnectionsTotal : DEFAULT_MAX_CONNECTIONS_TOTAL;
59+
}
60+
61+
/** @param maxConnectionsTotal the maximum total number of connections to set */
62+
public void setMaxConnectionsTotal(Integer maxConnectionsTotal) {
63+
this.maxConnectionsTotal = maxConnectionsTotal;
64+
}
65+
66+
/** @return the maximum number of connections per route (per backend server) */
67+
public int getMaxConnectionsPerRoute() {
68+
return maxConnectionsPerRoute != null ? maxConnectionsPerRoute : DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
69+
}
70+
71+
/** @param maxConnectionsPerRoute the maximum connections per route to set */
72+
public void setMaxConnectionsPerRoute(Integer maxConnectionsPerRoute) {
73+
this.maxConnectionsPerRoute = maxConnectionsPerRoute;
74+
}
75+
76+
/** @return the connection timeout in seconds */
77+
public int getConnectionTimeout() {
78+
return connectionTimeout != null ? connectionTimeout : DEFAULT_CONNECTION_TIMEOUT;
79+
}
80+
81+
/** @param connectionTimeout the connection timeout to set */
82+
public void setConnectionTimeout(Integer connectionTimeout) {
83+
this.connectionTimeout = connectionTimeout;
84+
}
85+
86+
/** @return the socket timeout in seconds */
87+
public int getSocketTimeout() {
88+
return socketTimeout != null ? socketTimeout : DEFAULT_SOCKET_TIMEOUT;
89+
}
90+
91+
/** @param socketTimeout the socket timeout to set */
92+
public void setSocketTimeout(Integer socketTimeout) {
93+
this.socketTimeout = socketTimeout;
94+
}
95+
96+
@Override
97+
public boolean equals(Object obj) {
98+
if (this == obj) return true;
99+
if (obj == null || getClass() != obj.getClass()) return false;
100+
HttpConnectionSettings that = (HttpConnectionSettings) obj;
101+
return Objects.equals(maxConnectionsTotal, that.maxConnectionsTotal)
102+
&& Objects.equals(maxConnectionsPerRoute, that.maxConnectionsPerRoute)
103+
&& Objects.equals(connectionTimeout, that.connectionTimeout)
104+
&& Objects.equals(socketTimeout, that.socketTimeout);
105+
}
106+
107+
@Override
108+
public int hashCode() {
109+
return Objects.hash(maxConnectionsTotal, maxConnectionsPerRoute, connectionTimeout, socketTimeout);
110+
}
111+
112+
@Override
113+
public String toString() {
114+
return "HttpConnectionSettings{" + "maxConnectionsTotal="
115+
+ getMaxConnectionsTotal() + ", maxConnectionsPerRoute="
116+
+ getMaxConnectionsPerRoute() + ", connectionTimeout="
117+
+ getConnectionTimeout() + ", socketTimeout="
118+
+ getSocketTimeout() + '}';
119+
}
120+
}

geowebcache/core/src/main/java/org/geowebcache/config/XMLConfiguration.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import org.geowebcache.storage.UnsuitableStorageException;
8989
import org.geowebcache.util.ApplicationContextProvider;
9090
import org.geowebcache.util.ExceptionUtils;
91+
import org.geowebcache.util.HttpClientConnectionManagerFactory;
9192
import org.geowebcache.util.URLs;
9293
import org.springframework.beans.factory.InitializingBean;
9394
import org.springframework.beans.factory.annotation.Autowired;
@@ -801,6 +802,9 @@ public void afterPropertiesSet() throws GeoWebCacheException {
801802

802803
this.setGwcConfig(loadConfiguration());
803804

805+
// Initialize HTTP connection manager with configuration settings
806+
initializeHttpConnectionManager();
807+
804808
log.config("Initializing GridSets from " + getIdentifier());
805809

806810
getGridSetsInternal();
@@ -855,6 +859,29 @@ private void initialize(final TileLayer layer) {
855859
layer.initialize(gridSetBroker);
856860
}
857861

862+
/**
863+
* Initialize the HTTP connection manager with settings from the configuration. This replaces the static singleton
864+
* approach with a configurable solution.
865+
*/
866+
private void initializeHttpConnectionManager() {
867+
try {
868+
HttpConnectionSettings settings = getGwcConfig().getHttpConnectionSettings();
869+
if (settings == null) {
870+
// Use default settings if none configured
871+
settings = new HttpConnectionSettings();
872+
log.info("No HTTP connection settings found in configuration, using defaults: " + settings);
873+
} else {
874+
log.info("Initializing HTTP connection manager with settings: " + settings);
875+
}
876+
877+
HttpClientConnectionManagerFactory.getInstance().initialize(settings);
878+
} catch (Exception e) {
879+
log.warning("Failed to initialize HTTP connection manager with configuration settings: " + e.getMessage());
880+
// Fall back to default settings
881+
HttpClientConnectionManagerFactory.getInstance().initialize(new HttpConnectionSettings());
882+
}
883+
}
884+
858885
/** @see TileLayerConfiguration#getIdentifier() */
859886
@Override
860887
public String getIdentifier() {

geowebcache/core/src/main/java/org/geowebcache/layer/wms/WMSLayer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ public enum HttpRequestMode {
106106
@SuppressWarnings("unused")
107107
private String cachePrefix;
108108

109+
/**
110+
* @deprecated This parameter is deprecated and ineffective due to the static singleton connection manager. Use
111+
* global HTTP connection settings in geowebcache.xml instead.
112+
*/
113+
@Deprecated
109114
private Integer concurrency;
110115

111116
// private transient int expireCacheInt = -1;
@@ -685,6 +690,9 @@ public void setSourceHelper(WMSSourceHelper source) {
685690
log.fine("Setting sourceHelper on " + this.name);
686691
this.sourceHelper = source;
687692
if (concurrency != null) {
693+
log.warning("Layer '" + this.name + "' uses deprecated 'concurrency' parameter. "
694+
+ "This parameter is ineffective due to the static singleton connection manager. "
695+
+ "Use global HTTP connection settings in geowebcache.xml instead.");
688696
this.sourceHelper.setConcurrency(concurrency);
689697
} else {
690698
this.sourceHelper.setConcurrency(32);

geowebcache/core/src/main/java/org/geowebcache/util/HttpClientBuilder.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020
import org.apache.http.client.HttpClient;
2121
import org.apache.http.client.config.CookieSpecs;
2222
import org.apache.http.client.config.RequestConfig;
23-
import org.apache.http.conn.HttpClientConnectionManager;
2423
import org.apache.http.impl.client.BasicCredentialsProvider;
25-
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
2624
import org.geotools.util.logging.Logging;
2725

2826
/** Builder class for HttpClients */
@@ -35,7 +33,8 @@ public class HttpClientBuilder {
3533
private AuthScope authscope = null;
3634

3735
private Integer backendTimeoutMillis = null;
38-
private static final HttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
36+
private static final HttpClientConnectionManagerFactory connectionManagerFactory =
37+
HttpClientConnectionManagerFactory.getInstance();
3938

4039
private boolean doAuthentication = false;
4140

@@ -71,8 +70,9 @@ public HttpClientBuilder(
7170

7271
clientBuilder = org.apache.http.impl.client.HttpClientBuilder.create();
7372
clientBuilder.useSystemProperties();
74-
clientBuilder.setConnectionManager(connectionManager);
75-
clientBuilder.setMaxConnTotal(concurrency);
73+
clientBuilder.setConnectionManager(connectionManagerFactory.getConnectionManager());
74+
// Note: concurrency parameter is now handled globally via HttpConnectionSettings
75+
// The per-layer concurrency parameter is deprecated and ineffective
7676
}
7777

7878
/*
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/**
2+
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
3+
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
4+
* later version.
5+
*
6+
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
7+
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8+
*
9+
* <p>You should have received a copy of the GNU Lesser General Public License along with this program. If not, see
10+
* <http://www.gnu.org/licenses/>.
11+
*
12+
* @author Lennart Juette, PTV AG (http://www.ptvag.com) 2010
13+
*/
14+
package org.geowebcache.util;
15+
16+
import java.util.logging.Logger;
17+
import org.apache.http.conn.HttpClientConnectionManager;
18+
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
19+
import org.geotools.util.logging.Logging;
20+
import org.geowebcache.config.HttpConnectionSettings;
21+
22+
/**
23+
* Factory class for creating and managing HTTP connection managers. This replaces the static singleton approach with a
24+
* configurable solution that properly respects the connection settings.
25+
*/
26+
public class HttpClientConnectionManagerFactory {
27+
28+
static final Logger log = Logging.getLogger(HttpClientConnectionManagerFactory.class.toString());
29+
30+
private static volatile HttpClientConnectionManagerFactory instance;
31+
private volatile HttpClientConnectionManager connectionManager;
32+
private volatile HttpConnectionSettings settings;
33+
34+
private HttpClientConnectionManagerFactory() {
35+
// Private constructor for singleton
36+
}
37+
38+
/** Get the singleton instance of the factory */
39+
public static HttpClientConnectionManagerFactory getInstance() {
40+
if (instance == null) {
41+
synchronized (HttpClientConnectionManagerFactory.class) {
42+
if (instance == null) {
43+
instance = new HttpClientConnectionManagerFactory();
44+
}
45+
}
46+
}
47+
return instance;
48+
}
49+
50+
/**
51+
* Initialize the connection manager with the given settings. This method should be called once during application
52+
* startup.
53+
*
54+
* @param settings the HTTP connection settings to use
55+
*/
56+
public synchronized void initialize(HttpConnectionSettings settings) {
57+
if (this.settings != null && this.settings.equals(settings)) {
58+
// Settings haven't changed, no need to recreate
59+
return;
60+
}
61+
62+
this.settings = settings != null ? settings : new HttpConnectionSettings();
63+
64+
// Close existing connection manager if it exists
65+
if (connectionManager != null) {
66+
try {
67+
connectionManager.shutdown();
68+
} catch (Exception e) {
69+
log.warning("Error closing existing connection manager: " + e.getMessage());
70+
}
71+
}
72+
73+
// Create new connection manager with proper settings
74+
PoolingHttpClientConnectionManager newManager = new PoolingHttpClientConnectionManager();
75+
newManager.setMaxTotal(this.settings.getMaxConnectionsTotal());
76+
newManager.setDefaultMaxPerRoute(this.settings.getMaxConnectionsPerRoute());
77+
78+
this.connectionManager = newManager;
79+
80+
log.info("Initialized HTTP connection manager with settings: " + this.settings);
81+
}
82+
83+
/**
84+
* Get the current connection manager. If not initialized, creates one with default settings.
85+
*
86+
* @return the HTTP connection manager
87+
*/
88+
public HttpClientConnectionManager getConnectionManager() {
89+
if (connectionManager == null) {
90+
synchronized (this) {
91+
if (connectionManager == null) {
92+
// Initialize with default settings if not already done
93+
initialize(new HttpConnectionSettings());
94+
}
95+
}
96+
}
97+
return connectionManager;
98+
}
99+
100+
/**
101+
* Get the current connection settings
102+
*
103+
* @return the current HTTP connection settings
104+
*/
105+
public HttpConnectionSettings getSettings() {
106+
return settings != null ? settings : new HttpConnectionSettings();
107+
}
108+
109+
/** Shutdown the connection manager and release resources. This should be called during application shutdown. */
110+
public synchronized void shutdown() {
111+
if (connectionManager != null) {
112+
try {
113+
connectionManager.shutdown();
114+
connectionManager = null;
115+
settings = null;
116+
log.info("HTTP connection manager shutdown completed");
117+
} catch (Exception e) {
118+
log.warning("Error during connection manager shutdown: " + e.getMessage());
119+
}
120+
}
121+
}
122+
}

geowebcache/core/src/main/resources/geowebcache.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@
9999

100100
</blobStores>
101101

102+
<httpConnectionSettings>
103+
<maxConnectionsTotal>20</maxConnectionsTotal>
104+
<maxConnectionsPerRoute>6</maxConnectionsPerRoute>
105+
<connectionTimeout>30</connectionTimeout>
106+
<socketTimeout>60</socketTimeout>
107+
</httpConnectionSettings>
108+
102109
<gridSets>
103110
<!-- Grid Set Example, by default EPSG:900913 and EPSG:4326 are defined -->
104111
<gridSet>

0 commit comments

Comments
 (0)