Developers working with REST and XML/HTTP services have traditionally used light-weight APIs, such as java.net classes or Apache HttpClient. Web services APIs provided by JAX-RPC were SOAP and "enterprise" only and they required J2EE libraries. The situation changed with the release of JAX-WS and its inclusion into Java SE 6. JAX-WS supports RESTful services; also, its Dispatch interface allows clients to work with XML directly as opposed to having to use object mapping or completely unwieldy SAAJ API. A nice example of using JAX-WS to implement a client for a RESTful service is available in Mark Hadley's blog.
However, I don't think that JAX-WS is going to become the primary way of implementing RESTful clients just yet. In many situations, HttpClient provides more flexibility and control over HTTP calls.
For example, it is very straightforward to turn on basic authentication for a client using HttpClient APIs: (more...)
This is a brief tutorial describing how to create a Web service using WSDL and annotations.
The approach presented here allows you to design and implement your WSDL/Schema and Java classes
independently without having to generate anything. You can then use annotations
to map Java classes to appropriate WSDL and Schema elements.
Since JAXB and JAX-WS have sensible defaults, the number of annotations can be kept
to the minimum.
In my opinion, it is always best to develop WSDL and schemas by hand to ensure that the service contract is appropriately defined and also that the schema can be re-used (by other services) and extended if necessary. I do not recommend using annotations for automatically producing WSDL and schemas at runtime as this leads to simplistic schemas and WSDLs.
Generating classes from WSDL/schema sometimes makes sense, say in situations when you have to use a pre-defined schema. However, as with any code generation, it creates a maitainance problem especially if there is a need to put behavior into generated classes. So it is desirable to be able to evolve object model and service implementation classes independently from WSDL/schemas. You can do it quite easily by following the steps below.
Even though I use the word "create" when describing some of the steps, the same approach will also work for updates or refactoring caused by changes in service requirements.
The steps are the following:
public String add( Person person )
xsd:token instead of xsd:string since "token" does not allow
carriage return, tabs, and leading spaces:
<xsd:element name="person">
<xsd:complexType >
<xsd:sequence>
<xsd:element name="ssn" type="xsd:token"
minOccurs="1" maxOccurs="1"/>
<xsd:element name="firstName" type="xsd:token"
minOccurs="1" maxOccurs="1"/>
<xsd:element name="lastName" type="xsd:token"
minOccurs="1" maxOccurs="1" />
<xsd:element name="address" type="Address"
minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<wsdl:definitions
targetNamespace="http://personservice"
xmlns="http://personservice"
xmlns:pers="http://person"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xsd:import namespace="http://person"
schemaLocation="person.xsd" />
<!-- Return value -->
<xsd:element name="status" type="xsd:string" />
</wsdl:types>
...
// By default JAXB uses getters/setters for marshaling/unmarshaling
// Using fields access allows us not to define getters/setters for
// all mapped instance variables.
@XmlAccessorType(XmlAccessType.FIELD)
// We need to provide the namespace, otherwise it defaults to
// the package name
@XmlRootElement(namespace="http://person")
public class Person {
// we only need annotations for variables that don't match
// element names
private String ssn;
private String firstName;
private String lastName;
@XmlElement(name = "address")
protected List<Address> addresses;
...
@XmlRegistry
public class ObjectFactory {
public Person createPerson() {
return new Person();
}
public Address createAddress() {
return new Address();
}
}
@WebService(
// all values must match corresponding attributes
// and elements of the WSDL file
// Name of the port type in WSDL
name="PersonService",
// Target namespace of WSDL, could be
// different from the schema namespace
targetNamespace="http://personservice",
serviceName="PersonServicePorts",
portName="PersonService",
// the file must be available to Web container
wsdlLocation="WEB-INF/wsdl/PersonService.wsdl"
)
/*
* We'are using "bare" style since "add" operation
* takes only one argument, person document. Therefore, there
* is no need to "wrap" it by nesting it inside operation element.
* Note that "wrapped" style is the default.
*/
@SOAPBinding(parameterStyle=SOAPBinding.ParameterStyle.BARE)
public class PersonService {
@WebResult(name = "status")
public String add( Person person ) {
System.out.println("Adding person :"+person );
return "Added "+person.getFullName();
}
}
SoapUI Project (you can import it into SoapUI).
You can also download all files in a zip file.