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

Good evening everyone!

Not so long ago (in February of this year), I decided to learn programming. As the language chosen was Java. And since I persistently exploring all possibilities of the language. Recently came across an article about RESTful habré. Read and understand that it is necessary to highlight an alternative way of creating data services. I was also struck by how some articles are written it is not clear for beginners. I decided to write an article which will tell about the application creating the web service.

I do not claim to truth. I just want to show a simple and quick way to create a web service.

Start


Required BY

The most important thing is the IDE. I prefer Eclipse. This project I've written in Eclipse for Java EE Developers Juno. Honestly, the previous release was more stable, but that looks nicer. As a framework for the REST I chose Jersey. It is easy to find and download. In the same way as the IDE itself. As a server I have installed GlassFish plugin for Eclipse. Manual installation is just as easy to find. Well, that's all.

creating a project

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


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

the
<?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>EN.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 must specify the package in which to store our files for resources. And in the tag url-pattern is the path to our web service. I think you should not tell that this is a fundamental moment of RESTful web services, because its very ideology suggests that each resource should have its own address. Now our web service is available at localhost:8080/[our project]/rest/. This will be our base URI.

Creating web service


Create sushnosti

As an example, I wanted to give you a simple web sevris, which consists of a mini diary. I.e. we have a single entity type Message and with it we and will work.

Here is the Message class

the
package EN.example.rest.entity;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Message {
private long messageId;
private String messagetitle line that is similar;
private String messageText;

public Message() {

}

public Message(long messageId, String messagetitle line that is similar, String messageText) {

this.messageId = messageId;
this.messagetitle line that is similar = messagetitle line that is similar;
this.messageText = messageText;
}

public long getMessageId() {
return messageId;
}

public void setMessageId(long messageId) {
this.messageId = messageId;
}

public String getMessageTitle() {
return messagetitle line that is similar;
}

public void setMessageTitle(String messagetitle line that is similar) {
this.messagetitle line that is similar = messagetitle line that is similar;
}

public String getMessageText() {
return messageText;
}

public void setMessageText(String messageText) {
this.messageText = messageText;
}

@Override
public String toString() {
return "Message [messageId=" + messageId + ", messagetitle line that is similar="
+ messagetitle line that is similar + ", messageText=" + messageText + "]";
}

}


The only thing that may cause issue is the annotation @XmlRootElement. We need it to Java itself convert this class to XML format for transmission through HTTP. The rest is ordinary class with three fields. Message Id, message Title and Text message.
create 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 go deep into ORM and storing data in database tables, because this subject deserves the hospitality of the article, which may be the following. And so I created a utility class which will keep a List of our messages and to provide methods for working with this sheet.

Here is the listing class for working with data

the
package EN.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 the 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 some records. Then the getData() method returns us a link to the List. What do the rest of the methods are easy to guess by their names.

Now the service

The service itself is a resource class with annotations specifying what URI or what resource what action will occur depending on the type of request. Basic annotation
Path("path to this resource") this annotation indicates the 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 which we can pass into the method.
Consumes (MediaType) and Produces(MediaType) will tell us about sent and received data, respectively. In our case, I chose APPLICATION_XML. Don't ask me why not JSON, just for me it's easier. No wonder that invented 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 the data in our service.
Personally I use PUT to add, and POST to update.
DELETE, says that this method will delete the data from our repository

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

the
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 I should clarify a few points. Our class resource will be available at localhost:8080/[project name]/rest/message when prompted we GET will return a list of all records. when a POST request with a Message, we will update the Message element. When prompted PUT the c Message parameter will be appended a new message to the data store. When you try to access a method like GET localhost:8080/[project name]/rest/message/id we will return the message having the number equal to id. And if also to apply the method be deleted DELETE the message with id number.

Opinion


a Small example client

To test this service, I have written a small Java client. A plain java application which demonstrates the appeal on all methods and prints the results to the console.

Here is his listing

the
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 line here is the

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


genericType is roughly a List type, it is necessary to send and receive more complex structures using JAX-B. And all of the data we pass in as a JAXBElement.

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

in conclusion


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

Client code and service code available on my github HERE.

The data which I enjoyed this article Vogella.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

A Bunch Of MODx Revolution + LiveStreet

Monitoring PostgreSQL with Zabbix

PostgreSQL load testing using JMeter, Yandex.Tank and Overload