1313 */
1414package org .geowebcache .layer .wms ;
1515
16+ import com .google .common .annotations .VisibleForTesting ;
1617import java .io .IOException ;
1718import java .io .InputStream ;
1819import java .io .UnsupportedEncodingException ;
2930import org .apache .http .HttpEntity ;
3031import org .apache .http .HttpResponse ;
3132import org .apache .http .NameValuePair ;
33+ import org .apache .http .client .ClientProtocolException ;
3234import org .apache .http .client .HttpClient ;
3335import org .apache .http .client .methods .HttpGet ;
3436import org .apache .http .client .methods .HttpPost ;
3537import org .apache .http .client .methods .HttpRequestBase ;
3638import org .apache .http .entity .StringEntity ;
3739import org .apache .http .message .BasicNameValuePair ;
3840import org .geotools .util .logging .Logging ;
41+ import org .geowebcache .GeoWebCacheEnvironment ;
3942import org .geowebcache .GeoWebCacheException ;
43+ import org .geowebcache .GeoWebCacheExtensions ;
4044import org .geowebcache .io .Resource ;
4145import org .geowebcache .layer .TileResponseReceiver ;
4246import org .geowebcache .mime .ErrorMime ;
4852import org .geowebcache .util .URLs ;
4953import org .springframework .util .Assert ;
5054
51- /** This class is a wrapper for HTTP interaction with WMS backend */
55+ /**
56+ * Helper class for HTTP interactions of {@link WMSLayer} with a WMS backend.
57+ *
58+ * <p>HTTP Basic Auth username and password supplied to the constructor support environment parametrization.
59+ *
60+ * @see GeoWebCacheEnvironment#isAllowEnvParametrization()
61+ * @see GeoWebCacheEnvironment#resolveValue(Object)
62+ */
5263public class WMSHttpHelper extends WMSSourceHelper {
5364 private static final Logger log = Logging .getLogger (WMSHttpHelper .class .getName ());
5465
66+ /**
67+ * Used by {@link #getResolvedHttpUsername()} and {@link #getResolvedHttpPassword()} to
68+ * {@link GeoWebCacheEnvironment#resolveValue resolve} the actual values against environment variables when
69+ * {@link GeoWebCacheEnvironment#isAllowEnvParametrization() ALLOW_ENV_PARAMETRIZATION} is enabled.
70+ */
71+ protected GeoWebCacheEnvironment gwcEnv ;
72+
5573 private final URL proxyUrl ;
5674
75+ /**
76+ * HTTP Basic Auth username, might be de-referenced using environment variable substitution. Always access it
77+ * through {@link #getResolvedHttpUsername()}
78+ */
5779 private final String httpUsername ;
5880
81+ /**
82+ * HTTP Basic Auth password, might be de-referenced using environment variable substitution. Always access it
83+ * through {@link #getResolvedHttpUsername()}
84+ */
5985 private final String httpPassword ;
6086
61- protected volatile HttpClient client ;
87+ protected HttpClient client ;
6288
6389 public WMSHttpHelper () {
6490 this (null , null , null );
@@ -71,23 +97,75 @@ public WMSHttpHelper(String httpUsername, String httpPassword, URL proxyUrl) {
7197 this .proxyUrl = proxyUrl ;
7298 }
7399
74- HttpClient getHttpClient () {
75- if (client == null ) {
76- synchronized (this ) {
77- if (client != null ) {
78- return client ;
79- }
100+ /**
101+ * Used by {@link #executeRequest}
102+ *
103+ * @return the actual http username to use when executing requests
104+ */
105+ public String getResolvedHttpUsername () {
106+ return resolvePlaceHolders (httpUsername );
107+ }
108+
109+ /**
110+ * Used by {@link #executeRequest}
111+ *
112+ * @return the actual http password to use when executing requests
113+ */
114+ public String getResolvedHttpPassword () {
115+ return resolvePlaceHolders (httpPassword );
116+ }
117+
118+ /**
119+ * Assigns the environment variable {@link GeoWebCacheEnvironment#resolveValue resolver} to perform variable
120+ * substitution against the configured http username and password during {@link #executeRequest}
121+ *
122+ * <p>When unset, a bean of this type will be looked up through {@link GeoWebCacheExtensions#bean(Class)}
123+ */
124+ public void setGeoWebCacheEnvironment (GeoWebCacheEnvironment gwcEnv ) {
125+ this .gwcEnv = gwcEnv ;
126+ }
80127
81- HttpClientBuilder builder = new HttpClientBuilder (
82- null , getBackendTimeout (), httpUsername , httpPassword , proxyUrl , getConcurrency ());
128+ private String resolvePlaceHolders (String value ) {
129+ GeoWebCacheEnvironment env = getEnvironment ();
130+ return env != null && env .isAllowEnvParametrization () ? env .resolveValue (value ) : value ;
131+ }
83132
84- client = builder .buildClient ();
85- }
133+ private GeoWebCacheEnvironment getEnvironment () {
134+ GeoWebCacheEnvironment env = this .gwcEnv ;
135+ if (env == null ) {
136+ env = GeoWebCacheExtensions .bean (GeoWebCacheEnvironment .class );
137+ this .gwcEnv = env ;
86138 }
139+ return env ;
140+ }
87141
142+ /**
143+ * Note: synchronizing the entire method avoids double-checked locking altogether. Modern JVMs optimize this and
144+ * reduce the overhead of synchronization. Otherwise PMD complains with a `Double checked locking is not thread safe
145+ * in Java.` error.
146+ */
147+ synchronized HttpClient getHttpClient () {
148+ if (client == null ) {
149+ int backendTimeout = getBackendTimeout ();
150+ String user = getResolvedHttpUsername ();
151+ String password = getResolvedHttpPassword ();
152+ URL proxy = proxyUrl ;
153+ int concurrency = getConcurrency ();
154+ client = buildHttpClient (backendTimeout , user , password , proxy , concurrency );
155+ }
88156 return client ;
89157 }
90158
159+ @ VisibleForTesting
160+ HttpClient buildHttpClient (int backendTimeout , String username , String password , URL proxy , int concurrency ) {
161+
162+ URL serverUrl = null ;
163+ HttpClientBuilder builder =
164+ new HttpClientBuilder (serverUrl , backendTimeout , username , password , proxy , concurrency );
165+
166+ return builder .buildClient ();
167+ }
168+
91169 /** Loops over the different backends, tries the request */
92170 @ Override
93171 protected void makeRequest (
@@ -319,7 +397,13 @@ public HttpResponse executeRequest(
319397 if (log .isLoggable (Level .FINER )) {
320398 log .finer (method .toString ());
321399 }
322- return getHttpClient ().execute (method );
400+ HttpClient httpClient = getHttpClient ();
401+ return execute (httpClient , method );
402+ }
403+
404+ @ VisibleForTesting
405+ HttpResponse execute (HttpClient httpClient , HttpRequestBase method ) throws IOException , ClientProtocolException {
406+ return httpClient .execute (method );
323407 }
324408
325409 private String processRequestParameters (Map <String , String > parameters ) throws UnsupportedEncodingException {
0 commit comments