View Javadoc

1   /*
2    * Copyright 2008 University Corporation for Advanced Internet Development, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.ws.soap.client.http;
18  
19  import net.jcip.annotations.NotThreadSafe;
20  
21  import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
22  import org.apache.commons.httpclient.HostConfiguration;
23  import org.apache.commons.httpclient.HttpClient;
24  import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
25  import org.apache.commons.httpclient.UsernamePasswordCredentials;
26  import org.apache.commons.httpclient.auth.AuthScope;
27  import org.apache.commons.httpclient.params.HttpClientParams;
28  import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
29  import org.apache.commons.httpclient.protocol.Protocol;
30  import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
31  import org.opensaml.xml.util.DatatypeHelper;
32  
33  /**
34   * A builder for {@link HttpClient}s.
35   * 
36   * This builder will produce clients that employ the {@link MultiThreadedHttpConnectionManager} and as such users of the
37   * clients MUST be sure to invoke {@link org.apache.commons.httpclient.HttpMethod#releaseConnection()} after they have
38   * finished with the method.
39   */
40  @NotThreadSafe
41  public class HttpClientBuilder {
42  
43      /** Host name of the HTTP proxy server through which connections will be made. */
44      private String proxyHost;
45  
46      /** Port number of the HTTP proxy server through which connections will be made. */
47      private int proxyPort;
48  
49      /** Username used to connect to the HTTP proxy server. */
50      private String proxyUsername;
51  
52      /** Password used to connect to the HTTP proxy server. */
53      private String proxyPassword;
54  
55      /** Whether authentication should be performed preemptively, defaults to false. */
56      private boolean preemptiveAuthentication;
57  
58      /** Character set used for HTTP content, defaults to UTF-8. */
59      private String contentCharSet;
60  
61      /** Amount of time, in milliseconds, to wait for a connection to be established, default is 5,000. */
62      private int connectionTimeout;
63  
64      /** Size of the buffer, in bytes, used to hold outbound information, defaults to 4,096. */
65      private int sendBufferSize;
66  
67      /** Size of the buffer, in bytes, used to hold inbound information, defaults to 16,384. */
68      private int receiveBufferSize;
69  
70      /** Whether to use TCP No Delay algorithm, defaults to true. */
71      private boolean tcpNoDelay;
72  
73      /** Total number of connections allowed to a specific host, defaults to 5. */
74      private int maxConnectionsPerHost;
75  
76      /** Total number of connections allowed to all hosts, defaults to 20. */
77      private int maxTotalConnectons;
78  
79      /** Number of times a failed connection to a host should be retried. */
80      private int connectionRetryAttempts;
81  
82      /** Socket factory used for the 'https' scheme. */
83      private SecureProtocolSocketFactory httpsProtocolSocketFactory;
84  
85      /** Constructor. */
86      public HttpClientBuilder() {
87          resetDefaults();
88      }
89  
90      /** Resets the builder to its default values. */
91      public void resetDefaults() {
92          proxyPort = -1;
93          preemptiveAuthentication = false;
94          contentCharSet = "UTF-8";
95          connectionTimeout = 5000;
96          sendBufferSize = 4096;
97          receiveBufferSize = 16384;
98          tcpNoDelay = true;
99          maxConnectionsPerHost = 5;
100         maxTotalConnectons = 20;
101         connectionRetryAttempts = 0;
102     }
103 
104     /**
105      * Builds an HTTP client with the given settings. Settings are NOT reset to their default values after a client has
106      * been created.
107      * 
108      * @return the created client.
109      */
110     public HttpClient buildClient() {
111         if (httpsProtocolSocketFactory != null) {
112             Protocol.registerProtocol("https", new Protocol("https", httpsProtocolSocketFactory, 443));
113         }
114 
115         HttpClientParams clientParams = new HttpClientParams();
116         clientParams.setAuthenticationPreemptive(isPreemptiveAuthentication());
117         clientParams.setContentCharset(getContentCharSet());
118         clientParams.setParameter(HttpClientParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(
119                 connectionRetryAttempts, false));
120 
121         HttpConnectionManagerParams connMgrParams = new HttpConnectionManagerParams();
122         connMgrParams.setConnectionTimeout(getConnectionTimeout());
123         connMgrParams.setDefaultMaxConnectionsPerHost(getMaxConnectionsPerHost());
124         connMgrParams.setMaxTotalConnections(getMaxTotalConnections());
125         connMgrParams.setReceiveBufferSize(getReceiveBufferSize());
126         connMgrParams.setSendBufferSize(getSendBufferSize());
127         connMgrParams.setTcpNoDelay(isTcpNoDelay());
128 
129         MultiThreadedHttpConnectionManager connMgr = new MultiThreadedHttpConnectionManager();
130         connMgr.setParams(connMgrParams);
131 
132         HttpClient httpClient = new HttpClient(clientParams, connMgr);
133 
134         if (proxyHost != null) {
135             HostConfiguration hostConfig = new HostConfiguration();
136             hostConfig.setProxy(proxyHost, proxyPort);
137             httpClient.setHostConfiguration(hostConfig);
138 
139             if (proxyUsername != null) {
140                 AuthScope proxyAuthScope = new AuthScope(proxyHost, proxyPort);
141                 UsernamePasswordCredentials proxyCredentials = new UsernamePasswordCredentials(proxyUsername,
142                         proxyPassword);
143                 httpClient.getState().setProxyCredentials(proxyAuthScope, proxyCredentials);
144             }
145         }
146 
147         return httpClient;
148     }
149 
150     /**
151      * Gets the host name of the HTTP proxy server through which connections will be made.
152      * 
153      * @return host name of the HTTP proxy server through which connections will be made
154      */
155     public String getProxyHost() {
156         return proxyHost;
157     }
158 
159     /**
160      * Sets the host name of the HTTP proxy server through which connections will be made.
161      * 
162      * @param host host name of the HTTP proxy server through which connections will be made
163      */
164     public void setProxyHost(String host) {
165         proxyHost = DatatypeHelper.safeTrimOrNullString(host);
166     }
167 
168     /**
169      * Gets the port of the HTTP proxy server through which connections will be made.
170      * 
171      * @return port of the HTTP proxy server through which connections will be made
172      */
173     public int getProxyPort() {
174         return proxyPort;
175     }
176 
177     /**
178      * Sets the port of the HTTP proxy server through which connections will be made.
179      * 
180      * @param port port of the HTTP proxy server through which connections will be made
181      */
182     public void setProxyPort(int port) {
183         proxyPort = port;
184     }
185 
186     /**
187      * Gets the username used to connect to the HTTP proxy server.
188      * 
189      * @return username used to connect to the HTTP proxy server
190      */
191     public String getProxyUsername() {
192         return proxyUsername;
193     }
194 
195     /**
196      * Sets the username used to connect to the HTTP proxy server.
197      * 
198      * @param username username used to connect to the HTTP proxy server
199      */
200     public void setProxyUsername(String username) {
201         proxyUsername = DatatypeHelper.safeTrimOrNullString(username);
202     }
203 
204     /**
205      * Gets the password used to connect to the HTTP proxy server.
206      * 
207      * @return password used to connect to the HTTP proxy server
208      */
209     public String getProxyPassword() {
210         return proxyPassword;
211     }
212 
213     /**
214      * Sets the password used to connect to the HTTP proxy server.
215      * 
216      * @param password password used to connect to the HTTP proxy server
217      */
218     public void setProxyPassword(String password) {
219         proxyPassword = DatatypeHelper.safeTrimOrNullString(password);
220     }
221 
222     /**
223      * Gets whether authentication is performed preemptively. Default value is <code>false</code>.
224      * 
225      * @return whether authentication is performed preemptively
226      */
227     public boolean isPreemptiveAuthentication() {
228         return preemptiveAuthentication;
229     }
230 
231     /**
232      * Sets whether authentication is performed preemptively.
233      * 
234      * @param preemptive whether authentication is performed preemptively
235      */
236     public void setPreemptiveAuthentication(boolean preemptive) {
237         preemptiveAuthentication = preemptive;
238     }
239 
240     /**
241      * Gets the character set used for HTTP content. Default value is <code>UTF-8</code>.
242      * 
243      * @return character set used for HTTP content
244      */
245     public String getContentCharSet() {
246         return contentCharSet;
247     }
248 
249     /**
250      * Sets the character set used for HTTP content.
251      * 
252      * @param charSet character set used for HTTP content
253      */
254     public void setContentCharSet(String charSet) {
255         contentCharSet = charSet;
256     }
257 
258     /**
259      * Gets the time, in milliseconds, to wait for connection establishments. Default value is 5,000. A value of 0
260      * indicates there is no timeout.
261      * 
262      * @return time, in milliseconds, to wait for connection establishments
263      */
264     public int getConnectionTimeout() {
265         return connectionTimeout;
266     }
267 
268     /**
269      * Sets the time, in milliseconds, to wait for connection establishments. A value of 0 indicates there is no
270      * timeout.
271      * 
272      * @param timeout time, in milliseconds, to wait for connection establishments
273      */
274     public void setConnectionTimeout(int timeout) {
275         connectionTimeout = timeout;
276     }
277 
278     /**
279      * Gets the size of buffer, in bytes, used when sending content. Default value is 4,096.
280      * 
281      * @return size of buffer, in bytes, used when sending content
282      */
283     public int getSendBufferSize() {
284         return sendBufferSize;
285     }
286 
287     /**
288      * Sets the size of buffer, in bytes, used when sending content.
289      * 
290      * @param size size of buffer, in bytes, used when sending content
291      */
292     public void setSendBufferSize(int size) {
293         sendBufferSize = size;
294     }
295 
296     /**
297      * Gets the size of buffer, in bytes, used when receiving content. Default value is 16,384.
298      * 
299      * @return size of buffer, in bytes, used when sending content
300      */
301     public int getReceiveBufferSize() {
302         return receiveBufferSize;
303     }
304 
305     /**
306      * Sets the size of buffer, in bytes, used when sending content.
307      * 
308      * @param size size of buffer, in bytes, used when sending content
309      */
310     public void setReceiveBufferSize(int size) {
311         receiveBufferSize = size;
312     }
313 
314     /**
315      * Gets whether to use TCP No Delay when sending data. Default value is <code>true</code>.
316      * 
317      * @return whether to use TCP No Delay when sending data
318      */
319     public boolean isTcpNoDelay() {
320         return tcpNoDelay;
321     }
322 
323     /**
324      * Sets whether to use TCP No Delay when sending data.
325      * 
326      * @param noDelay whether to use TCP No Delay when sending data
327      */
328     public void setTcpNoDelay(boolean noDelay) {
329         tcpNoDelay = noDelay;
330     }
331 
332     /**
333      * Gets the maximum number of connections, per host, that the client will create. Default value is 5. A value of 0
334      * indicates there is no maximum.
335      * 
336      * @return maximum number of connections, per host, that the client will create
337      */
338     public int getMaxConnectionsPerHost() {
339         return maxConnectionsPerHost;
340     }
341 
342     /**
343      * Sets the maximum number of connections, per host, that the client will create. A value of 0 indicates there is no
344      * maximum.
345      * 
346      * @param max maximum number of connections, per host, that the client will create
347      */
348     public void setMaxConnectionsPerHost(int max) {
349         maxConnectionsPerHost = max;
350     }
351 
352     /**
353      * Gets the maximum number of total connections the client will create. Default value is 20.
354      * 
355      * @return maximum number of total connections the client will create
356      */
357     public int getMaxTotalConnections() {
358         return maxTotalConnectons;
359     }
360 
361     /**
362      * Sets the maximum number of total connections the client will create.
363      * 
364      * @param max maximum number of total connections the client will create, must be greater than zero
365      */
366     public void setMaxTotalConnections(int max) {
367         if (max < 1) {
368             throw new IllegalArgumentException("Maximum total number of connections must be greater than zero.");
369         }
370         maxTotalConnectons = max;
371     }
372 
373     /**
374      * Gets the number of times a connection will be tried if a host is unreachable.
375      * 
376      * @return number of times a connection will be tried if a host is unreachable
377      */
378     public int getConnectionRetryAttempts() {
379         return connectionRetryAttempts;
380     }
381 
382     /**
383      * Sets the number of times a connection will be tried if a host is unreachable.
384      * 
385      * @param attempts number of times a connection will be tried if a host is unreachable
386      */
387     public void setConnectionRetryAttempts(int attempts) {
388         connectionRetryAttempts = attempts;
389     }
390 
391     /**
392      * Gets the protocol socket factory used for the https scheme.
393      * 
394      * @return protocol socket factory used for the https scheme
395      */
396     public SecureProtocolSocketFactory getHttpsProtocolSocketFactory() {
397         return httpsProtocolSocketFactory;
398     }
399 
400     /**
401      * Sets the protocol socket factory used for the https scheme.
402      * 
403      * @param factory the httpsProtocolSocketFactory to set
404      */
405     public void setHttpsProtocolSocketFactory(SecureProtocolSocketFactory factory) {
406         httpsProtocolSocketFactory = factory;
407     }
408 }