javax.xml.ws.WebServiceException: 方法X被暴露为WebMethod,但没有相应的wsdl操作

huangapple go评论78阅读模式
英文:

javax.xml.ws.WebServiceException: Method X is exposed as WebMethod, but there is no corresponding wsdl operation

问题

我正在使用JAX-WS RI与第三方网络服务(Adyen)集成。我已经下载了他们的wsdl副本,并且已经在构建过程中使用了jaxws:wsdl2java来生成网络服务实现源代码。在运行时,当我尝试通过调用自动生成的Payment服务类的getPort()方法来设置端口时,我会得到以下异常,它声称存在一个已公开的方法,但在wsdl portType元素中却找不到相应的方法:

javax.xml.ws.WebServiceException: 方法adjustAuthorisation被公开为WebMethod,但在wsdl:portType{http://payment.services.adyen.com}PaymentPortType中没有相应的名称为{http://payment.services.adyen.com}adjustAuthorisation的wsdl操作

但是,它确实存在于portType元素中。以下是wsdl的相关片段:

<wsdl:portType name="PaymentPortType">
  <wsdl:operation name="adjustAuthorisation">
    <wsdl:input name="adjustAuthorisationRequest" message="tns:adjustAuthorisationRequest" />
    <wsdl:output name="adjustAuthorisationResponse" message="tns:adjustAuthorisationResponse" />
    <wsdl:fault name="ServiceException" message="tns:ServiceException" />
  </wsdl:operation>
  ...
</wsdl:portType>

完整的wsdl可以在此处查看:https://pal-live.adyen.com/pal/servlet/Payment/v30?wsdl

wsdl包含在目标jar中,类路径为/wsdl/Payment.wsdl。我在运行时使用配置类中的以下代码加载它:

URL wsdl = getClass().getResource(wsdlLocation);
onlineService = new Payment(wsdl, new QName(serviceUrl, serviceName));

其中 serviceUrl = "http://payment.services.adyen.com"serviceName = "Payment" 与wsdl匹配。

最后,以下是我尝试打开端口并最终遇到异常的代码片段:

ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try
{
  port = config.getOnlineService().getPaymentHttpPort(); // 异常在此处抛出
}
finally
{
  Thread.currentThread().setContextClassLoader(oldClassLoader);
}

是否有任何想法,为什么它似乎在错误地解析wsdl?另一个可能重要的信息是,我最近更新了wsdl,之前我的应用程序使用Adyen版本12的API与相应的wsdl,现在我正在升级到版本30。之前相同的代码可以正常运行。

英文:

I'm integrating with a third party web service (Adyen) using JAX-WS RI. I have downloaded a copy of their wsdl and have used jaxws:wsdl2java in my build to generate the web service implementation source code. At runtime, when I attempt to setup a port by calling the getPort() method of my auto-generated Payment service class, I get the following exception claiming that there is a method exposed, but it is not present in the wsdl portType element:

javax.xml.ws.WebServiceException: Method adjustAuthorisation is exposed as WebMethod, but there is no corresponding wsdl operation with name {http://payment.services.adyen.com}adjustAuthorisation in the wsdl:portType{http://payment.services.adyen.com}PaymentPortType

But, it is present in the portType element. Here's the relevant snipped of the wsdl:

&lt;wsdl:portType name=&quot;PaymentPortType&quot;&gt;
  &lt;wsdl:operation name=&quot;adjustAuthorisation&quot;&gt;
    &lt;wsdl:input name=&quot;adjustAuthorisationRequest&quot; message=&quot;tns:adjustAuthorisationRequest&quot; /&gt;
    &lt;wsdl:output name=&quot;adjustAuthorisationResponse&quot; message=&quot;tns:adjustAuthorisationResponse&quot; /&gt;
      &lt;wsdl:fault name=&quot;ServiceException&quot; message=&quot;tns:ServiceException&quot; /&gt;
  &lt;/wsdl:operation&gt;
  ...
&lt;/wsdl:portType&gt;

The full wsdl can be seen here: https://pal-live.adyen.com/pal/servlet/Payment/v30?wsdl

The wsdl is included in the target jar with classpath /wsdl/Payment.wsdl. I am loading it at runtime using this code in a config class:

URL wsdl = getClass().getResource(wsdlLocation);
onlineService = new Payment(wsdl, new QName(serviceUrl, serviceName));

Where serviceUrl = &quot;http://payment.services.adyen.com&quot; and serviceName = &quot;Payment&quot; which matches the wsdl.

Finally, here's the code snippet where I attempt to open the port and ultimately get the exception:

ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try
{
  port = config.getOnlineService().getPaymentHttpPort(); // exception thrown here
}
finally
{
  Thread.currentThread().setContextClassLoader(oldClassLoader);
}

Any ideas why it seems to be misreading the wsdl? Another potentially important piece of info is that I recently updated the wsdl, previously my app used version 12 of Adyen's API with the corresponding wsdl, now I am upgrading to version 30. The application worked fine with the same code previously.

答案1

得分: 5

你在使用用于生成类的wsdl文件和在运行时加载的wsdl文件之间存在不匹配。

  • 这些类是使用较新版本生成的,因为错误中提到了WebMethod adjustAuthorisation。<br>
  • 在运行时加载的wsdl是旧版本,不包含adjustAuthorization方法。

请注意v30v12之间的差异:
javax.xml.ws.WebServiceException: 方法X被暴露为WebMethod,但没有相应的wsdl操作

英文:

You have a mismatch between the wsdl file that you are using to generate classes and the wsdl file that you are loading at runtime.

  • the classes were generated using the newer version, since the error mentions WebMethod adjustAuthorisation.<br>
  • the wsdl loaded at runtime is an older version and it does not contain the adjustAuthorization method.

Notice the difference between v30 and v12:
javax.xml.ws.WebServiceException: 方法X被暴露为WebMethod,但没有相应的wsdl操作

答案2

得分: 1

以下是翻译好的部分:

`javax.xml.ws.WebServiceException` 的堆栈跟踪显示了在使用从 wsdl 生成的代码构建新的 `Payment` 服务时内部发生的情况:

at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(RuntimeWSDLParser.java:265) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:246) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:209) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:178) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.client.WSServiceDelegate.parseWSDL(WSServiceDelegate.java:363) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:321) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:230) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:211) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:207) ~[jaxws-rt.jar:2.2.10]
at com.sun.xml.ws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:114) ~[jaxws-rt.jar:2.2.10]
at javax.xml.ws.Service.<init>(Service.java:77) ~[?:1.8.0_252]
at com.amazon.adyen.payment.Payment.<init>(Payment.java:58) ~[?:?]

