Spring web service call using proxy per connection

Very often we work behind a firewall and have a proxy server that we route our outgoing requests through. Sometimes we even need to use proxy server in order for the target web service to identify the request sender. In such cases, we need to set a proxy host and a proxy port that all our requests would be sent via. Details on how to set proxy are not so publicly documented in java documentation. However, some googling shalll tell you that you can set the proxy host and port by setting System.properties in jvm as shown below:

            Properties sysProps = System.getProperties();
            sysProps.put("proxySet", "true");
            sysProps.put("proxyHost", "192.168.1.111");
            sysProps.put("proxyPort", "8080");

The example above sets “proxySet” property to true and then sets “proxyHost” and “proxyPort” to the proxy server ip address and the port. You can even define these properties in your spring context and pass them to your class’s init-method that you can again define in spring context. This way you can achieve setting of proxy via spring context. What this does is sets the proxy on JVM. There are times when you would wonder how do I set this just per connection so that it doesn’t affect any other code running in JVM? That is exactly what I shall try to cover in this post.

Some time ago, I wrote a post on how to set socket timeout in spring using http client. To recap, all we did in that exercise was following:

  1. Declare http client dependency in maven pom.
  2. In Spring context, define your custom HttpClientParams by setting soTimeout property on it.
  3. In Spring context, define your custom HttpClient by injecting your custom HttpClientParams onto it.
  4. In Spring context, define your custom CommonsHttpMessageSender by injecting the HttpClient you just defined in the step before.
  5. In Spring context, define reference to your custom “CommonsHttpMessageSender” in a list of messageSenders that Spring WebServiceGatewaySupport accepts.

Now, we shall follow a similar pattern and try to set proxy per connection right within spring context. For simplicity, I am covering a case of simple proxy without the need of username and password i.e. just with host and port. We shall use the same HttpClient class and set a HostConfiguration on it, so in java you can do following:


import org.apache.commons.httpclient.HostConfiguration;
...
...
//Say, in your code you have handle "httpClient" - your HttpClient 

HostConfiguration myHostConfig = new HostConfiguration();
//Setting proxy server host and port onto host config
myHostConfig.setProxy("192.168.1.111",8080)
//Setting hostconfig onto http client
httpClient.setHostConfiguration(myHostConfig);
...
...

The example above shows how you could set HostConfiguration onto your HttpClient in order to achieve setting of proxy host and port onto your connection only. But this is all in java code and remember we want to achieve our goal right within spring context ! The problem in doing this in spring context is that HostConfiguration class does not provide us with any javabean compliant methods / property in order to set proxy host and port. And because of this we cannot directly use HostConfiguration class as a bean in spring context. To understand this further, please refer to HttpClient 3.1 javadocs. The version of HttpClient we are using (HttpClient 3.1), the HostConfiguration has following methods for setting and getting proxy host and port:

void setProxy(String proxyHost, int proxyPort);
void setProxyHost(ProxyHost proxyHost);
String getProxyHost();
int getProxyPort();

So if you notice above, the setProxyHost takes ProxyHost type as an argument and getProxyHost actually returns a String type. We could have used setProxyHost and getProxyHost methods in order to set our proxy settings within spring context onto our HostConfiguration but since these get-set methods do not comply by javabean contract of setting and getting the same type, spring would complain if you try to use this get-set approach in order to set your proxy onto HostConfiguration bean in spring context. What we can do is a little trick: extend the HostConfiguration class and create our own MyHostConfiguration class as shown below:

package mypackage;

import org.apache.commons.httpclient.HostConfiguration;

public class MyHostConfiguration extends HostConfiguration {

    private final String DELIMITER = ":";

    public synchronized String getProxyHostPlusPort() {

        return new StringBuffer(getProxyHost()).append(DELIMITER).append(getProxyPort()).toString();
    }

    public synchronized void setProxyHostPlusPort(String proxyHostPlusPort) {

        if (null == proxyHostPlusPort)
            throw new IllegalArgumentException("proxyHostPlusPort string cannot be null");
        if (proxyHostPlusPort.indexOf(DELIMITER) == -1)
            throw new IllegalArgumentException("proxyHostPlusPort string is expected in 'host:port' format");

        String[] hostPlusPort = proxyHostPlusPort.split(DELIMITER);

        setProxy(hostPlusPort[0], new Integer(hostPlusPort[1]).intValue());
    }

}

