Simple RESTful web services with Glassfish
Join the DZone community and get the full member experience.
Join For FreeHere’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.
- Add the @Stateless annotation to the SimpleService class and an entity manager field annotated with @PersistenceContext along with the getters and setters.
- 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.
Published at DZone with permission of Andy Gibson, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments