In this article I will talk about creating a self-documenting web service (jax-ws) that uses elements of XSD Restrictions.
Task Description
In the book The Programmer Pragmatist there is a chapter on documentation. It says that there should be one source of information, and on the basis of it, derived forms of this information should be generated. for example, all information about the structure of tables is stored in one format (it can be a file of any suitable or own format), and on the basis of it, the tables themselves, documentation on these tables, DAO objects for working with tables are generated. The advantages of this approach are that there will be no desynchronization between the derived forms of information, because the only place where the information will be edited is the original source.
The downsides are that all interested people should be able to edit this file and there should be converters into the necessary formats.
In web services, these concepts are called contract first and contract last. Contract first - when wsdl is first created, and Java code is generated from it. Contract last - on the contrary, first Java code, and wsdl on it.
You can read about the pros and cons of the links:
docs.spring.io/spring-ws/sites/1.5/reference/html/why-contract-first.html stackoverflow.com/questions/763827/which-is-the-better- approach-to-web-services-contract-first-or-contract-lastIn this article I will discuss the creation of the SOAP Web Service with self-documentation, in order to get a similar solution - all the information about web services will be contained in it. It turns out a sort of “contract last” - first, the code is written, and then it produces documentation in the form of wsdl, which contains as much information as possible about this web service - comments to all methods and types with which the service works and most of the data validation logic. What can this solution be useful for? In that case when all development is concentrated on writing of the code and wsdl is generated on the basis of it, and not vice versa. Or when it is difficult to keep the documentation originally in wsdl. But if web methods are written by analytics, then it is better to store this analytics in wsdl. Those. At first wsdl, and on it methods, classes and readable documentation are generated. Otherwise, there will be problems verifying the compliance of an already written service with those. assignment.
It is also better to check the validity of the input data on the xsd side, so that you can verify the request without connecting to the web service and that this logic would be visible (in xsd) and not protected somewhere in the service.
Solution options
Currently, you can get wsdl from any jax-ws application. But standard jax-ws / jaxb annotations do not support documentation for web services — to do this, you need to take advantage of the capabilities provided by third-party frameworks. Specifically, apache CXF (jax-ws) provides the WSDLDocumentation annotation, which is applied to web methods and which specifies the description of the web method, which will later be included in wsdl. To document the xml entities that will be used in web methods, use the jaxb-facets library (
dsg.tuwien.ac.at/staff/hummer/tools/jaxb-facets.html ,
github.com/whummer/jaxb-facets ). It allows, as well as CXF for web methods, to document classes and xml bean fields. Besides, it adds support for XSD Restrictions - additional restrictions on the values ​​of xml fields. (A description of XSD Restrictions can be found here
www.w3schools.com/schema/schema_facets.asp ) This will be useful when you need the logic to check the correctness of values ​​more complicated than mandatory / not mandatory - because jaxb does not fully support the validation capabilities of xsd. Specifically, it will be possible to specify restrictions on the minimum, maximum values, use regular expressions, use different ways of processing strings for empty values, set limits on the length of the value.
')
Implementation
As an example, create a simple web service with several methods.
In the article I will describe specific examples with pieces of code. The whole service can be downloaded from the link
dl.dropboxusercontent.com/u/7519092/jax-ws-example.zip This is an IDEA maven project. In IDEA, you can open either by importing an IDEA project or by importing a maven project. In pom.xml, the wsdl file is configured to transform it into html using xslt, but unfortunately, this wsdl does not contain the documentation tag from jaxb-facets, although in the generated wsdl (accessible by link <application path> / web_service / WebService? wsdl) this tag is there. Also in the project, the application of annotations is different - there annotations are applied to get methods, and in these examples to class fields. This is done to shorten the article. To apply annotations to class fields, you must either not get method or specify @XmlAccessorType (XmlAccessType.FIELD).
Examples
As an example, consider several specific tasks.
Restriction on the maximum and minimum value of the date and time.
For date:
@XmlSchemaType(name = "date") @Facets(minInclusive = "1900-01-01", maxInclusive = "9999-12-31Z") Date date;
Generated xsd scheme:
<xs:element minOccurs="0" name="date"> <xs:simpleType> <xs:restriction base="xs:date"> <xs:maxInclusive value="9999-12-31Z"/> <xs:minInclusive value="1900-01-01"/> </xs:restriction> </xs:simpleType> </xs:element>
@XmlSchemaType(name = "dateTime") @Facets(minInclusive = "1900-01-01T00:00:00", maxInclusive = "9999-12-31T23:59:59") Date dateTime;
<xs:element minOccurs="0" name="dateTime"> <xs:simpleType> <xs:restriction base="xs:dateTime"> <xs:maxInclusive value="9999-12-31T23:59:59"/> <xs:minInclusive value="1900-01-01T00:00:00"/> </xs:restriction> </xs:simpleType> </xs:element>
(Abstract Facets from jaxb-facets library)
We had to use because The DBMS did not support the entire range of dates available in Java. Those. You can send dates to the web service, for example, 1000-01-01 and in Java it will parse correctly, but there will be an error when writing to the DBMS, since It does not support such a date range. And to limit the values ​​taken, this restriction is introduced.
For the date, the maximum date is based on the time zone. It is necessary that the weekend 9999-12-31 with the time zone be supported (for example, 9999-12-31 + 04: 00). If you do not do this, then when you serialize this value (when returned to the client) there will be an xsd validation error.
Restriction on a specific field length
@Facets(length = 16) String id;
<xs:element minOccurs="0" name="id"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:length value="16"/> </xs:restriction> </xs:simpleType> </xs:element>
It is necessary, for example, when a field is a unique identifier of a certain length or some other identifier of a specific length - the number of a passport or card.
Although in the case of id, you can select a separate type of Id:
public class Id { @Facets(length = 16) @XmlAttribute String attr; }
And use it like this:
Id id;
So we will clearly state that this is a variable - a unique identifier, while all restrictions will work.
Zeroing fields
Some optional fields need to be reset. For strings, this is done very simply - an empty tag is sent (eg <name />) and with unmarshalling (converting the xml of the query to java objects) we get just an empty string (String.isEmpty () == true). And it can be quite interpreted as a string to be set to null.
But if you need to reset the numbers or dates, then when you pass an empty tag there will be an error:
Unmarshalling Error: cvc-datatype-valid.1.2.1: '' is not a valid value for 'integer'
This is understandable - the empty tag can not be represented as a number (null is not considered - null is the absence of a tag).
This can be solved using the default values ​​for the field.
@XmlElement(defaultValue = "-2147483648") Integer id;
<xs:element default="-2147483648" minOccurs="0" name="id" type="xs:int"/>
Moreover, if you pass an empty tag (<id />) to Java, the id variable will take the value -2147483648 (this is Integer.MIN_VALUE). This solution is a bit like the Null Object Pattern - when you need to specify that a null value is explicitly passed (and not just did not specify a value), then we pass a special null object.
en.wikipedia.org/wiki/Null_Object_patternFor dates, the same approach would look like this:
@XmlSchemaType(name = "date") @XmlElement(defaultValue = "1900-01-01") Date date;
<xs:element default="1900-01-01" minOccurs="0" name="date" type="xs:date"/>
@XmlSchemaType(name = "dateTime") @XmlElement(defaultValue = "1900-01-01T00:00:00") Date dateTime;
<xs:element default="1900-01-01T00:00:00" minOccurs="0" name="dateTime" type="xs:dateTime"/>
Documenting
Documentation of fields is done using Documentation annotation (from the jaxb-facets library).
@XmlSchemaType(name = "date") @Documentation(" ") @Facets(minInclusive = "1900-01-01", maxInclusive = "9999-12-31Z") Date date;
<xs:element minOccurs="0" name="date"> <xs:annotation> <xs:documentation> </xs:documentation> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:date"> <xs:maxInclusive value="9999-12-31Z"/> <xs:minInclusive value="1900-01-01"/> </xs:restriction> </xs:simpleType> </xs:element>
Documenting web service methods is done using the WSDLDocumentation annotation from CXF.
@WSDLDocumentation(" . XML Schema Restrictions") String facetsExampleMethod(FacetsExample request);
<wsdl:operation name="facetsExampleMethod"> <wsdl:documentation> . XML Schema Restrictions </wsdl:documentation> <wsdl:input message="tns:facetsExampleMethod" name="facetsExampleMethod"/> <wsdl:output message="tns:facetsExampleMethodResponse" name="facetsExampleMethodResponse"/> </wsdl:operation>
Further description of these fields and methods can be found in wsdl.
Generating service documentation (wsdl to html)
To convert the generated wsdl into a readable form, use the xslt
tomi.vanek.sk/index.php?page=wsdl-viewer file. SoapUI can also generate readable html. All this is described on StackOverflow:
stackoverflow.com/questions/686103/generating-html-documentation-from-wsdl Converting to html at the build stage is implemented in pom.xml Next this page will be available at <application path> /WebService.html
A few comments on the html generation:
Parameters of web methods are correctly displayed only if @SOAPBinding (style = SOAPBinding.Style.RPC) is specified.
Abstract Documentation gets into wsdl and, accordingly, into the documentation in html only when runtime generating wsdl from a running service. When building wsdl does not contain documentation. Why so - I do not know. Most likely the wsdl generator error.
Other jaxb-facets features
Briefly tell about the remaining features of jaxb-facets that I used:
Checking the value with a regular expression - Facets.pattern.
Setting the logic for processing whitespace characters - Facets.whiteSpace. Whitespace characters can be taken into account, removed, replaced with spaces (for tabs and line breaks). It works only when checking in xsd, i.e. Java code will get the value as is, with whitespace. It will be useful to check the mandatory filling of a string field, when the field is filled with spaces, should also be interpreted as empty.
In general, jaxb-facets supports all xsd restrinction. You can read them at the end of the page
www.w3schools.com/schema/schema_facets.asp Also in the latest version (2.2.6) there is support for xs: assert. Example xs: assert from the IBM website (
www.ibm.com/developerworks/ru/library/x-xml11pt2 ):
<xs:element name="dimension"> <xs:complexType> <xs:attribute name="height" type="xs:int"/> <xs:attribute name="width" type="xs:int"/> <xs:assert test="@height < @width"/> </xs:complexType> </xs:element>
Remarks
I repeat, in general, if a service is written “from scratch”, it is better to make a correct wsdl, and then generate Java code and documentation from it. But if there is already a written web service, then adding XSD Restrictions into it will make it more transparent by the client - he will see the logic of checking the input values ​​(although, of course, not all - in some cases it is impossible to verify the correctness of requests without accessing the DBMS or related resources). Plus, the generation of documentation will allow you to conveniently compare the compliance of real web methods with their parameters, those. assignment. Without this, one would have to check either the “spear” method - depending on the requests and responses of the web methods, or to understand the intricacies of the generated wsdl.