📜 ⬆️ ⬇️

Create a RESTful web service in Java using Eclipse + Jersey + Glassfish3

Good evening everyone!

Not so long ago (in February of this year), I decided to start programming. Java was selected as the language. And from that moment on I have been studying hard all the possibilities of this language. Recently I came across an article about RESTful on Habré. I read and understood that it is necessary to highlight an alternative way to create these services. I was also struck by how incomprehensible some articles are written for beginners. I decided to write an article in which I will talk about the applied part of creating a web service.

I do not claim the truth. I just want to show a simple and fast way to create a web service.
')

Start


Required software

The most important is IDE. I prefer Eclipse. I wrote this project in Eclipse for Java EE Developers Juno. Even speaking, the previous release was more stable, but this one looks more pleasant. As a framework for REST, I chose Jersey. Its easy to find and download. Just like the IDE itself. As a server, I installed the GlassFish plugin for Eclipse. Installation instructions are just as easy to find. Well, that's it.

Creating a project

So. We have Eclipse, a folder with Jersey JAR files and a GlassFish plugin installed. Now we run Eclipse and create an empty Dynamic Web Project, on the last tab we do not forget to check the check box, which is responsible for generating the web.xml file.


Eclipse will create us an empty project that you can already try to run on our server (Run As -> Run On Server).
After launch, the built-in browser will appear and show the Hello World page. Now, you need to copy the Jersey JAR files to the [ project name ] / WebContent / WEB-INF / lib folder. Thus we will connect all the necessary libraries. Now it's the turn for the web.xml file. It is located in [ project name ] / WebContent / WEB-INF .
Here is the listing of this file.

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>WebRest</display-name> <servlet> <servlet-name>Jersey REST Service</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>ru.example.rest.resource</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey REST Service</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> </web-app> 


The com.sun.jersey.config.property.packages parameter should specify the package in which our recovery files will be stored. And the url-pattern tag stores the path to our web service. I think it is not necessary to tell that this is a fundamental moment of RESTful web services, because its ideology itself says that every resource should have its own address. Now our web service is available at localhost : 8080 / [ our project name ] / rest /. This will be our base URI.

Creating a web service


Creating Entities

As an example, I would like to give a simple web sevris, which is such a mini diary. Those. we have one type of Message entity and we will work with it.

Here is the Message class

 package ru.example.rest.entity; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Message { private long messageId; private String messageTitle; private String messageText; public Message() { } public Message(long messageId, String messageTitle, String messageText) { this.messageId = messageId; this.messageTitle = messageTitle; this.messageText = messageText; } public long getMessageId() { return messageId; } public void setMessageId(long messageId) { this.messageId = messageId; } public String getMessageTitle() { return messageTitle; } public void setMessageTitle(String messageTitle) { this.messageTitle = messageTitle; } public String getMessageText() { return messageText; } public void setMessageText(String messageText) { this.messageText = messageText; } @Override public String toString() { return "Message [messageId=" + messageId + ", messageTitle=" + messageTitle + ", messageText=" + messageText + "]"; } } 


The only thing that can cause a question here is the abstract @XmlRootElement. We need it so that Java itself converts this class to the XML format for transfer via HTTP. The rest is the usual class with three fields. Message Id, Title Message and Text Message.

Creating a model for working with our class


As a content provider, I decided to use a regular List. At this stage, I would not like to delve into the ORM and data storage in database tables, because this topic deserves a separate article, which may be next. And so I created a utility class that will store the List of our messages in itself and provide methods for working with this sheet.

Here is the class listing for working with data.

 package ru.example.rest.model; import java.util.ArrayList; import java.util.List; import ru.example.rest.entity.Message; public class Data { private static List<Message> data; private static long count = 5; static { data = new ArrayList<Message>(); data.add(new Message(1L, "Hello", "Hello! I'm first entry!")); data.add(new Message(2L, "2nd", "second messages")); data.add(new Message(3L, "here again!", "some text")); data.add(new Message(4L, "HI!", "pam param")); } public static List<Message> getData() { return data; } public static Message findMessageById(long id) { for (Message message : data) { if (message.getMessageId() == id) { return message; } } return null; } public static boolean deleteMessageById(long id) { boolean result = false; for (Message message : data) { if (message.getMessageId() == id) { result = data.remove(message); return result; } } return result; } public static boolean updateMessage(Message message) { boolean result = false; for (Message temp: data) { if (temp.getMessageId() == message.getMessageId()) { temp.setMessageText(message.getMessageText()); temp.setMessageTitle(message.getMessageTitle()); result = true; } } return result; } public static boolean addMesage(Message message) { message.setMessageId(count); count++; return data.add(message); } } 


Here we simply create a List and fill it with several entries. Next, the getData () method returns a link to the List itself. What other methods do is easy to guess by their names.

Now the service itself

