I'm sucessfully exposing a contract-first JAX-WS web service on a Tomcat servlet container with Jax-WS Spring support. I'm having troulbes with xs:idref types though. The original XSD file contains a complex type
<xs:complexType name="DocumentScopeOptionalTypeReferenceIdentifier">
<xs:simpleContent>
<xs:extension base="DocumentScopeReferenceIdentifierContent">
...
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="DocumentScopeReferenceIdentifierContent">
<xs:restriction base="xs:IDREF">
<xs:minLength value="1"/>
<xs:maxLength value="64"/>
</xs:restriction>
</xs:simpleType>
which xjc correctly compiles to
public class DocumentScopeOptionalTypeReferenceIdentifier {
#XmlValue
#XmlIDREF
protected Object value;
...
}
When I deploy the webservice, however, the #XmlIDREF annotation gets ignored and I end up with an xs:anyType in the namespace declaration of the resulting WSDL
<xs:complexType name="DocumentScopeOptionalRoleReferenceIdentifier">
<xs:simpleContent>
<xs:extension base="xs:anyType">
...
</xs:extension>
</xs:simpleContent>
</xs:complexType>
Clients of the webservice in question report, that they cannot generate Client stubs (using C#) with this anyType present. How would I change that back to xs:idref? Thanks.
Ok, I guess my approach was a little bit too naiv. After doing a bit of research I came to the conclusion that customizing the generated WSDL is not possible. I now switched to a contract first approach and specify a hand-crafted WSDL file manually.
I've read that any WSDL file located under META-INF/wsdl should be picked up automatically by JAX-WS, if it contains a corresponding service and port name. This didn't work for me, so I had to specify the WSDL file explicitely using the wsdlLocation attribute on the #WebService annotation
#WebService(
targetNamespace = "...",
serviceName = "...",
portName = "...",
wsdlLocation = "/META-INF/wsdl/mywebservice.wsdl"
)
public class MyWebService { ... }
Deploying the webservice on Tomcat was straight forward following the instructions on the Jax-WS community page
Related
We have a JAX-WS/JAXB binding to an external web-service that is working fine on Java 7 (1.7.0u80) with the included reference implementations. During migration to Java 8 (1.8.0u66) the web service calls generally work OK, however it cannot unmarshall SOAP faults and their detail elements to Java Exceptions with custom detail any longer, giving instead a prefix not bound to a namespace error.
The failure is
Caused by: javax.xml.ws.WebServiceException: java.lang.IllegalArgumentException: prefix xsd is not bound to a namespace
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:138)
at com.sun.xml.internal.ws.client.sei.StubHandler.readResponse(StubHandler.java:238)
at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:189)
at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:276)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:104)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:77)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:147)
at com.sun.proxy.$Proxy61.proprietaryServiceCall(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.remoting.jaxws.JaxWsPortClientInterceptor.doInvoke(JaxWsPortClientInterceptor.java:580)
at org.springframework.remoting.jaxws.JaxWsPortClientInterceptor.doInvoke(JaxWsPortClientInterceptor.java:554)
... 56 more
Caused by: java.lang.IllegalArgumentException: prefix xsd is not bound to a namespace
at com.sun.xml.internal.bind.DatatypeConverterImpl._parseQName(DatatypeConverterImpl.java:355)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyXsiLoader.selectLoader(LeafPropertyXsiLoader.java:75)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyXsiLoader.startElement(LeafPropertyXsiLoader.java:58)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:559)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:538)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:153)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:229)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:266)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:235)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(DOMScanner.java:112)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:354)
at com.sun.xml.internal.bind.v2.runtime.BridgeImpl.unmarshal(BridgeImpl.java:124)
at com.sun.xml.internal.bind.api.Bridge.unmarshal(Bridge.java:309)
at com.sun.xml.internal.ws.db.glassfish.BridgeWrapper.unmarshal(BridgeWrapper.java:217)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.getJAXBObject(SOAPFaultBuilder.java:304)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:135)
The response from the external service looks like the below (I have anonymized type names, but left everything else)
<env:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header/>
<env:Body>
<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>ERROR MESSAGE</faultstring>
<detail>
<n1:ProprietaryException xmlns:n1="java:com.company.service" xsi:type="n1:ProprietaryException">
<errorCode xsi:type="xsd:int">400</errorCode>
<errorReason xsi:type="xsd:string">Specific error</errorReason>
</n1:ProprietaryException>
</detail>
</env:Fault>
</env:Body>
</env:Envelope>
The problem is with the xsd:int and xsd:string in the faultCode and faultReason. It seems like the prefix/namespace declarations are not being inherited from the top level envelope when binding. The problem looks similar to this question except unlike that question this is about SOAP Fault handling, and in my case, the code is deep inside JAX-WS and JAXB so I have no idea how we could fix it or workaround it.
Unless the old code was relying on some behaviour that never should have worked, I can't help but conclude something has been broken between JAX-WS and JAXB in their Java 8 implementations.
Update (Jan 4 2016): I've also tried this with a CXF 3.1.4 client instead of the Metro RI. Same problem. It seems to be the same issue as mentioned here
Update (Jan 6 2016): I've narrowed this problem to a change introduced to the JAXB RI 2.2.6. Thus the problem can be replicated on Java 7 with a forced upgrade to JAXB RI 2.2.6. It seems it might relate to changes made in JAXB-890.
I have tested working around this in at least two different ways:
Use Java 8 with JAXB force downgraded back to 2.2.5 (JAX-WS version doesn't seem to matter). Doesn't seem a good long-term solution.
I found that -Dcom.sun.xml.bind.improvedXsiTypeHandling=false (or the equivalent .internal property if using the bundled JDK JAXB RI) seems to workaround the issue. But I have no idea what this setting really does; or what the implications would be for the rest of the JAXB usage in my system.
Any ideas for how to proceed here?
One workaround which seems to work (but should not be required and has other consequences for JAXB usage in other parts of my application which make it undesirable) is to replace the JAXB provider with EclipseLink MOXy (tested 1.6.2).
Given this works, it does seem that this is a problem in the JAXB RI (Metro) version included with Java 8 (up to at least 1.8.0u66).
Faced a similar issue recently. Switching to MOXy or using some obscure JVM parameters wasn't an option, so I looked on ways to implement some sort of a "pre-unmarshall hack" mentioned above.
Turns out, if you make a SOAPHandler like so
public class NamespaceBindingShim implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
context.getMessage();
return true;
}
#Override
public void close(MessageContext context) {
}
#Override
public Set<QName> getHeaders() {
return null;
}
}
and then add it to the handler chain of your client like so
ServicePort port = service.getServicePort();
BindingProvider bindingProvider = (BindingProvider) port;
Binding binding = bindingProvider.getBinding();
binding.setHandlerChain(Collections.singletonList(new NamespaceBindingShim()));
then everything miraculously works and proprietary SOAP faults get converted to proprietary exceptions.
I don't yet know why this works and whether it breaks something else, since it's pretty obvious I don't do any XML manipulation inside the handler (the getter only invokes lazy DOM initialization for the envelope AFAICT).
EDIT: after more testing I have discovered this to pass in some tests, but to fail in others. Back to the drawing board...
I am deploying JAVA EE application in WildFly 8.1.0 Final. After deployments its replaces address in SOAP address location to Remote Interface rather than to Stateless EJB WebService.
E.g,
#Remote
public interface SbbAccess {
...
}
#WebService(name="SbbAccess", serviceName="pfmjainslee11_diagram", targetNamespace="http://jee.v1.sbb.profile" )
#Addressing
#HandlerChain(file = "jaxws-handlers.xml")
#TransactionManagement(TransactionManagementType.BEAN)
public #Stateless class SbbAccessBean implements SbbAccess {
...
}
WSDL available from wildlfy is
<wsdl:port binding="tns:pfmjainslee11_diagramSoapBinding" name="SbbAccessPort">
<soap:address location="http://linus:8080/sbb---v1.EJB/pfmjainslee11_diagram/SbbAccess"/>
</wsdl:port>
this should have been like
<wsdl:port binding="tns:pfmjainslee11_diagramSoapBinding" name="SbbAccessPort">
<soap:address location="http://linus:8080/sbb---v1.EJB/pfmjainslee11_diagram/SbbAccessBean"/>
</wsdl:port>
Any suggestion what is causing this behavior?
Regards,
That's the expected behaviour as a result of at least two of the following :
Your SLSB implements an #Remote interface
It's recommended by the specification that the interface name is mandatory in the JNDI lookup naming, when the SLSB implements a business interface.
The SLSB is being accessed remotely
The naming lookup options are:
[/application name]/module name/enterprise bean name[/interface name]
In theory, you're not required to implement #Remote, so if it's causing you any discomfort, you could remove the interface implementation and you'll be fine
We're working with JAX-WS in conjuntion with JAXB (2.1).
First, we've created the Java classes linked to the XSDs types.
Then, using CXF (2.1.3), Spring (2.0.8) and JAX-WS we've published a Webservice from a JAXB type (in fact a #XmlType), like this:
#WebMethod(operationName = "addExample")
public #WebResult void addExample(
#WebParam(name = "EXAMPLE") EXAMPLETYPE exemple);
We know that the webservice is published using the package name of the interface (inverse) or using the #WebService annotation the "targetNamespace" we could define. In fact, we did it with something like:
#WebService(targetNamespace = "http://blablabla")
At this time, we need to maintain the whole targetNameSpace of the WebService but we need to change the namespace of the #java.jws.WebParam (i.e EXAMPLE param in our case).
It is possible? The annotation never works for us, doing things like that:
#WebMethod(operationName = "addExample")
public #WebResult void addExample(
#WebParam(name = "EXAMPLE", targetNamespace = "http://thats.all.folks") EXAMPLETYPE exemple);
We are really lost...
Thank you,
I think you need to upgrade to a newer (and supported) version of CXF. I believe this was a bug that was fixed a very long time ago.
I have wsdl and I create classes with wsimport tool. Everything is going ok for SOAP messages but for http:binding GET/POST I get error. Part of the WSDL follows:
<wsdl:binding name="DictServiceHttpPost" type="tns:DictServiceHttpPost">
<http:binding verb="POST" />
...
...
...
<wsdl:port name="DictServiceHttpGet" binding="tns:DictServiceHttpGet">
<http:address location="http://services.aonaware.com/DictService/DictService.asmx" />
</wsdl:port>
<wsdl:port name="DictServiceHttpPost" binding="tns:DictServiceHttpPost">
<http:address location="http://services.aonaware.com/DictService/DictService.asmx" />
</wsdl:port>
wsimport generates classes, for SOAP port its working but for GET/POST but when I use them, I receive error.
Caused by: com.sun.xml.internal.ws.model.RuntimeModelerException: runtime modeler error: SEI invoker.DictServiceHttpPost has method match annotated as BARE but it has more than one parameter bound to body. This is invalid. Please annotate the method with annotation: #SOAPBinding(parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
at com.sun.xml.internal.ws.model.RuntimeModeler.validateDocBare(RuntimeModeler.java:1247)
at com.sun.xml.internal.ws.model.RuntimeModeler.processDocBareMethod(RuntimeModeler.java:1236)
at com.sun.xml.internal.ws.model.RuntimeModeler.processMethod(RuntimeModeler.java:609)
at com.sun.xml.internal.ws.model.RuntimeModeler.processClass(RuntimeModeler.java:401)
at com.sun.xml.internal.ws.model.RuntimeModeler.buildRuntimeModel(RuntimeModeler.java:240)
at com.sun.xml.internal.ws.client.WSServiceDelegate.createSEIPortInfo(WSServiceDelegate.java:687)
at com.sun.xml.internal.ws.client.WSServiceDelegate.addSEI(WSServiceDelegate.java:675)
at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:330)
at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:313)
at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:295)
at javax.xml.ws.Service.getPort(Service.java:92)
at invoker.DictService.getDictServiceHttpPost(DictService.java:124)
... 29 more
com.sun.xml.internal.ws.model.RuntimeModelerException: runtime modeler error: SEI invoker.DictServiceHttpPost has method match annotated as BARE but it has more than one parameter bound to body. This is invalid. Please annotate the method with annotation: #SOAPBinding(parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
This happened to me, the problem was that I had a public method in the service implementation, that had more than one parameter but this method was not a service operation. Services marked with: SOAPBinding.ParameterStyle.BARE should only have public methods with only one parameter. My solution was mark this method as private since I was using it from one public method of the service (operation).
Make sure you don't have any public method in your service implementation that has more than one parameter.
I need to incorporate a pretty complex 3rd party web service into my Grails app. My plan was to use WSDL2Java to generate the stub classes from the wsdl, and this was the method recommended in the 3rd party's documentation (complete with examples). First i tried to use the Axis2 codegen plugin for Eclipse but eventually came up against an InvocationTargetexception. I debugged the plugin and found it was because the wsdl is defined with RPC encoding.
Some people have recommended using Axis 1.4 instead, so I've now installed that too but have come up against an IO Exception - Type {http://xml.apache.org/xml-soap}DataHandler is referenced but not defined.
Can anyone suggest a method for creating the java classes from this wsdl without having to hack the wsdl apart?
I ended up using the Axis2 wdsl2java and copying the required annotated code into the service and used the CXF plugin. I also put in my service the following code
static expose=['cxfjax']
The reason why I had to do this was because there was a "complicated" (for grails) structure my methods look like
#WebMethod(operationName = "authenticate", action = "http://betterhidethis/authenticate")
#WebResult(name = "authenticateResult", targetNamespace = "http:/betterhidethis/")
public ArrayOfString authenticate(
#WebParam(name = "strUserName", targetNamespace = "http://betterhidethis/")
String strUserName,
#WebParam(name = "strPassword", targetNamespace = "http://betterhidethis/")
String strPassword) {
Of cause the Geneerator also created the ArrayOfString class which I use later.
Hope this helps.