Simple RESTful services in Glassfish Pt 2
In part 2 of this article, we are going to create a data driven web service that will return JSON and XML to the client, and then use jQuery to add a new item to the database and display it in our page.
In part 1, we looked at creating simple web services and now we’re going to look at making something more practical and interesting. We’ll start from where we left off with the source code as it was at the end of the part 1 which you can download from here (restwebdemo_pt1) if you want to follow along. If not, the final source can be downloaded from here (restwebdemo_pt2).
- First off, we’re going to change the entity manager that is available for injection to be request scoped. To do this, open up
DataRepositoryProducer.java
and change the@ConversationScoped
annotation on thegetEntityManager()
method to be@RequestScoped
. The reason for this is documented here in A Little Less Conversation. - Next we are going to create a simple dao for Course objects, and the only reason to do this is to demonstrate the integration of CDI and the ability to layer your code. Create a new class called
CourseDao
with the following code.package org.fluttercode.restwebdemo.bean; @Stateless @LocalBean public class CourseDao { @Inject @DataRepository private EntityManager entityManager; public void save(Course course) { entityManager.persist(course); } public Course update(Course course) { return entityManager.merge(course); } public Course find(Long id) { return entityManager.find(Course.class, id); } }
This just injects an entityManager and uses it to locate, save and update Course objects.
- Now create a new
CourseService
bean that will handle the web services. To start with we want to make it a stateless EJB and inject the course Dao. We are going start by re-implementing the method to return the course name for the given id.@Path("courseName/{id}") @GET public String getCourseName(@PathParam("id") Long id) { Course course = courseDao.find(id); if (course == null) { return "Course not found"; } else { return course.getTitle(); } }
To see this method in action, deploy the application and go to http://localhost:8080/restwebdemo/rest/course/courseName/126. Now we know everything is working and hooked up together, we can look at adding some new functionality.
Let’s start by returning a course with a given id from the service. This is fairly simple given what we already know. The only thing to determine now is what format to return the object as and to convert it to that type. Luckily, Java EE already provides JAXB which can take an object graph and convert it to XML for us as long as we annotate the classes with the annotations to let the JAXB implementation know how to convert it. The same annotations can be used by the body writer that handles JSON.
First we’ll annotate the Course
class and make a couple of changes that we need to. Next we’ll create methods to return a Cource
object from the service in XML or JSON format.
- Open the
Course
class and add the following annotations to the class.@Entity @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Course extends BaseEntity { ... ... }
This tells the JAXB processor that this class can be serialized and to access the values using fields in the class. This also means that any other annotations we want to add to control the serialization needs to be applied to the fields.
- Now in our
CourseService
class we will create methods to return the course entity and we will create one for JSON and one for XML.@Path("find/{id}/xml") @GET @Produces(MediaType.APPLICATION_XML) public Course getCourseAsXml(@PathParam("id") Long id) { return courseDao.find(id); } @Path("find/{id}/json") @GET @Produces(MediaType.APPLICATION_JSON) public Course getCourseAsJson(@PathParam("id") Long id) { return courseDao.find(id); }
This is a simple example, so we don’t want to serialize the teacher
or enrolled
properties which we can do by marking them with the @XmlTransient
attributes. Also, remove the @NotNull
annotation from the teacher
attribute as we will need it blank later. The following code shows the fields with both the JAXB and JPA annotations. JAXB (like JPA) uses default conventions for fields that don’t have annotations :
@Column(length = 32, nullable = false) @Size(max = 32) @NotEmpty(message = "title is required") private String title; @Column(length = 8, nullable = false) @Size(max = 8 ) @NotEmpty(message = "code is required") private String code; @ManyToOne(fetch = FetchType.LAZY) @XmlTransient private Teacher teacher; @ManyToMany(mappedBy = "enrolled") @XmlTransient private List<Student> students = new ArrayList<Student>();
We are just using a simple JAXB model for the sake of the example which is why we aren’t including the Teacher
and Student
classes.
(note : At this point, I had to switch to using Hibernate as the JPA provider since JAXB didn’t like the interface EclipseLink used for proxying the properties. You can do this using the Glassfish update tool).
If you redeploy the application and browse to http://localhost:8080/restwebdemo/rest/course/find/1/json you should be prompted to save a file, or it will display the text, but the content should be something like :
{"createdOn":"2010-08-27T16:36:57.015-04:00", "id":"1", "modifiedOn":"2010-08-27T16:36:57.015-04:00", "title":"Computing for Beginners", "code":"CS101"}
or if you go to http://localhost:8080/restwebdemo/rest/course/find/1/xml you will get an XML version :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <course> <createdOn>2010-08-27T16:36:57.015-04:00</createdOn> <id>1</id> <modifiedOn>2010-08-27T16:36:57.015-04:00</modifiedOn> <title>Computing for Beginners</title> <code>CS101</code> </course>
Now we can grab objects from our web service, we should look at creating objects from the service. We add a new method that takes the title and code values, creates a new Course
with those values and saves it using the courseDao
.
@Path("create") @PUT @Produces(MediaType.APPLICATION_JSON) public Course createCourse(@FormParam("title") String title,@FormParam("code") String code) { Course course = new Course(); course.setTitle(title); course.setCode(code); courseDao.save(course); return course; }
Here I’ve used the FormParam
annotations to plug form values into the method call. You’ll notice that using REST conventions, the method to create a course uses the PUT type of request. Now let’s create a page to enter a title and code and create the course. Notice that our method returns the created course so we can return the course back to the user. This is probably not ideal, but suits for the purposes of demonstration. Now lets create a new HTML page to allow for data entry and calling the web service to create the Course.
<html> <head> <title>Insert title here</title> <script type="text/javascript" src="http://localhost:8080/restwebdemo/jquery-1.4.min.js"> </script> </head> <body> <div id="message" style="display: none; background: #d0d0f0; padding: 12px">Message Div</div> <form action="rest/course/create" method="POST"> <fieldset> <legend>Create Course</legend> <p> Title<br /> <input id="title" /><br /> </p> <p> Code<br /> <input id="code" /><br /> </p> <input type="submit" id="submit" /> </fieldset> </form> </body> <script type="text/javascript"> //jquery pieces $(document).ready(function() { //change the submit button behaviouus. $('#submit').click(function () { var title = $("input#title").val(); var code = $("input#code").val(); params = "title="+title+"&code="+code; //alert("posting form : "+data); $.ajax({ type: "PUT", url: "rest/course/create", data: params, success: function(result) { showMessage("Created Course "+result.title+" with id "+result.id+" on "+result.createdOn); } }); return false; }); }); function showMessage(msg) { $('#message').html(msg); $('#message').fadeIn('fast'); $('#message').delay(3000).fadeOut('slow'); } </script> </html>
This looks a lot code, but not really. We import jquery to help us post our form, and we create our form with the two fields. We use JQuery to add an event handler so when you click submit, it packages up the form, calls our web service with a PUT type of request and grabs the returned object as a JSON object, and displays a message using the values from the new instance obtained from the server. To verify that your course has been created, go to the front page and you should see it listed.
That about wraps it up for this post, the source code can be downloaded from (restwebdemo_pt2), just unzip it, use mvn clean package
and deploy the war to glassfish and use the URLs mentioned in the article.