This post is about
building a basic RESTful application with Maven using the JAX-RS 2.0 API (
JSR339: JAX-RS 2.0). The data is exchanged using the JSON format. You will also see how JEE7 can simplify persistence for you. As server we will use
Glassfish4 which contains the reference implementation of JEE7. At the end we will test our service with curl and with a unit test.
You can checkout this project from
Github or download the archive
here.
POM
So let's start. The only dependency we need in our
pom.xml is the following:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
Persistence
To be able to persist the data we first create a
basic entity class, nothing special about that.
@Entity
public class Customer {
@Id
private long id;
private String name;
// getters, setters and others
...
}
Using JEE7 we now have the possibility of using a default datasource for persistence. The
persistence.xml file looks a bit shorter though.
<persistence-unit name="mypu" transaction-type="JTA">
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create">
</property></properties>
</persistence-unit>
That's all about your persistence configuration. Featured by JEE7 we will use a
default datasource under
java:comp/DefaultDataSource. This is defined in Glassfish4 and connects to an integrated Derby database instance.
CRUD
To work with the
Customer object we first need a service that handles the persistence. The following stateless bean offers the common
CRUD functionality.
@Stateless
public class CustomerService {
@PersistenceContext
EntityManager em;
public void create(Customer entity) {
em.persist(entity);
}
public void update(Customer entity) {
em.merge(entity);
}
public void remove(long id) {
Customer customer = find(id);
em.remove(customer);
}
public Customer find(long id) {
return em.find(Customer.class, id);
}
}
To make sure that Glassfish find our bean we also need the
beans.xml in the
WEB-INF directory.
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemalocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all"></beans>
Make sure you use the
bean-discovery-mode attribute because otherwise the container will not find your bean. At this point we are done with persistence so far.
RESTful
So let's step further to the
RESTful part of our project. What we need is another stateless session bean with a method for each CRUD operation we want to support. The only thing we have to do for getting RESTful functionality is to use some annotations. On class level we annotate
@Path("customers") as path to our service. Then we use
@POST,
@GET,
@PUT and
@DELETE to denote the different RESTful operations. As we want to communicate through
JSON we use the
MediaType.APPLICATION_JSON.
@Stateless
@Path("customers")
public class CustomerResource {
@Inject
CustomerService service;
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void create(Customer customer) {
service.create(customer);
}
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Customer find(@PathParam("id") long id) {
return service.find(id);
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void update(Customer customer) {
service.update(customer);
}
@DELETE
@Path("{id}")
public void delete(@PathParam("id") long id) {
service.remove(id);
}
}
There is one last thing we need to do to complete our REST configuration.
@ApplicationPath("api") defines the global path to our application and this class has to extend javax.ws.rs.core.Application.
@ApplicationPath("api")
public class RESTConfig extends Application {
}
That's all the magic. Start the integrated Derby database with
asadmin start-database and fire up your
Glassfish server to try your new RESTful service.
curl
You can do a quick functionality check using
curl. To send a POST request to your server and thereby create and persist the data do something like this:
curl -i -X POST -d '{"id":1,"name":"Annabelle"}' http://localhost:8080/jee7-rest-crud/api/customers -H 'Content-Type: application/json'
Basically that means, send a
POST request containing that string to this address using the JSON format. Doing it right, your server will return something like that:
HTTP/1.1 204 No Content
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
Server: GlassFish Server Open Source Edition 4.0
Date: Fri, 20 Sep 2013 05:11:11 GMT
Status code
204 No Content is perfect here because we defined our RESTful service not to send any data back on a POST request. If you want to read the data you saved before you can send a
GET request:
curl -i -X GET http://localhost:8080/jee7-rest-crud/api/customers/1 -H 'Content-Type: application/json'
Your server should return a result like this:
HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
Server: GlassFish Server Open Source Edition 4.0
Content-Type: application/json
Date: Fri, 20 Sep 2013 05:10:16 GMT
Content-Length: 27
{"id":1,"name":"Annabelle"}
The
PUT (update) and
DELETE (remove) operations can be tested in the same way.
Unit Test
If you want to unit test your RESTful service you can now use the new
ClientBuilder from the JAX-RS 2.0 API coming with JEE7. But you will need some extra dependencies for the JSON data binding because Java do not yet provide that (likely to come with Java 8).
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.6.3</version>
<scope>test</scope>
</dependency>
To do the
data binding for JSON we need to implement the
MessageBodyReader<T> and
MessageBodyWriter<T> from the javax.ws.rs.ext package. These classes need to be annotated with
@Provider and
@Consumes respectively
@Produces. Check the
CustomerMessageBodyReader and
CustomerMessageBodyWriter classes of the project for implementation details.
Now we can write a
unit test to check the CRUD functionality of our RESTful service.
public class CustomerClientTest {
private static final String SERVER = "http://localhost:8080/jee7-rest-crud/api/customers";
private WebTarget target;
@Before
public void setUp() {
Client client = ClientBuilder.newClient();
client.register(CustomerMessageBodyReader.class);
client.register(CustomerMessageBodyWriter.class);
this.target = client.target(SERVER);
}
@Test
public void crud() {
Customer origin = new Customer(1, "Mathilda");
Entity entity = Entity.entity(origin, MediaType.APPLICATION_JSON);
// create
Response response = target.request(MediaType.APPLICATION_JSON).post(entity, Response.class);
assertThat(response.getStatus(), equalTo(204));
// read
Customer result = target.path(String.valueOf(origin.getId())).request(MediaType.APPLICATION_JSON).get(Customer.class);
assertThat(result, equalTo(origin));
// update
entity.getEntity().setName("Annabelle");
target.request().put(entity);
// delete
target.path(String.valueOf(origin.getId())).request(MediaType.APPLICATION_JSON).delete();
}
}
First we build the client and register the custom reader and writer classes. Then we set the target to the address of our service. The rest is straight forward testing of the various functionality we have built.
See also