Learn about REST with JAX-RS
The writing of REST services in JAVA follows the JAX-RS (Jakarta RESTful Web Services) specifications and is achieved with the Eclipse Jersey and Eclipse Grizzly frameworks.
For this lab, we use the JSON and optionally XML representations (for XML serialisation, please have a look to the Addendum REST slides).
Read the source files in your favorite development environment. We favor using Eclipse. Before importing the project into Eclipse, build it by executing command mvn install. Then, import the Maven project using the menu File / import, then select Maven / Existing Maven Projects, and use the Browse button to select directory Learn-REST-JAX-RS.
Follow the instructions in the readme.md file in order to test the server:
- With the Junit tests.
- With some GET commands in a browser.
- With JAVA clients.
When you have understood the examples (especially the one with the skiers), you are ready to write your own RESTFul client and server.
Verify first that you are able to answer the following questions:
-
You do need to start the server before starting any
client. How do you start the server?
mvn exec:java@serverYou first have to compile the code withmvn clean install
-
On which TCP port is the server listening?
The TCP port is defined when you start the server. In our example the server reads in the file src/main/resources/rest.properties the address and the TCP port of the server:Then, it defines its baseURI (http://localhost:8083/MyServer/) and creates the REST server that will be listening for requests.rest.serveraddress=localhost:8083baseURI = "http://" + properties.getProperty("rest.serveraddress") + "/MyServer/"; GrizzlyHttpServerFactory.createHttpServer(URI.create(baseURI), rc)
-
What does that mean if I have the Address already in
use error when I start the server?
It means that another process is already listening to this port. You can use the following command to find out which process is using this port:If the answer is not empty, it gives you the details of the process listening on that port. If necessary, you can stop and remove it with the following command:lsof -i:8083(PID is the process number you have found with command lsof.)kill -9 PID
-
What is the URL you can use in the navigator to see the API of
the server?
It displays this kind of information. In this display, we have unfolded only resource calc resource, endpoint add.http://localhost:8083/MyServer/application.wadl— <application> <doc jersey:generatedBy="Jersey: 3.1.8 2024-08-02 14:29:24"/> <doc jersey:hint="This is simplified WADL with user and core resources only. To get full WADL with extended resources use the query parameter detail. Link: http://localhost:8083/MyServer/application.wadl?detail=true"/> — <grammars> — <include href="application.wadl/xsd0.xsd"> <doc title="Generated" xml:lang="en"/> </include> </grammars> — <resources base="http://localhost:8083/MyServer/"> — <resource path="/calc"> — <resource path="/add"> — <method id="addPlainText" name="GET"> — <request> <param name="a" style="query" type="xs:double"/> <param name="b" style="query" type="xs:double"/> </request> — <response> <representation mediaType="text/plain"/> </response> — </method> ...
-
In which JAVA class the skiers API is defined?
mw.learn.rest.jaxrs.b.serverSkiersInterface.
-
In which JAVA class the server class is defined?
mw.learn.rest.jaxrs.b.server.Main.
-
How do you specify the resources that will be handled by the
HTTP server?
With the following line of code, you define the name of the packages that contain the REST resources.final ResourceConfig rc = new ResourceConfig().packages(Skiers.class.getPackageName());
In this package, all the resources that have a @Path annotation are REST resources handled by the HTTP server. -
How the marshalling in JSON or in XML is realized?
The marshalling is handled automatically according to the returned class and the format provided by the resource and required by the client. In the following example, the returned value is of type Skier.The format provided by the method are JSON and XML. If the client requires JSON, the Jersey and JSON libraries will manage the marshalling in JSON.@GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/searchByName") public Skier searchName(@QueryParam("name") final String name) throws JAXBException, IOException { Skier skier; getSkiersFromFile(fileName); skier = skiers.lookupName(name); if (skier == null) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { return skier; } }
If the client requires XML, the Jersey and JAXB library will manage the marshalling in XML. In JAXB, some annotations are necessary in the returned class (Skier) to manage correctly the marshalling.
N.B.: The client needs also a Skier class for the unmarshalling of the returned value: See class mw.learn.rest.jaxrs.b.Skier in directory src/test/java. -
Are the server-side and client-side objects
(Person, Skier, Skiers)
of the same class?
No: on the server side, classes of package mw.learn.rest.jaxrs.b.server; on the client side, e.g. in the test class, classes of package mw.learn.rest.jaxrs.b.
In a way, this demonstrates that the example doesn't use JAVA serialisation. -
(Warning: you can give it a go, but our latest tests have revealed a problem with a lot of extra display output).
Use the logging facility to trace the interactions between the client and the server. For this purpose, provide a configuration file for the logging library through a JAVA variable.This option may be very usefull for debugging your REST clients in the future labs. Here is an exerpt of the output with logging.$ # in a terminal, start the server $ mvn exec:java@server $ # in another terminal, execute the client $ mvn exec:java@client-skier -Djava.util.logging.config.file=src/main/resources/logging.properties... --------------------------------------------------- skiers of 41 years old removed : FINE [sun.net.www.protocol.http.HttpURLConnection] sun.net.www.MessageHeader@25071a125 pairs: {DELETE /MyServer/skiers/delete/41 HTTP/1.1: null}{User-Agent: Jersey/3.1.8 (HttpUrlConnection 21.0.6)}{Host: localhost:8083}{Accept: */*}{Connection: keep-alive} FINE [sun.net.www.protocol.http.HttpURLConnection] sun.net.www.MessageHeader@351b23671 pairs: {null: HTTP/1.1 204 No Content} FINE [sun.net.www.protocol.http.HttpURLConnection] sun.net.www.MessageHeader@2a9518895 pairs: {GET /MyServer/skiers/alltxt HTTP/1.1: null}{Accept: text/plain}{User-Agent: Jersey/3.1.8 (HttpUrlConnection 21.0.6)}{Host: localhost:8083}{Connection: keep-alive} FINE [sun.net.www.protocol.http.HttpURLConnection] sun.net.www.MessageHeader@37df40433 pairs: {null: HTTP/1.1 200 OK}{Content-Type: text/plain}{Content-Length: 32} all skiers in plain text : Jean Claude Killy 73 years old ...