I am creating a webservice client from a WSDL.
A typical SOAP request to the service looks something like this
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:someGateway">
<soapenv:Header/>
<soapenv:Body>
<urn:send>
<urn:message>
<urn:messageID>1001</urn:messageID>
<urn:messageBody>
<DataContainer>
SOME MORE ELEMENTS
</DataContainer>
</urn:messageBody>
</urn:message>
</urn:send>
</soapenv:Body>
</soapenv:Envelope>
I used JAX-WS to generate the service artefacts and populated my objects as below:
Message message = objectFactory.createMessage();
//Set message ID
String messageID = "123456"
message.setMessageID(messageID );
//Set message Body
MessageBody messageBody = objectFactory.createMessageMessageBody()
The messageBody object has only 1 method messageBody.setAny(value). But i need to place a DataContainer Element inside it.
I have tried passing:
org.w3c.dom.DocumentObject (I get "javax.xml.ws.soap.SOAPFaultException: Failed to process the request.") probbaly due to the xml decleration.
DataContainer object as generated by JAXB from an XSD (I get "[javax.xml.bind.JAXBException: class DataContainer nor any of its super class is known to this context]")
JAXBElement (I get "[javax.xml.bind.JAXBException: class DataContainer is not known to this context]")
What am I doing wrong? Or what do i Need to do to get the DataContainer in the message body
As you mentioned you have messageBody.setAny(value) which means that the XSI:type for MessageBody has been set to anytype. This means you can set any object there, the cavet being JAXB should be able to marshal it within the context defined by the JAX-WS wsdl2java tool. From the error message 'cannot find DataContainer in Conext' it seems like your DataContainer classes are not in the same context.
Here is a workaround for that, you can probably marshal your DataContainer Object into a JAXBElement<String> (or probably simply a String, but I am not sure if that will work) object and then set that into the anyType. This way you won't get Class not know in context as String is a basic JAXB type.
I don't know how you have defined your package structure when you were trying to use point 2 or 3, So I am taking a wild stab here. From the error message, it seems that your separately generated DataContainer Class is not in the same package as the Message and its sub classes. Try to move your DataContainer and its associated classes to the same package as the Message Class and merge the two ObjectFactory Classes together. This should allow JAXB to find DataContainer in the same 'context' as Message.
The error is probably occurring when you make the actual request and JAXB is marshaling the objects to create the request (i.e. JAX-WS is internally calling the JAXB Marshelling service). In this case when you generated the client the JAXBContext was set to the package where Message class is.
Here is a simple tutorial which deals with JAXBContext Marshaling and unmarshaling.
http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/1.6/tutorial/doc/JAXBUsing3.html
Also as per this, you can probably set the anyType to org.w3c.dom.Element and not org.w3c.dom.Document
The secret for using the xs:any of non-included XSD type is #XmlSeeAlso. When you create your JAXB classes from xjc tool you will get an interface defines the #WebService methods. That interface will also be used by client and service implementation. If you won't to modify the auto-generated java files you'd better extends this interface in your package and add #XmlSeeAlso({ExternalClassYouWantToReferTo.class}) to this new interface, ex: IWebServiceInterface
#WebService(name = "IExternalXmlBatchReceive", targetNamespace = "http://External.ServiceContract.BatchReceive")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
#XmlSeeAlso({
ObjectFactory.class, ExternalClassYouWantToReferTo.class
})
public IWebServiceInterface extends InterfaceYourAutoCreationCode {
#WebMethod(name=...)
......
}
All your Service class and #WebService are implemented from this interface.
When your client call getPort method, you should pass your new implemented interface as the second parameter like:
IWebServiceInterface wi = service.getPort(YOUR_QNAME, IWebServiceInterface.class);
The getPort method will look into the interface which you passed in for the #XmlSeeAlso and initialize its internal JAXBContext.
Related
PROBLEM
If I generate a jaxb object from an XML root element named 'Message' it produces the following class:
#XmlRootElement(name = "Message")
public class Message{
...
}
I have a method with the following definition:
public void doSomething(Object rootElement) {
...
}
This method is part of a framework used by 30+ developers. The method only accepts a class with the annotation #XmlRootElement. Developers sometimes miss this crucial javadoc and pass a non #XmlRootElement object to the method giving errors during runtime. As the generated class has no superclass, no interfaces, etc I have to accept an Object as argument.
QUESTION
Is it not possible to enforce this check compile time ?
ALREADY TRIED
I've looked into (via jaxb binding file) letting generated classes implement an interface and then accept this interface as argument, however it cannot (in an automated manner) be applied to only the root element class.
We have several hundred different XML Schema files from which we generated in an automated manner.
I have a Message class like this:
class Message {
#JsonProperty("content")
Object content;
}
where the content attribute can be a User, a Post, or a String
and I have to send this object to the server and cast the content to the right class.
I'm using Jackson annotations to serialize the JSON, but when I try to cast the content, an error appears, because the attribute content arrives in the server like a LinkedHashMap object.
The error is:
ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/MegaRadarSocial].[Resteasy]] (http-localhost-127.0.0.1-8080-1) Servlet.service() for servlet Resteasy threw exception: org.jboss.resteasy.spi.UnhandledException: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to br.com.megaradar.megaradarsocial.model.User
I would like a help to casting...
Thanks
As you control both ends (server and client), you could try Genson library http://code.google.com/p/genson/. One of its features allows you to serialize to json and type information, this enables you to deserialize back to the right type.
Genson genson = new Genson.Builder().setWithClassMetadata(true).create();
json = genson.serialize(yourMessage);
// then deserialize it back
Message message = genson.deserialize(json, Message .class);
The serialized json will look like : {"content": {"#class":"package.path.Message", ...the object values...}}
You can even define aliases for the serialized classes
new Genson.Builder().addAlias("message", Message.class)
Important: Note that you need to use the same configuration of genson on both sides. So enable type information with setWithClassMetadata and if you use aliases, you must define the same on the client side.
What you need is #JsonTypeInfo annotation, like so:
class Message {
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY property="type")
#JsonProperty("content")
Object content;
}
(you can see http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html for examples)
which would add property "type" with class name as value (there are many alternative ways as well) when serializing, and using that when deserializing.
Thank you for all the answers. But I found another way to convert my Object to any type I want.
I'm using the method convertValue from the ObjectMapper object. Then, I can simulate the casting.
Thanks again
I need to create a webservice, that accepts any xml in the request soap:Body element and replies with any xml in the respond soap:Body. The appropriate WS operation style is (in my opinion) "document-literal-bare". I expect that the resulting WSDL message would look something like this:
<s0:message name="process">
<s0:part type="xs:any"/>
</s0:message>
How am I to do it?
Details:
In Weblogic 10.3, I have written:
#WebService(.....)
#SOAPBinding(style=SOAPBinding.Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle=ParameterStyle.BARE)
public class BackEndSimulatorWS {
#WebMethod
public XmlObject process(XmlObject request) {
.....
}
}
However the Eclipse protests with
(The "process" method) must be doc/literal/wrapped when binding wildcards to any.
I understand that the WS must be able to determine which method to execute, so I tried to annotate the method with
#Webmethod(action="simulation")
but this didn't help. When I replace the org.apache.xmlbeans.XmlObject parameter and response type with org.w3c.dom.Document, the error disappears, but the resulting WSDL messages are not general, they are of the {java:org.w3c.dom}:Document type.
Any help will be appreciated.
I'm trying out apache-camel, and I've set up a basic route that calls an http service via http4 component, transforms the result via unmarshal().json(JsonLibrary.Jackson), and then prints out part of the response in a bean component.
The problem I'm having is that it blows up at runtime when it gets to the json unmarhsaller:
No type converter available to convert from type: java.util.HashMap to the required type: com.xxx.MyType
The response is of this format:
{"data":[{"x":"y"},{"x":"z"}]}
And my object model is like:
#lombok.Data
class Response {
private List<Elem> data;
}
#lombok.Data
class Elem {
private String x;
}
So it would appear that the unmarshaller thinks the response is a hash map, whereas I want it to unmarshal into an object structure. Is there a way to get it to do what I want?
Found the answer, posting in case anyone else runs into this. The route builder should be setup like:
from("direct:start").to("http4://...").unmarshal().json(JsonLibrary.Jackson,com.xxx.Response)
.to("bean:com.xxx.MyResponseEchoer")
I.e. pass the class type to the json method.
We are creating a webservice (CXF-based) driven by a java class (Java2WS) with the following method:
#WebMethod
#RequestWrapper(className = "com.myproject.wrapper.MyRequestWrapper")
#ResponseWrapper(className = "com.myproject.wrapper.MyResponseWrapper")
public MyResponse verifyCode(#WebParam(name = "code") String code) {
...
return new MyResponse("Hello",StatusEnum.okay);
}
I use the wrappers to define the elements of the request resp. response in more detail: the correct element names (which start with an uppercase character), required and optional elements, ...). But I am not sure if this is the right way to do it (there is no in-depth documentation about wrappers, isn't it?)
The class MyResponse:
public class MyResponseWrapper {
private String result;
private ModeEnum status;
// getters and setters
}
The class MyReponseWrapper
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "myResponse")
public class MyResponseWrapper {
#XmlElement(name="Result")
private String result;
#XmlElement(name = "Status")
private StatusEnum status;
public MyResponseWrapper() {
result="fu"; // just for testing
}
// getters and setters
}
Currently I don't understand the Wrappers. When I return an instance of MyReponse, how does the data from MyResponse be injected into MyResponseWrapper respectivly to the SOAP body of the response?
By testing this webservice I can see that an instance of MyResponseWrapper is instantiated and the SOAP body contains the correct elements but with default data (for example: result="fu" instead of "Hello"). I expected that CXF injects matching data from MyResponse to MyResponseWrapper. Is that wrong?
If this is the wrong way to do it:
Wat is the right way to specify the resulting SOAP xml when using Java2WS?
By the way: The above source snippets are just examples taken from our more complex (more fields) classes.
This is the right way to do it.
The request and response wrappers just allow for overriding the xml namespace and element/attribute names for the request/response elements; respectively - which in turn map to the methods used to manage those values.
Ref: http://cxf.apache.org/docs/developing-a-service.html#DevelopingaService-The#RequestWrapperannotation
The #RequestWrapper annotation is defined by the
javax.xml.ws.RequestWrapper interface. It is placed on the methods in
the SEI. As the name implies, #RequestWrapper specifies the Java class
that implements the wrapper bean for the method parameters that are
included in the request message sent in a remote invocation. It is
also used to specify the element names, and namespaces, used by the
runtime when marshalling and unmarshalling the request messages.
The following table describes the properties of the #RequestWrapper
annotation.
localName
Specifies the local name of the wrapper element in the XML
representation of the request message. The default value is the name
of the method or the value of the #WebMethod annotation's
operationName property.
targetNamespace
Specifies the namespace under which the XML wrapper element is
defined. The default value is the target namespace of the SEI.
className
Specifies the full name of the Java class that implements the wrapper
element.