Now, if you notice the piece of code above setProxyHostPlusPort and getProxyHostPlusPort follow the javabean contract and set and get the same type which is “String”. We can now use this class in our spring context as a bean that we shall inject onto our HttpClient bean as shown below:

...
...

<!-- Here is a relevant section of Spring Context -->
    <bean id="reqSender" class="mypackage.RequestSender">
       <property name="messageFactory">
         <bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
       </property>
       <property name="messageSenders">
         <list>
            <ref bean="httpSender" />
         </list>
       </property>
       <property name="marshaller" ref="myMarshaller" />
       <property name="unmarshaller" ref="myMarshaller" />
       <property name="defaultUri" value="http://webservice-url/deployed/at/some/server" />
    </bean>

    <bean id="myHostConfig" class="mypackage.MyHostConfiguration">
        <property name="proxyHostPlusPort" value="192.168.1.111:8080" />
    </bean>

    <bean id="httpParams" class="org.apache.commons.httpclient.params.HttpClientParams">
      <!-- Timeout in milliseconds: in this case 2 minutes -->
      <property name="soTimeout" value="120000" />
    </bean>

    <bean id="httpClient" class="org.apache.commons.httpclient.HttpClient">
      <!-- hostConfiguration set to myHostConfig that sets the proxy host and port as shown above -->
      <property name="hostConfiguration" ref="myHostConfig" />
      <property name="params" ref="httpParams" />
    </bean>

    <bean id="httpSender" class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
      <constructor-arg>
        <ref bean="httpClient"/>
      </constructor-arg>
    </bean>    

    <bean id="myMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" >
      <property name="contextPath" value="path_to_directory_with_jaxb_xml_generated_classes"/>
    </bean>
...
...

As you see in the example spring context above, we have used myHostConfig to set our poxy host and port and later injected this config onto our HttpClient. You can comment hostConfiguration property line of http client definition if you don’t want to use proxy for this connection. Similarly you can choose not to set the timeout for this connection by commenting params property line that sets HttpParams onto this HttpClient.

~srs

9 Responses to “Spring web service call using proxy per connection”

  1. Davy Steegen Says:

    Great post! I was looking for this solution and it really helped me out for the project I am working on.

  2. How I Lost Thirty Pounds in Thirty Days Says:

    Hi, nice post. I have been pondering this issue,so thanks for sharing. I’ll certainly be coming back to your site.

  3. vinicius Says:

    Hi…

    Nice post. I’ve been searching for it, but in fact I have the same problem, however, I need to set username and password. Is it possible ?

  4. Sridhar Says:

    Hi,

    I am using spring httpclient and CommonsHttpMessageSender,

    I want to display request time out, if the response take too much of time.

    Code snippet:

    Quote:

    WebService client

    In java

    Quote:
    private int connectionTimeout;
    public int getConnectionTimeout() {
    return connectionTimeout;
    }

    public void setConnectionTimeout(int connectionTimeout) {
    this.connectionTimeout = connectionTimeout;
    }
    HttpClient client = new HttpClient();
    client.getHttpConnectionManager().getParams().setS oTimeout(new Integer(getConnectionTimeout()));

    logger.debug(“connections timeout :”+ getConnectionTimeout());
    System.out.print(“connections timeout :”+ getConnectionTimeout());

    How to configure and check whether the request has been timeout or not?

    Thanks in Advance
    Sridhar

    • onebyteatatime Says:

      If request times out you shall receive a SocketTimeout error at your client assuming you have a java client. Since you are setting timeout you also know that after xyz millisec your request is anyway going to timeout. so that is another way to guard against it.

      hope that helps,
      ~srs

  5. anton Says:

    Thank you for sharing information and knowledge

  6. Jit Says:

    Good one! I was looking for a webservice timeout solution and it took not more than 10 mins to implement with the help of your post. Thanks.


Leave a Reply