Issue:
I have created ONVIF WSDL Based Java Client and Server using JAX-WS.
The problem is that the automatically generated Client is using SOAP 1.2 with HTTP "Content-Type: application:/xml+soap" while the Server is exposing SOAP 1.1 with HTTP "Content-Type: text/xml"
The result is that client fails with media type exception and server throws "Unsupported Content-Type" exception:
Jan. 03, 2023 3:42:55 PM com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit handle
SEVERE: Unsupported Content-Type: application/soap+xml; charset=utf-8;action="http://www.onvif.org/ver10/device/wsdl/GetDeviceInformation" Supported ones are: [text/xml]
com.sun.xml.ws.server.UnsupportedMediaException: Unsupported Content-Type: application/soap+xml; charset=utf-8;action="http://www.onvif.org/ver10/device/wsdl/GetDeviceInformation" Supported ones are: [text/xml]
I have confirmed this by doing direct SOAP invocations via curl.
This is successful when "Content-Type: text/xml":
$ curl --verbose http://127.0.0.1:9080/onvif/device_service -H "Content-Type: text/xml" --data '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><getDeviceInformation xmlns="http://www.onvif.org/ver10"></getDeviceInformation></s:Body></s:Envelope>' | xmllint --format -
* Trying 127.0.0.1:9080...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 9080 (#0)
> POST /onvif/device_service HTTP/1.1
> Host: 127.0.0.1:9080
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Type: text/xml
> Content-Length: 273
>
} [273 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 02 Jan 2023 06:14:40 GMT
< Content-Type: text/xml;charset=utf-8
< Transfer-Encoding: chunked
< Server: Jetty(10.0.12)
<
{ [114 bytes data]
100 1037 0 764 100 273 31538 11269 --:--:-- --:--:-- --:--:-- 45086
* Connection #0 to host 127.0.0.1 left intact
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns3:getDeviceInformationResponse xmlns:ns3="http://www.onvif.org/ver10" xmlns:ns4="http://www.onvif.org/ver10/schema" xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:ns6="http://docs.oasis-open.org/wsn/b-2" xmlns:ns7="http://www.w3.org/2005/08/addressing" xmlns:ns8="http://docs.oasis-open.org/wsrf/bf-2" xmlns:ns9="http://docs.oasis-open.org/wsn/t-1" xmlns:ns10="http://www.w3.org/2004/08/xop/include" xmlns:ns11="http://www.onvif.org/ver10/device/wsdl" xmlns:ns12="http://www.w3.org/2003/05/soap-envelope">
<arg0>john</arg0>
<arg1>beta</arg1>
<arg2>0.0.1</arg2>
<arg3>1</arg3>
<arg4>hw1</arg4>
</ns3:getDeviceInformationResponse>
</S:Body>
</S:Envelope>
And fails when "Content-Type: application/xml+soap":
$ curl --verbose http://127.0.0.1:9080/onvif/device_service -H "Content-Type: application/xml+soap" --data '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><getDeviceInformation xmlns="http://www.onvif.org/ver10"></getDeviceInformation></s:Body></s:Envelope>' | xmllint --format -
* Trying 127.0.0.1:9080...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 9080 (#0)
> POST /onvif/device_service HTTP/1.1
> Host: 127.0.0.1:9080
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Type: application/xml+soap
> Content-Length: 273
>
} [273 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 415 Unsupported Media Type
< Date: Mon, 02 Jan 2023 06:19:50 GMT
< Transfer-Encoding: chunked
< Server: Jetty(10.0.12)
<
{ [5 bytes data]
100 273 0 0 100 273 0 5812 --:--:-- --:--:-- --:--:-- 5934
* Connection #0 to host 127.0.0.1 left intact
-:1: parser error : Document is empty
^
Environment is:
- Ubuntu 22.04
- OpenJDK 11
- Embedded Jetty Version 10 for javax.* generated code
- Embedded Jetty Version 11 for jakarta.* generated code
- Maven JAX-WS Plugin: com.sun.xml.ws: jaxws-maven-plugin: 2.3.5 & 3.0.2
JAX-WS Version:
I have tested with both Java EE & Jakarta EE and they both show the same SOAP 1.1/1.2 Asymmetry for client and server
Here is server code which just boiler plate using the generated code where
JaxDeviceImpl device = new JaxDeviceImpl();
Is derived from WSDL JAX-WS generated "Device" Server Interface class (WSDL below):
/**
@what Embedded Jetty JAVAX JAX-WS Device Simulator
@note: See Eclipse Jetty: Programming Guide
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.eclipse.jetty.server.Server;
import javax.xml.ws.Endpoint;
import onvif_relay.service.JaxDeviceImpl;
import fence.util.ConfigurationData;
public class EmbeddedJettyJaxDevice {
public static void main(String[] args) throws Exception {
ConfigurationData confData = new ConfigurationData(args);
String srvPort = confData.getItem("onvif-device", "port");
String port = confData.getItem("onvif-device", "device-port");
String request = confData.getItem("onvif-device", "request");
try {
System.out.println("Starting the Jetty server on port: " + srvPort + " onvif mgt on: " + port);
Server server = new Server(Integer.parseInt(srvPort));
JaxDeviceImpl device = new JaxDeviceImpl();
// System.setProperty("com.sun.net.httpserver.HttpServerProvider", "org.eclipse.jetty.http.spi.JettyHttpServerProvider");
// System.setProperty("jakarta.xml.ws.spi.Provider", "org.eclipse.jetty.http.spi.JettyHttpServerProvider");
// System.setProperty("javax.xml.ws.spi.Provider", "org.eclipse.jetty.http.spi.JettyHttpServerProvider");
String uri = "http://127.0.0.1:" + port + request;
// Endpoint ep = Endpoint.create(uri, device);
Endpoint ep = Endpoint.publish(uri, device);
server.start();
// consoleLoop(DevManager);
server.join();
System.out.println("Stopped the simple server...");
} catch (Exception ex) {
ex.printStackTrace();
}
}
Service WSDL:
<?xml version="1.0" encoding="utf-8" ?>
<wsdl:definitions name="onvif_device"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl"
xmlns:trt="http://www.onvif.org/ver10/media/wsdl"
targetNamespace="http://www.onvif.org/ver10">
<wsdl:import namespace="http://www.onvif.org/ver10/device/wsdl"
location="../ver10/device/wsdl/devicemgmt.wsdl"/>
<wsdl:import namespace="http://www.onvif.org/ver10/media/wsdl"
location="../ver10/media/wsdl/media.wsdl"/>
<wsdl:service name="DeviceService">
<wsdl:documentation>ONVIF - Device</wsdl:documentation>
<!-- wsdl:port name="Device" binding="tns:DeviceBinding" -->
<wsdl:port name="DevicePort" binding="tds:DeviceBinding">
<soap:address location="http://127.0.0.1/onvif/device_service"/>
</wsdl:port>
</wsdl:service>
<!-- wsdl:service name="MediaService">
<wsdl:documentation>ONVIF - Media</wsdl:documentation>
<!- wsdl:port name="Device" binding="tns:MediaBinding" ->
<wsdl:port name="MediaPort" binding="trt:MediaBinding">
<soap:address location="http://127.0.0.1/onvif/device_service"/>
</wsdl:port>
</wsdl:service -->
</wsdl:definitions>
Full code set for both test client and server is posted via github
Expectation:
Expectation is that automatically generated JAX-WS code would work transparently between Client / Server.
NOTE: The current Jakarta XML Web Services specification (Version 3.0) appears to be ambiguous on SOAP 1.1/1.2 support:
- "Goals - SOAP 1.2 - Whilst SOAP 1.1 is still widely deployed, it’s expected that services will migrate to SOAP 1.2[3][4] now that it is a W3C Recommendation. Jakarta XML Web Services will add support for SOAP 1.2 whilst requiring continued support for SOAP 1.1."
- "Non Goals - SOAP Encoding Support - Use of the SOAP encoding is essentially deprecated in the web services community, e.g., the WS-I Basic Profile[8] excludes SOAP encoding. Instead, literal usage is preferred, either in the RPC or document style. SOAP 1.1 encoding is supported in JAX-RPC 1.0 and Jakarta XML RPC but its support in Jakarta XML Web Services runs counter to the goal of delegation of data binding to Jakarta XML Binding. Therefore Jakarta XML Web Services will make support for SOAP 1.1 encoding optional and defer description of it to Jakarta XML RPC. Support for the SOAP 1.2 Encoding[4] is optional in SOAP 1.2 and Jakarta XML Web Services will not add support for SOAP 1.2 encoding.
What I tried:
Tried using:
- full Jakarta EE toolset
- transitional Java EE toolset
Testing with generated Client / Server and via direct curl invocation.
Always get the same result of client (SOAP 1.2) and Server (SOAP 1.1).
NOTE #1: as the underlying WSDL is from ONVIF I do not have option of changing WSDL,
Questions:
- Can I change client behavior to always request via SOAP 1.1 ?
- Is the problem with Jetty Embedded Server and SOAP 1.2 support ?
- Is the problem with Jakarta Reference Implementation ?
sDoing further investigation on this I found that the ONVIF SOAP Binding URL:http://schemas.xmlsoap.org/soap/http get redirected to dead end.
Here is snippet from devicemgmt.wsdl
I am not sure if this is root cause of problem, but found that I could work around it by changing the server EndPoint create/publish code to it explicitly used SOAP 1.2 / HTTP binding:
I have committed update to example and confirmed that ONVIF Device Simulator is now serving SOAP 1.2/HTTP as per ONVIF Specification.