Enterprise Integration Zone is brought to you in partnership with:

Andy is a software developer of over 15 years experience and currently specializes in Java with Seam, Hibernate, Wicket and Spring as well as JSF, JPA and CDI. His own blog at www.andygibson.net is host to his Open Source projects DataValve , Knappsack and JTexGen and provides a home for his articles and tutorials on Java and Java EE of which he is a big supporter. He also has personal interests in 2D and 3D graphics as well as retro computing and gaming. You can follow him on twitter here. Andy is a DZone MVB and is not an employee of DZone and has posted 9 posts at DZone. View Full User Profile

Simple RESTful web services with Glassfish

02.04.2011
| 36484 views |
  • submit to reddit

Here’s a quick guide to creating a RESTful web service with Glassfish using JAX-RS.

First create a new maven project called restwebdemo using the jee6-sandbox-archetype so we have a model and some data to work with. To get this working with Glassfish, open the persistence.xml file and change the jta-data-source name to jdbc/__default. Also, make sure that the javaDB is up and running by going to $glassfish_dir/bin and typing asadmin start-database. Verify that the application is working correctly by going to http://localhost:8080/restwebdemo/ and you should get a list of courses.

Before we start getting to the interesting stuff, we have one more boring piece of configuration to perform specific to web services. We need to add the jersey servlet container to our web.xml file:

