How to set a connection timeout when using JAXRPC-RI web services client?
Asked Answered
A

8

8

I'm working with a bit of a legacy component in which we interact with a SOAP web service (a technology which I absolutely, positively abhor) using some client code built using the JAXRPC-RI (reference implementation) library.

I'm interested in being able to set a timeout with the stubs so that in case the web services server does not reply within X seconds, the application isn't setting there forever waiting for a response.

I'm using to working with clients/stubs generated by Apache Axis, in which you can simply use org.apache.axis.client.Stub.setTimeout() to set a timeout.

For the life of me I can't figure out how to set a timeout when using Stubs created with JAXRPC-RI:

  • The port class I am instantiating extends com.sun.xml.rpc.client.StubBase and implements javax.xml.rpc.Stub and com.sun.xml.rpc.spi.runtime.StubBase.
  • The JavaDocs for none of these classes mention any sort of timeout or method to do this.
  • Trying code like stub._setProperty("axis.connection.timeout", 1000); results in an exception at runtime: javax.xml.rpc.JAXRPCException: Stub does not recognize property: axis.connection.timeout

Does anyone have any ideas on how to set/enforce a timeout when using a JAXRPC-RI client? Is it even possible?

Alf answered 30/4, 2009 at 18:50 Comment(0)
Z
4

You may have luck setting properties such as sun.net.client.defaultConnectTimeout or sun.net.client.defaultReadTimeout, though that would then introduce a system-wide timeout.

In code the properties values are set using Strings:

System.setProperty("sun.net.client.defaultConnectTimeout", "1000");
System.setProperty("sun.net.client.defaultReadTimeout", "1000");

For a quick test, it might be easier to set the environment variable JAVA_OPTS or use the command line:

java -Dsun.net.client.defaultConnectTimeout="1000" ...
Zusman answered 7/6, 2009 at 15:45 Comment(5)
Any idea if this affects not just outgoing connections but incoming ones as well?Alf
I guess the first can only apply to outgoing requests, unless any handshakes would be taken into account as well. And given the names "net.client" I assume both properties affect outgoing only, which for the second property is supported by its definition "[..] specifies the timeout (in milliseconds) when reading from input stream when a connection is established to a resource." Indeed, this makes one wonder if there are similar properties for sockets that have accepted an incoming connection, but I cannot quickly find those.Zusman
And, "These properties specify the default connect and read timeout (resp.) for the protocol handler used by java.net.URLConnection." So, outgoing only for sure. java.sun.com/javase/6/docs/technotes/guides/net/properties.htmlZusman
Thank you for this. I was struggling with a SocketTimeoutException on Google App Engine and this got me past it.Stitch
I just discovered that these properties are supposed to be set on command line or before doing anything Net-Connection related because "Yes, it [default timeouts] is cached, or more precisely it is looked up only at startup time. This is by design." See bugs.sun.com/bugdatabase/view_bug.do?bug_id=6245589.Cayes
R
2

I am not sure why this particular JAXRPC implementation goes out of its way to make it difficult to set the timeout. Perhaps, there's a good reason for it. Other implementations such as Axis and, I believe JAX-WS, let you simply call the setTimeout() method on the Stub. After some pains, I was able to come up with this solution. Hopefully, it will be helpful. You will need to do the following in order to set the timeout on the underlying URLConnection:

  1. Create a new ClientTransport class. It should extend the com.sun.xml.rpc.client.http.HttpClientTransport class. Override the createHttpConnection(String, SOAPMessageContext) method. Call super.createHttpConnection() which will return HttpURLConnection object. On the HttpURLConnection object you can call setReadTimeout() which will force client-side timeout in X number of milliseconds. Once that's done, return the modified HttpURLConnection object.
  2. Extend the client-side Stub class with your own. Override the _getTransport() method to return a new instance of the ClientTransport class that you created in step (1).
  3. Extend the client-side _Impl class with your own. Override the get...Port() method so that it uses the Stub you created in step (2) instead of the generated one.
  4. Modify the client code you wrote to call the Web Service so that it uses the Stub you created in step (2) and the _Impl that you created in step (3).

Note: Make sure that whatever it is that you're calling to generate the Stubs via wscompile (Ant script?) does not overwrite the 3 Java classes you just created/modified. It probably makes sense to move them to a different package so that they don't end up being overwritten.

Rechabite answered 4/11, 2010 at 17:8 Comment(0)
H
1

Yevgeniy Treyvus answer is very good, but results in a new HTTPUrlConnection being created for every SOAP call. I found out while testing his approach, that one can set a ClientTransportFactory. I now use a CustomClientTransportFactory and set it on the Stub ( casting it to StubBase ). Then one doesn't need step 2 and 3.

