JAXB-WS: XmlElement and WebParam - java

I'm trying to make a web method parameter required. I'm using JAXB2.2, so I can use the XmlElement annotation on params. The problem is that in the generated wsdl I can still have minOccurs=0.
#WebMethod(operationName = "retrieve")
#WebResult(name = "response")
public RetrieveWrapper retrieve(#WebParam(name = "name") #XmlElement(nillable=false, required=true) String name) {
And in the generated wsdl I have this:
<xs:complexType name="retrieve">
<xs:sequence>
<xs:element name="name" type="ns1:String" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
Any advice please? What I am missing?

The "name" in your complexType "retrieve" is optional.
You probably have a Class that corresponds to "retrieve" with a field "name" on which you didn't define "required=true". But that's not what you want.
I think your wsdl is correct for what you want, check your wsdl you should have and other element "name" which corresponds to your webparam and is required (no "minoccurs").

Related

JAXB translate element name when marshalling/un-marshalling

I am generating a java class from xsd which is used in to marshal/un-marshal xml.
I have an element currently defined in my xsd as
<xs:element maxOccurs="1" minOccurs="0" name="versionLabel" type="xs:string"/>
which results in a java class containing
String versionLabel
and setters and getters, setVersionLabel()/getVersionLabel().
I want the incoming/outgoing xml element to be <version> and for that to translate to/from the java class property "versionLabel". How do I do define that behavior in the xsd?
add #XmlElement annotation to the attribute and update your xsd if it used for any validation
#XmlElement(name = "version")
String versionLabel;

SOAP : How to make generic method that provides response to all operations of WSDL dynamically

I am trying to create Webservice Simulator where we can create SOAP Webservices by just providing the .xsd or xml file. After providing xml/xsd wsdl file will be generated and for that reason only i am trying to make ServiceEndPoint class methods generic so that single method provides response for all operations.
Till now i have made sample code to test how soap web service is generated using spring web service and uses JAXP APIs.
I want to make following method generic so that it provides response to all operations :
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getStudentRequest")
#ResponsePayload
public GetStudentResponse getCountry(#RequestPayload GetStudentRequest request) {
GetStudentResponse response = new GetStudentResponse();
response.setStudent(studentUtility.getStudent(request.getStudentId()));
return response;
As of now above method is binded to specific operation getStudentRequest
Please help me to know how can I make above method generic so that it provides response for all operations.
Please find below the xsd file through which I am generating WSDL file:
<xs:element name="getStudentRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="studentId" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getStudentResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="student" type="tns:student"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="student">
<xs:sequence>
<xs:element name="studentId" type="xs:int"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:int"/>
<xs:element name="class" type="xs:string"/>
</xs:sequence>
</xs:complexType>
You can try using generics in your method like below:
public <T extends IRequest, R extends IResponse> R getCountry(#RequestPayload T request) {
To make this work, the coresponding request and response must be sub classed.
class GetStudentRequest implements IRequest{}
class GetStudentResponse implements IResponse{}
If yor are generating code using apache cxf or jaxb, this can be done by providing JAXB custom binding with inheritance plugin to create request/response classes with the interface implemented.
for eg.
<inheritance:implements>mypackage.IRequest</inheritance:implements>
You cannot, WSDL doesn't support Generic Types to be Passed across.
The reason for wsdl is basically used to inform the client about the methods and types, the client can generate proxy using the wsdl and your types will be strongly typed.
If you still want to do it, it defeats the purpose of wsdl. you can serialise and pass as xml or any string and parse/deserialise back in the client. obviously the client will not know about this type, which doesnt make any sense.
If you want to do this:
getCountry, getStudent, getX, getY actions (which have same input/output signature) should all point to single web service function you implemented, below hack can be solution:
If you use CXF (or similar lib), you can use interceptors for manipulating your requests headers and content. If you register this interceptor to a proper place like "READ" (see phases list), it'd be possible to change header: SOAPAction value from getX() to getCountry().
Also see this question: Stackoverflow: Apache CXF - Set HTTP header

A message body writer for Java type, class bookInfoListType, and MIME media type application/xml was not found

I have been browsing stackoverflow for this error lately and I'm unable to find a solution on almost all the threads that I have been to. That's why I'm posting this question here.
The problem is that I'm having said error while returning the response. Here is my XSD definition:
<xs:element name="bookInfoList">
<xs:complexType>
<xs:sequence>
<xs:element name="bookInfo" type="bookInfoType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="bookInfo" type="bookInfoType"/>
<xs:complexType name="bookInfoListType">
<xs:sequence>
<xs:element name="bookInfo" type="bookInfoType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="bookInfoType">
<xs:sequence>
<xs:element name="bookId" type="xs:string" minOccurs="1" maxOccurs="1"/>
<!-- ... more elements !-->
</xs:sequence>
</xs:complexType>
which generated the following bookListInfoType class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "bookInfoListType", propOrder = {
"bookInfo"
})
public class bookInfoListType {
protected List<bookInfoType> bookInfo;
public List<bookInfoType> getbookInfo() {
if (bookInfo == null) {
bookInfo = new ArrayList<bookInfoType>();
}
return this.bookInfo;
}
}
But when I try to send the response back like
return Response.status(HttpStatus.SC_OK).entity(bookInfoListConverter.convert(wsContext, allBooks)).build();
I get the said error.
In my ObjectFactory, I do see
public bookInfoListType createbookInfoListType() {
return new bookInfoListType();
}
Here is my REST method:
#Path ("v1/storename/")
public class BookResource
{
#GET
#Path ("books/{book_id}/info.xml")
#Produces (MediaType.APPLICATION_XML)
public Response getBookInfoXML () {
//business logic
return Response.status(HttpStatus.SC_OK).entity(bookInfoListConverter.convert(wsContext, allBooks)).build();
}
}
Not to mention, I have another JAXB generated class called bookInfoList as you can see in the XSD. Their definition is almost identical (this is one thing I'm suspecting which could be the problem, but rest of my classes which are following same pattern are working fine).
I am still trying to bang my head around to figure out whats going on but I'm running out of ideas, any help would be appreciated.
An entity returned from the resource method in the form of an arbitrary Java object can be serialized by Jersey into a container output stream as a specified representation. Of course, while JAX-RS implementations do provide default support for most common combinations of Java type and it's respective on-the-wire representation formats, JAX-RS implementations do not support the conversion described above for any arbitrary Java type and any arbitrary representation format by default.
JAX-RS Entity Providers
For every Java type and content type combination your application uses (which is not covered by the default providers that come with the JAX-RS implementation) you must have a provider that knows how to handle the combination, so you must have appropriate MessageBodyWriter and MessageBodyReader classes (see the link above for how to write them if you should need to - you might want to refer to the exact documentation for the JAX-RS implementation/version you are using).
With that said, I find your error a little bit strange since Jersey already contains default support for entity providers that can marshall JAXB beans. You might want to compare your service against a tutorial (e.g. Using JAX-RS With JAXB) to make sure the error you posted is not a red herring

Change WSDL xsd:complexType name with Apache CXF

I use Apache CXF to publish a webservice, generating the WSDL "on-the-fly". This works great, but I would like to change the naming convention of the generated types. As the service clients (C#) generate code based on the WSDL, the default xsd:complexType naming results in type names starting with lower-case letters.
Here is an excerpt from the generated WSDL:
<xs:complexType name="protocolItem">
<xs:sequence>
<xs:element minOccurs="0" name="data" type="tns:protocolItemData"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="elements" nillable="true" type="tns:protocolElement"/>
<xs:element minOccurs="0" name="meta" type="tns:protocolItemMeta"/>
</xs:sequence>
</xs:complexType>
This is the Java code that results in the above WSDL fragment:
#RooJavaBean
public class ProtocolItem {
private ProtocolItemData data;
private ProtocolItemMeta meta;
private List<ProtocolElement> elements;
}
How can I change the generated WSDL to use something like <xs:complexType name="ProtocolItem">?
Hopefully I am not missing a obvious annotation... Thanks!
EDIT: thanks for the first answer! So there is a way to do this "per class" - can I configure the CXF naming conventions? Would be nice if I did not need to annotate all classes.
Try this:
#XmlType(name="ProtocolItem")
public class ProtocolItem {
...
}
Hope this helps.
Using the Aegis data binding instead of the JAXB data binding with Apache CXF helped: This changed the naming convention. Unfortunately, I realised this after having added all #XmlType annotations, so I removed them again...
I stumbled over Aegis when searching for a solution for collections/arrays - they are not wrapped with JAXB, but with Aegis, which is required for proper client code generation (typed collections instead of arrays).

What's the purpose of minOccurs, nillable and restriction?

Documentation for required says:
If required() is
true, then Javabean property is mapped to an XML schema element
declaration with minOccurs="1". maxOccurs is "1" for a single valued
property and "unbounded" for a multivalued property.
If required() is false, then the Javabean property is mapped to XML
Schema element declaration with minOccurs="0". maxOccurs is "1" for a
single valued property and "unbounded" for a multivalued property.
Documentation for nillable says:
If nillable() is true, then the JavaBean property is mapped to a XML
Schema nillable element declaration.
Code for xs:complexType:
public class WSData {
//...
#XmlElement(required = true, nillable = false)
public void setMonth(XmlMonthType month) {
this.month = month;
}
public void setUserLogin(String userLogin) {
this.userLogin = userLogin;
}
}
Code for xs:simpleType:
#XmlType
#XmlEnum(Integer.class)
public enum XmlMonthType {
#XmlEnumValue("1")
JANUARY,
#XmlEnumValue("2")
FEBRUARY,
#XmlEnumValue("3")
MARCH,
/* ... months 4 ~9 ... */
#XmlEnumValue("10")
OCTOBER,
#XmlEnumValue("11")
NOVEMBER,
#XmlEnumValue("12")
DECEMBER;
}
Generated XML Schema:
<xs:complexType name="wsData">
<xs:sequence>
<xs:element name="month" type="xs:string"/>
<xs:element minOccurs="0" name="userLogin" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="xmlMonthType">
<xs:restriction base="xs:int">
<xs:enumeration value="1"/>
<xs:enumeration value="2"/>
<xs:enumeration value="3"/>
<!-- ... months 4 ~9 ... -->
<xs:enumeration value="10"/>
<xs:enumeration value="11"/>
<xs:enumeration value="12"/>
</xs:restriction>
</xs:simpleType>
The facts:
The default value for minOccurs is 1. So, month is required (must exist);
The month has a restriction. So, month can only have a value defined by one of the 12 defined enumerations;
The default value for nillable is false. So, month can't have empty values;
The XML Schema is generated correctly.
The problems:
It is accepting month's abcense (mustn't exist);
It is accepting any values for month, like 13 (except when isn't parseable to Integer);
It is accepting empty values;
I wasn't expecting these problems, am I missing something?
If that behavior is correct, what is the purpose of required, nillable and xs:restriction?
Nillable allows empty values. For example, if you have an Integer or a Date, if it's nillable, the XML tag could be empty. If it's not nillable but not required, the XML element would either have to exist with a valid content, or not exist at all; an empty tag wouldn't be valid.
Make minOccurs 1, to make month required;
The default value for minOccurs is one, so the month element is required. Note how minOccurs="0" had to be added to userLogin to make it optional.
<xs:complexType name="wsData">
<xs:sequence>
<xs:element name="month" type="xs:string"/>
<xs:element minOccurs="0" name="userLogin" type="xs:string"/>
</xs:sequence>
</xs:complexType>
Validate month with its generated restriction (without a XmlAdapter).
You can set an instance of Schema on the Unmarshaller to have the input validated:
http://blog.bdoughan.com/2010/12/jaxb-and-marshalunmarshal-schema.html
Demo
The following code can be used to generate the XML schema:
package forum9111936;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(WSData.class);
SchemaOutputResolver sor = new SchemaOutputResolver() {
#Override
public Result createOutput(String namespaceUri,
String suggestedFileName) throws IOException {
StreamResult result = new StreamResult(System.out);
result.setSystemId(suggestedFileName);
return result;
}
};
jc.generateSchema(sor);
System.out.println();
}
}
UPDATE
The JAXB RI normally throws a ValidationEvent of severity 1 for conversion issues. The default ValidationEventHandler ignores all issues of severity less than 2. This normally results in the value being set to null. You can override the ValidationEventHandler as follows:
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event);
return event.getSeverity() < ValidationEvent.ERROR;
}
});
However the JAXB RI does not appear to throw events related to converting enum values (possible bug). If you happen to be using EclipseLink JAXB (MOXy) as your JAXB provider then you will get an exception like:
Exception in thread "main" Local Exception Stack:
Exception [EclipseLink-116] (Eclipse Persistence Services - 2.4.0.qualifier): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: No conversion value provided for the value [13] in field [month/text()].
Mapping: org.eclipse.persistence.oxm.mappings.XMLDirectMapping[month-->month/text()]
Descriptor: XMLDescriptor(forum9111936.WSData --> [])
at org.eclipse.persistence.exceptions.DescriptorException.noFieldValueConversionToAttributeValueProvided(DescriptorException.java:1052)
at org.eclipse.persistence.mappings.converters.ObjectTypeConverter.convertDataValueToObjectValue(ObjectTypeConverter.java:140)
at org.eclipse.persistence.oxm.mappings.XMLDirectMapping.getAttributeValue(XMLDirectMapping.java:287)
at org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue.endElement(XMLDirectMappingNodeValue.java:190)
at org.eclipse.persistence.oxm.record.UnmarshalRecord.endElement(UnmarshalRecord.java:910)
at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parseEvent(XMLStreamReaderReader.java:133)
at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parse(XMLStreamReaderReader.java:83)
at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parse(XMLStreamReaderReader.java:72)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:838)
at org.eclipse.persistence.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:626)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:472)
at forum9111936.Demo2.main(Demo2.java:30)
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
The purposes of required and minOccurs aren't mislead, the problem is that the schema validation is not enabled. Just enable SchemaValidation to the WebService and define the order of the XmlType's mapping as follows:
Web Service:
#javax.jws.WebService
#org.jboss.ws.annotation.SchemaValidation(enabled = true)
public class WebServiceClass {
#javax.jws.WebMethod
public WSResponseData webServiceMethod() {
//...
}
}
XmlType:
#javax.xml.bind.annotation.XmlType(propOrder = {"field1", "field2", "field3"})
public class WSData {
//...
private String field1;
private Long field2;
private XmlMonthType field3;
//...
}
Indeed it seems that SOAP envelopes aren't validated upon WSDL, neither on server or client.
And I think it's best this way. Validation consumes resources and time. For most WebServices, all we need with WSDL is to provide to client the definitions to be able to talk to WebService, and not set restrictions like if a property can or not be null.
When server and client are on same PC, Axis2 add 3-5 milisecs of overhead compared to internal calls. Unecessary validation would only increase overhead. I's better to make operations throw an exception and manually validate what's really needed.
I looked into the XSD structure that you have posted - and find that you have not used complex type that you have defined for the month , Insteqad of handling it on java code you can mention that all in XSD , and also the minOccur on month - element in your complex type is missing . Please use any standered XSD to java generator , it will do all the java file generation task for you
The convention are as follow -
MinOccur = 0 --> the element can be abscent in input , and can be present
MinOccur = 1 --> the element must be there in input
(but if you use it, then your java generated member will be of list type - list of ENUM for Int )
If you don't write MinOccur in attributes - then it makes the element mandatory , ( then you java generated member will be of simply ENUM for int )
MaxOccur = 1 --> minimum one element can be there in input
(but if you use it, then your java generated member will be of list type - list of ENUM for Int )
MaxOccur = unbound --> only one element can be there in input
(if you use it, then your java generated member will be of list type - list of ENUM for Int )
<xs:complexType name="wsData">
<xs:sequence>
<xs:element name="month" type="xmlMonthType" minOccurs="1" nillable="false" />
<xs:element name="userLogin" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="xmlMonthType">
<xs:restriction base="xs:int">
<xs:enumeration value="1"/>
<xs:enumeration value="2"/>
<xs:enumeration value="3"/>
<!-- ... months 4 ~9 ... -->
<xs:enumeration value="10"/>
<xs:enumeration value="11"/>
<xs:enumeration value="12"/>
</xs:restriction>
</xs:simpleType>

Categories

Resources