<servlet>
	<servlet-name>Jersey Web Application</servlet-name>
	<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>Jersey Web Application</servlet-name>
	<url-pattern>/rest/*</url-pattern>
</servlet-mapping>

This also tells Jersey to handle urls starting with /rest and pass it along to our web service methods.

Now we can dive right in an create a new server bean that will respond to requests for web services. For now we’ll just return a simple message from a POJO.

@Path("sample")
public class SimpleService {

	@Path("greet")
	@GET
	public String doGreet() {
		return "Hello Stranger, the time is "+ new Date();
	}
}

The path annotation on the class indicates that this is a root resource class and the path value given specifies the base URI for all the web service methods contained in the class.
On the doGreet method we have @Path which is used to specify the path template this method should match. The @GET annotation is used to differentiate between a sub-resource method that handles the actual web service request and a sub-resource locator method that returns an object that will instead be used to handle the request. In this case, the method has the @GET annotation which means this method handles the request and returns the result.
If you navigate to http://localhost:8080/restwebdemo/rest/sample/greet/ you should see a welcome message with the current date and time.

Now we’ll look at adding parameterized web services that extracts parameters from the request URL and uses them to form the output. Add the following method to the web service class:

@Path("sayHello/{name}")
@GET
public String doSayHello(@PathParam("name") String name) {
	return "Hello there "+name;
}

Again we have the path annotation to indicate what URLs this method will match, and this time we have have {name} added to the URL. This lets us extract a part of the url and give it a name. This name is used in the @PathParam annotation in the method signature to assign the URL fragment to the name parameter . To test our new method, redeploy the application and go to the URL http://localhost:8080/restwebdemo/rest/sample/sayHello/Andy to get the response Hello there Andy

We can also use request parameters to provide values to the method by using the @QueryParam annotation. We’ll create another method that is similar but uses a query parameter instead.

@Path("sayHello")
@GET
public String doSayHelloWithRequestParam(@QueryParam("name") String name) {
	return "Hi there "+name;
}

This time, the URL to use is http://localhost:8080/restwebdemo/rest/sample/sayHello?name=Andy to get the same message.

To make things more interesting, lets add a new page that lets us enter a name in a form and submit it to the web service. Add a new page called form.html with the following content :

<html>
<head>
<title>Insert title here</title>
</head>
<body>
<form action="rest/sample/sayHello" method="GET">
Name <input id="name" name="name"/> <input type="submit" />
</form>
</body>
</html>

Go to this page at http://localhost:8080/restwebdemo/form.html, enter your name and click submit and you should be greeted by name in the next page.

Notice that we had to set the form method to GET because our web service is only set up to respond to GET requests. If we change the form method to POST we can get the following error message :

HTTP Status 405 - Method Not Allowed
type Status report
message Method Not Allowed
description The specified HTTP method is not allowed for the requested resource (Method Not Allowed).

Remember, with REST, those actual verbs have meaning and adds meaning to the request so it is strict on how it matches the method to be called.

To solve this problem, we can add a new method to handle form POSTs like so :

@Path("sayHello")
@POST
public String doSayHelloWithFormParam(@FormParam("name") String name) {
	return "Hi there " + name;
}

Here we changed the @GET to a @POST to allow the different verb and changed the annotation on the name method parameter to @FormParam. The path remains the same because we can have service methods that match the same path, but for different request verbs. We can even have the same verb and path as long as the content type returned is different. The content type is used to specify the type of output the is returned from the method. It is set by adding a @javax.ws.rs.Produces (not to be confused with the CDI Produces annotation). The annotation takes a string parameter that indicates the type of media returned from the method. Common media types are defined as constants in the MediaType class so you can use :

@Path("sayHello")
@POST
@Produces(MediaType.APPLICATION_XML)
public String doSayHelloWithFormParam(@FormParam("name") String name) {
	return "<message>Hi there " + name+"</message>";
}

If you run your form again, and post it, you will get an xml response as follows :

<message>Hi there Andy</message>

Depending on your browser, if you return just the text, you will get an error because the plain text isn’t valid XML and the browser expects XML because that is the response type set on the response from the web service.

To finish up, we are going to do something a little more interesting, we will create a web service to return the name of a course from the database using the sandbox data built into the archetype. For various reasons, we will take the most direct route to getting data access which is to make the web service bean a stateless bean and inject a persistence context using the @PersistenceContext annotation.

  1. Add the @Stateless annotation to the SimpleService class and an entity manager field annotated with @PersistenceContext along with the getters and setters.
  2. Add a new method to return the course name for the given course id parameter. We will return it as text for the time being :

 

@Path("courseName/{id}")
@GET
public String getCourseNameFromId(@PathParam("id") Long id) {
	Course c = entityManager.find(Course.class, id);
	if (c == null) {
		return "Not Found, try the index <a href='/restwebdemo/'>page</a> and come back";
	} else {
		return c.getTitle();
	}
}

Note that the automatic type conversion takes place and the value is converted to a Long automatically. If the course is not found, we suggest the user goes to the main page of the demo. We aren’t just being overly helpful, the test data is generated when you request one of the application pages for the first time. In the current persistence context, when you redeploy, the database is dropped and rebuilt so it will be empty. You need to go to the front page to automatically create the data and then go back to your page to view the course. An example URL is http://localhost:8080/restwebdemo/rest/sample/courseName/124.

Of course you could grab the course object and build your own XML or JSON response to send back to the client, or use a third party library like Jackson to build the JSON response. However, as we’ll see next time, Java EE 6 has all these goodies built in for us, and with a few annotations, we’ll be slinging objects back and forth in no time at all.

You can download the source code for the project from here. Simply unzip, build with maven (mvn clean package) and deploy to Glassfish.





References
Published at DZone with permission of Andy Gibson, author and DZone MVB. (source)

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

Comments

Alexis MP replied on Sat, 2011/02/05 - 7:35am

Hi Andy, you can write even more portable JAX-RS applications with the 1.1 API (part of Java EE 6). Simply use a @ApplicationPath-annotated javax.ws.rs.core.Application class for that. Milan has a good example here. Using @PersistenceContext to inject an EntityManager is not thread-safe. So unless you make the JAX-RS resource an EJB (say using @Stateless) you probably want to use @PersistenceUnit to inject an EntityManagerFactory instead and derive EntityManager from there. cheers, (>code> tags don't work with the preview mode, I hope they make it into the post...)

Andy Gibson replied on Sun, 2011/02/06 - 9:47pm

Hey Alexis, I do make the bean a stateless session bean as mentioned in item 1 just before the last piece of code. I also just added that last example at the end there to come up with something more useful than just returning a constant string based on the input parameters. Part 2 expands on all this a little more and uses other beans to handle the persistence. 

Cheers,

Andy

Alexis MP replied on Mon, 2011/02/07 - 4:47am

Nice! I'll read more carefully next time... Looking forward to Part 2!

Carla Brian replied on Wed, 2012/05/30 - 11:20am

I am kinda new also with the Glassfish. I have heard good feedback about this. I might want to try this one as well. - DR Marketing Group

Passion Lab replied on Sun, 2012/09/02 - 4:00pm

you can write best term papers http://www.best-term-paper.us/ even more portable JAX-RS applications with the 1.1 API (part of Java EE 6). Simply use a @ApplicationPath-annotated javax.ws.rs.core.Application class for that.

Comment viewing options

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