I've been developing software for over 15 years working in Delphi and now Java. This site is a home for my open source projects, writings, articles and tutorials mainly focusing on Java and Java EE. Andy is a DZone MVB and is not an employee of DZone and has posted 34 posts at DZone. You can read more from them at their website. View Full User Profile

Simple RESTful services in Glassfish Pt 2

03.22.2011
| 4692 views |
  • submit to reddit

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).

  1. 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 the getEntityManager() method to be @RequestScoped. The reason for this is documented here in A Little Less Conversation.
  2. 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.
  3. 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.

  1. 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.

    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.

  2. 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);
    }
  3. (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.

     

From http://www.andygibson.net/blog/tutorial/simple-restful-services-in-glassfish-pt-2/
Published at DZone with permission of Andy Gibson, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Gervais Blaise replied on Tue, 2011/03/22 - 3:14am

I think that is not a RESTfull services. For two reason : You are using "verbs" into your URI : @Path("find/{id}/xml") You are not using content negociation : @Path("find/{id}/xml") , @Path("find/{id}/json"). You can use the extension to specify the response type but not an URI part.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.