In step 4 one then sets his new ClientTransportFactory like so: ((StubBase) myPort)._setTransportFactory(new CustomClientTransportFactory());

Higgler answered 25/11, 2010 at 12:3 Comment(0)
S
1

I know that you are using Axis but I struggled to find the same answer for Weblogic and since your question title and tags are generic, here is my solution.

In my client class that implements the generated MyObject interface, I have adapted the getServicePort() as follows (note that I also have security here).

protected MyObject getServicePort() throws java.rmi.RemoteException {
    if (port == null) {
        synchronized(this) {
            if (port == null) {
                try {
                    final MyObject_Impl service = new MyObject_Impl(wsdlURL);

                    // using a local variable until the object is completelly initialized
                    final MyObject localPort = service.getMyObjectPort();

                    // if username and password are provided: building a client which will include the 
                    // username and token
                    if (username != null && pwd != null) {
                        Stub localStub = ((Stub) localPort);

                        // We have UsernameToken Authentication too
                        localStub._setProperty(
                                WSSecurityContext.CREDENTIAL_PROVIDER_LIST, 
                                Collections.singletonList(
                                        new ClientUNTCredentialProvider(username.getBytes(), pwd.getBytes())));

                        if (timeout != null) {
                               log.debug("Setting timeout to " + timeout + " milliseconds");
                            localStub._setProperty("weblogic.wsee.transport.read.timeout", timeout);
                            localStub._setProperty("weblogic.wsee.transport.connection.timeout", timeout);
                        }
                    }

                    port = localPort;

                } catch (ServiceException e) {
                    throw new RemoteException("Could not initialize client to MyObject service", e);
                }
            }
        }
    }
    return port;
}

The Oracle documentation is here: http://docs.oracle.com/cd/E12840_01/wls/docs103/webserv_rpc/client.html#wp241849 but it incorrectly states that the timeout is in seconds. It actually is in milliseconds !

Slade answered 31/7, 2012 at 9:20 Comment(0)
C
0

I'm just going to try to reap the bounty, so don't shoot me if I am completely on the wrong track :) If your application is "setting there forever waiting for a response" than you could do the request in a separate thread and after spawning your requestThread you could just say requestThread.join(max_time_to_wait); the next call would check if the requestThread is still alive and if it this then try to kill it.

You application will move on after a time-out and it's not the most inelegant solution...

Cilicia answered 2/6, 2009 at 15:10 Comment(1)
This is an option, but I'd really like to do it within the confines of the web services client code / API itself, if possible. Thank you.Alf
R
0

Ray,

Thanks for your input. Sounds like your approach is superior since it will avoid a couple of extraneous steps. I will have to take a look. However, unless I am missing something I do not believe an additional HttpConnection is being created as the YourClientTransport.createHttpConnection(String s, SOAPMessageContext ctx) should override the HttpClientTransport one:

public class YourClientTransport extends HttpClientTransport {
   @Override
   protected HttpUrlConnection createHttpConnection(String s, SOAPMessageContext ctx) throws IOException {
      HttpURLConnection httpURLConnection = super.createHttpConnection(s, ctx);
      httpURLConnection.setReadTimeout(1000);
      httpURLConnection.setConnectTimeout(1000);
      return httpURLConnection;
   }
}
Rechabite answered 7/12, 2010 at 16:49 Comment(0)
C
0

Maybe a little bit late for this question but I came to this problem today. Based on a solution proposed by Yevgeniy Treyvus (thank for it!) I came up with the following:

((com.sun.xml.rpc.client.StubBase)myRemoteStub)._setTransportFactory(new ClientTransportFactory() {
    @Override
    public ClientTransport create() {
        return new HttpClientTransport() {
            @Override
            protected HttpURLConnection createHttpConnection(String endpoint, SOAPMessageContext context) throws IOException {
                HttpURLConnection conn = super.createHttpConnection(endpoint, context);
                conn.setConnectTimeout(2000);
                conn.setReadTimeout(2000);
                return conn;
            }
        };
    }
});

It is basically the same as Yevgeniy proposed but with a little less overriding (with the help of a connection factory).

Carvelbuilt answered 22/9, 2015 at 10:22 Comment(0)
M
-1

You should use:

import javax.xml.rpc.Stub;

...


int timeout = <number of milliseconds>;

((Stub) port)._setProperty(
                "axis.connection.timeout",
                timeout);
Markland answered 26/8, 2013 at 13:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.