如何在使用JAXRPC-RI Web服务客户端时设置连接超时?

8

我正在处理一个遗留组件,其中我们使用使用JAXRPC-RI(reference implementation)库构建的客户端代码与SOAP web服务(一种我非常厌恶的技术)进行交互。

我希望能够设置存根(stubs)的超时时间,以便在web服务服务器在X秒内没有回复时,应用程序不会永远等待响应。

我通常使用Apache Axis生成的客户端/存根,其中您可以简单地使用org.apache.axis.client.Stub.setTimeout()来设置超时时间。

但是,我无论如何都无法弄清楚在使用JAXRPC-RI创建的存根中如何设置超时时间:

  • 我实例化的端口类扩展了com.sun.xml.rpc.client.StubBase并实现了javax.xml.rpc.Stubcom.sun.xml.rpc.spi.runtime.StubBase
  • 这些类的JavaDocs中没有提到任何类型的超时或方法。
  • 尝试像stub._setProperty("axis.connection.timeout", 1000);这样的代码会在运行时导致异常:javax.xml.rpc.JAXRPCException: Stub does not recognize property: axis.connection.timeout

是否有人有关于如何在使用JAXRPC-RI客户端时设置/强制执行超时的想法?这是否可能?

8个回答

4
你可以尝试设置诸如sun.net.client.defaultConnectTimeoutsun.net.client.defaultReadTimeout等属性,但这可能会引入系统范围的超时。在代码中,可以使用字符串来设置属性值:
System.setProperty("sun.net.client.defaultConnectTimeout", "1000");
System.setProperty("sun.net.client.defaultReadTimeout", "1000");

为了进行快速测试,你可以通过设置环境变量JAVA_OPTS或使用命令行来简化操作:

java -Dsun.net.client.defaultConnectTimeout="1000" ...

这会影响到进入的连接吗?还是只有外向连接受到影响? - matt b
我猜第一个只适用于发出的请求,除非任何握手也会被考虑在内。鉴于“net.client”的名称,我假设两个属性仅影响输出,对于第二个属性,其定义支持这一点:“[...]指定建立与资源的连接时从输入流读取的超时时间(毫秒)。"确实,这使人想知道是否有类似的属性适用于已接受传入连接的套接字,但我无法快速找到它们。 - Arjan
这些属性指定了java.net.URLConnection使用的协议处理程序的默认连接和读取超时时间(分别)。因此,仅限于出站。 http://java.sun.com/javase/6/docs/technotes/guides/net/properties.html - Arjan
谢谢您。我在Google App Engine上遇到了SocketTimeoutException的问题,但是这个解决方案帮助我解决了它。 - Cuga
1
我刚刚发现这些属性应该在命令行上设置或在进行任何与Net-Connection相关的操作之前设置,因为“是的,它[默认超时]被缓存,更准确地说它只在启动时查找。这是按设计来实现的。”请参见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6245589。 - erik

2
我不确定为什么这个特定的JAXRPC实现要费劲地设置超时时间。也许有很好的原因。其他实现,如Axis和我相信JAX-WS,让您只需在Stub上调用setTimeout()方法即可。经过一些痛苦,我能够想出这个解决方案。希望它有所帮助。您需要按照以下步骤设置底层URLConnection的超时时间:
  1. 创建一个新的ClientTransport类。它应该扩展com.sun.xml.rpc.client.http.HttpClientTransport类。覆盖createHttpConnection(String, SOAPMessageContext)方法。调用super.createHttpConnection()将返回HttpURLConnection对象。在HttpURLConnection对象上,您可以调用setReadTimeout(),以强制在X毫秒内进行客户端超时。完成后,返回修改后的HttpURLConnection对象。
  2. 使用自己的类扩展客户端Stub类。覆盖_getTransport()方法,以返回您在步骤(1)中创建的ClientTransport类的新实例。
  3. 使用自己的类扩展客户端_Impl类。覆盖get...Port()方法,使其使用您在步骤(2)中创建的Stub而不是生成的Stub。
  4. 修改您编写的客户端代码,以便它使用您在步骤(2)中创建的Stub和您在步骤(3)中创建的_Impl。
注意:确保您调用wscompile(Ant脚本?)生成Stub的任何内容都不会覆盖您刚刚创建/修改的3个Java类。最好将它们移动到一个不同的包中,以避免被覆盖。

1

我知道你正在使用Axis,但我很难找到Weblogic相同的答案,因为你的问题标题和标签都是通用的,所以这是我的解决方案。

在实现生成的MyObject接口的客户端类中,我已经调整了getServicePort()方法(请注意,我这里也有安全性)。

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;
}

Oracle文档在这里:http://docs.oracle.com/cd/E12840_01/wls/docs103/webserv_rpc/client.html#wp241849,但它错误地说明超时时间是以秒为单位的。实际上,它是以毫秒为单位的!

1

Yevgeniy Treyvus的回答非常好,但会导致每个SOAP调用都创建一个新的HTTPUrlConnection。 在测试他的方法时,我发现可以设置ClientTransportFactory。 现在我使用CustomClientTransportFactory并将其设置在Stub上(将其转换为StubBase)。 然后就不需要步骤2和3了。

在第4步中,您可以这样设置新的ClientTransportFactory: ((StubBase) myPort)._setTransportFactory(new CustomClientTransportFactory());


0

Ray,

感谢您的建议。听起来您的方法更优秀,因为它将避免一些不必要的步骤。我需要看一下。然而,除非我漏掉了什么,我认为并没有创建额外的HttpConnection,因为YourClientTransport.createHttpConnection(String s, SOAPMessageContext ctx)应该覆盖了HttpClientTransport:

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;
   }
}

0

我只是想试图收获回报,所以如果我完全走了错误的道路,请不要射击我 :) 如果您的应用程序"永远等待响应",那么您可以在一个单独的线程中进行请求,在生成您的requestThread之后,您可以只说requestThread.join(max_time_to_wait);下一个调用将检查请求线程是否仍然活着,如果是,则尝试杀死它。

超时后,您的应用程序将继续运行,这并不是最优雅的解决方案...


这是一个选项,但如果可能的话,我真的很想在Web服务客户端代码/API本身的限制内完成它。谢谢。 - matt b

0
也许对于这个问题来说有点晚了,但我今天遇到了这个问题。 基于Yevgeniy Treyvus提出的解决方案(感谢他!),我想到了以下方法:
((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;
            }
        };
    }
});

这基本上与Yevgeniy提出的相同,但少了一些覆盖(借助连接工厂的帮助)。


-1

你应该使用:

import javax.xml.rpc.Stub;

...


int timeout = <number of milliseconds>;

((Stub) port)._setProperty(
                "axis.connection.timeout",
                timeout);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接