`RuntimeWSDLParser.parse` 调用 `java.net.URL.openStream`,该方法调用 `sun.net.www.protocol.jar.JarURLConnection.getInputStream`,因为在这种情况下,wsdl 部分位于 jar 包中。

在调用 `URLConnection` 上的 `getInputStream` 时,默认行为是检查缓存。可以通过打开到资源的连接,将 `useCaches` 设置为 `false` 来禁用给定连接的缓存。然而,在这种情况下,打开流到资源的代码是自动生成的,并且修改生成的类不会是一个适用于未来更改的好解决方案。

`URLConnection` 还有一个 `setDefaultUseCaches` 方法,它会设置当前连接和所有未来连接的行为。请参阅 `URLConnection` 源代码中的文档:http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/net/URLConnection.java#l245

我成功地通过设置此默认标志来强制从 jar 加载 wsdl,并在加载资源后将其恢复为原始设置。

以下是可正常工作的代码,它会在从缓存中获取资源时发生错误时从 jar 加载资源:

```java
URL wsdl = getClass().getResource(wsdlLocation);

URLConnection connection = wsdl.openConnection();
try {
    onlineService = new Payment(wsdl, new QName(serviceUrl, serviceName));
} catch (WebServiceException wsException) {
    // 如果 jar 中的 wsdl 资源发生了变化,由于从缓存中加载旧版本,将抛出异常。尝试在不使用 URLConnection 缓存的情况下加载。
    LOG.warn("初始化服务失败。尝试在不使用 URLConnection 缓存的情况下重试加载。捕获的异常:", wsException);
    connection.setDefaultUseCaches(false);
    onlineService = new Payment(wsdl, new QName(serviceUrl, serviceName));
} finally {
    connection.setDefaultUseCaches(true);
}

如果您能直接加载输入流,您可以在不影响其他连接的默认缓存行为的情况下执行以下操作:

URL wsdl = getClass().getResource(wsdlLocation);
URLConnection connection = wsdl.openConnection();
connection.setUseCaches(false);
InputStream inputStream = connection.getInputStream();
英文:

The stack trace for the javax.xml.ws.WebServiceException shows what's happening under the hood when constructing a new Payment service using the code generated from the wsdl:

	at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(RuntimeWSDLParser.java:265) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:246) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:209) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:178) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.client.WSServiceDelegate.parseWSDL(WSServiceDelegate.java:363) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.client.WSServiceDelegate.&lt;init&gt;(WSServiceDelegate.java:321) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.client.WSServiceDelegate.&lt;init&gt;(WSServiceDelegate.java:230) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.client.WSServiceDelegate.&lt;init&gt;(WSServiceDelegate.java:211) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.client.WSServiceDelegate.&lt;init&gt;(WSServiceDelegate.java:207) ~[jaxws-rt.jar:2.2.10]
	at com.sun.xml.ws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:114) ~[jaxws-rt.jar:2.2.10]
	at javax.xml.ws.Service.&lt;init&gt;(Service.java:77) ~[?:1.8.0_252]
	at com.amazon.adyen.payment.Payment.&lt;init&gt;(Payment.java:58) ~[?:?]

RuntimeWSDLParser.parse calls java.net.URL.openStream, which calls sun.net.www.protocol.jar.JarURLConnection.getInputStream since in this case, the wsdl in part of the jar.

When calling getInputStream on a URLConnection, the default behavior is to check the cache. This can be disabled for a given connection by opening a connection to the resource, setting useCaches to false, and then opening the input stream. However, the code which opens the stream to the resource is auto-generated in this case, and modifying the generated classes wouldn't be a good solution for future changes.

URLConnection also has a setDefaultUseCaches method, which will set the behavior for the current connection and all future connections. See the documentation in the URLConnection source code: http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/net/URLConnection.java#l245

I was able to force the wsdl to be loaded from the jar by setting this default flag, and then reverting it after loading the resource.

The working code is below which loads the resource from the jar if there is an error fetching it from cache:

URL wsdl = getClass().getResource(wsdlLocation);

URLConnection connection = wsdl.openConnection();
try {
    onlineService = new Payment(wsdl, new QName(serviceUrl, serviceName));
} catch (WebServiceException wsException) {
    // if wsdl resources in the jar have changed, an exception will be thrown due to
    // loading the old version from cache. Attempt to load without using the cache.
    LOG.warn(&quot;Failure initializing service. Trying again without using URLConnection caching. Caught exception: &quot;, wsException);
    connection.setDefaultUseCaches(false);
    onlineService = new Payment(wsdl, new QName(serviceUrl, serviceName));
} finally {
    connection.setDefaultUseCaches(true);
}

If you are able to load the input stream directly, you could do the following without touching the default caching behavior that could affect other connections:

URL wsdl = getClass().getResource(wsdlLocation);
URLConnection connection = wsdl.openConnection();
connection.setUseCaches(false);
InputStream inputStream = connection.getInputStream();

huangapple
  • 本文由 发表于 2020年9月11日 23:53:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/63850416.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定