The service itself is a resource class with annotations in which it is indicated by which URI which resource or action will take place depending on the type of request. The main annotations are
Path ("path to this resource") this annotation points to a specific address of a class or method.
there is also a kind of Path ("{id}") which tells us that id is a kind of variable that we can pass to our method.
Consumes (MediaType) and Produces (MediaType) will talk to us about the data sent and received, respectively. In our case, I chose APPLICATION_XML. Don't ask why not JSON, it's just easier for me. No wonder they invented the JAX-Bind.
GET - says that this method will send data from the service to the client.
PUT / POST - says that this method will add / update data to our service.
Personally, I use PUT to add, and POST to update.
DELETE - says that this method will delete data from our storage

Here is the resource class listing.
package ru.example.rest.resource;

 import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.xml.bind.JAXBElement; import ru.example.rest.entity.Message; import ru.example.rest.model.Data; @Path("message") public class MessageResource { @GET @Produces(MediaType.APPLICATION_XML) public List<Message> getAllMessages() { List<Message> messages = Data.getData(); if (messages == null) { throw new RuntimeException("Can't load all messages"); } return messages; } @GET @Path("{id}") @Produces(MediaType.APPLICATION_XML) public Message getMessageById(@PathParam("id") long id) { Message message = Data.findMessageById(id); if (message == null) { throw new RuntimeException("can't find mesage with id = " + id); } return message; } @PUT @Consumes(MediaType.APPLICATION_XML) public void addMessage(JAXBElement<Message> message) { if (Data.addMesage(message.getValue()) != true) { throw new RuntimeException("can't add mesage with id = " + message.getValue().getMessageId()); } } @DELETE @Path("{id}") public void deleteMessage(@PathParam("id") long id) { if (Data.deleteMessageById(id) != true) { throw new RuntimeException("can't delete mesage with id = " + id); } } @POST @Consumes(MediaType.APPLICATION_XML) public void updateMessage(JAXBElement<Message> message) { if (Data.updateMessage(message.getValue()) != true) { throw new RuntimeException("can't update mesage with id = " + message.getValue().getMessageId()); } } } 


I think it is worth clarifying a few points. Our resource class will be available at localhost : 8080 / [ project name ] / rest / message when we request a GET type, we will return a list of all entries. when you request a POST with the Message parameter, we will update the Message element. When requesting a PUT type with the Message parameter, we will add a new message to our data storage. When accessing the address using the GET method, localhost : 8080 / [ project name ] / rest / message / id will return a message with a number equal to id. And if you also call DELETE, the message with id number will be deleted.

Conclusion


A small customer example

To test this service, I wrote a small Java client. Normal java application that demonstrates the treatment of all methods and displays the results on the console.

Here is its listing

 public class RestClient { public static void main(String[] args) { ClientConfig config = new DefaultClientConfig(); Client client = Client.create(config); WebResource service = client.resource(getBaseURI()); /* * Get list of messages */ GenericType<List<Message>> genericType = new GenericType<List<Message>>() { }; List<Message> messages = service.path("rest").path("message") .accept(MediaType.APPLICATION_XML).get(genericType); for (Message temp : messages) { System.out.println(temp); } /* * Get message by ID */ long id = 4; Message message = service.path("rest").path("message") .path(String.valueOf(id)).accept(MediaType.APPLICATION_XML) .get(Message.class); System.out.println("Message with ID = " + id); System.out.println(message); /* * Update message */ message.setMessageTitle("udated title"); message.setMessageText("updated text"); service.path("rest").path("message").post(message); message = service.path("rest").path("message").path(String.valueOf(id)) .accept(MediaType.APPLICATION_XML).get(Message.class); System.out.println("Message with ID = " + id); System.out.println(message); /* * Delete message */ System.out.println("delete message with ID = " + id); service.path("rest").path("message").path(String.valueOf(id)).delete(); messages = service.path("rest").path("message") .accept(MediaType.APPLICATION_XML).get(genericType); for (Message temp : messages) { System.out.println(temp); } /* * Put message */ System.out.println("puttin' message"); message = new Message(); message.setMessageText("PUT MESSAGE!"); message.setMessageTitle("Put message"); service.path("rest").path("message") .accept(MediaType.APPLICATION_XHTML_XML).put(message); messages = service.path("rest").path("message") .accept(MediaType.APPLICATION_XML).get(genericType); for (Message temp : messages) { System.out.println(temp); } } private static URI getBaseURI() { return UriBuilder.fromUri("http://localhost:8080/WebRest").build(); } } 


The most interesting lines here are

 GenericType<List<Message>> genericType = new GenericType<List<Message>>() {}; List<Message> messages = service.path("rest").path("message") .accept(MediaType.APPLICATION_XML).get(genericType); 


A genericType is, roughly speaking, a List type; it is needed to transmit and receive more complex structures using JAX-B. And we transfer all data as JAXBElement.

In this client, you must include the same library Jersey and include the description file class Message.

And in conclusion


In conclusion, I would like to say that I would be very happy if this article will help someone. I am also ready to listen to any suggestions and any criticism. I'm just learning.

The client code and service code are available on my github HERE .

The data I used is the Vogella article and the internet.

Source: https://habr.com/ru/post/150176/


All Articles