Java web service пример

Создаем RESTful web service на Java с использованием Eclipse + Jersey + Glassfish3

Не так давно (в феврале этого года), я решил заняться программированием. В качестве языка был выбран Java. И с того момента я упорно изучаю все возможности данного языка. Недавно наткнулся на статью про RESTful на хабре. Прочитал и понял, что надо осветить альтернативный способ создания данных сервисов. Ещё меня поразило, насколько некоторые статьи пишутся непонятно для новичков. Я решил написать статью в которой расскажу и о прикладной части создания веб сервиса.

Я не претендую на истину. Я просто хочу показать простой и быстрый способ создать веб сервис.

Начало

Необходимое ПО

Самое главное это IDE. Я предпочитаю Eclipse. Данный проект я написал в Eclipse for Java EE Developers Juno. Четно говоря, предыдущий релиз был стабильнее, но этот внешне приятнее. В качестве фреймворка для REST я выбрал Jersey. Его легко найти и скачать. Точно так же как и саму IDE. В качестве сервера я установил GlassFish плагин для эклипса. Инструкцию по его установке точно так же легко найти. Ну на этом и всё.

Создание проекта

Итак. У нас есть Eclipse, папка с JAR-файлами Jersey и установленный GlassFish плагин. Теперь запускаем Eclipse и создаем пустой Dynamic Web Project, на последней вкладке не забываем отметить чек-бокс, который отвечает за генерацию web.xml файла.

Eclipse создаст нам пустой проект, который можно уже попробовать запустить на нашем сервере (Run As -> Run On Server).
После запуска появится встроенный браузер и покажет страничку Hello World. Теперь на надо скопировать JAR-файлы Jersey в папку [имя проекта]/ WebContent / WEB-INF / lib. Таким образом мы подключим все нужные библиотеки. Теперь очередь за web.xml файлом. Он находится в [имя проекта]/ WebContent / WEB-INF.
Вот листинг этого файла

  WebRest Jersey REST Service com.sun.jersey.spi.container.servlet.ServletContainer com.sun.jersey.config.property.packages ru.example.rest.resource  1  Jersey REST Service /rest/*   

Параметр com.sun.jersey.config.property.packages должен указывать у нас тот пакет в котором будут храниться наши файлы ресуры. А в тэге url-pattern хранится путь к нашему веб-сервису. Думаю не стоит рассказывать, что это основополагающий момент RESTful веб сервисов, ведь его сама идеология говорит о том, что каждый ресурс должен иметь свой адрес. Теперь наш веб сервис доступен по адресу localhost:8080/[имя нашего проекта]/rest/. Это будет являться нашим базовым URI.

Читайте также:  Webupd8team java ppa repository

Создание веб-сервиса

Создание сущностих

В качестве примера я хотел привести простой веб-севрис, который представляет из себя такой мини дневничок. Т.е. у нас есть один тип сущностей Message и с ним мы и будем работать.

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 + "]"; >> 

Единственное что тут может вызывать вопрос это аннотация @XmlRootElement. Она нам нужна чтобы Java сама преобразовывала данный класс в XML формат для передачи через HTTP. В остальном это обычный класс с тремя полями. Id сообщения, Title сообщения и Text сообщения.

Создание модели для работы с нашим классом

В качестве контент-провайдера я решил использовать обычный List. На данном этапе мне не хотелось бы углубляться в ORM и хранение данных в таблицах БД, потому что эта тема заслуживает отельной статьи, которая возможно будет следующей. И так я создал служебный класс который будет хранить в себе List наших сообщений и предоставлять методы для работы с этим листом.

Вот листинг класса для работы с данными

package ru.example.rest.model; import java.util.ArrayList; import java.util.List; import ru.example.rest.entity.Message; public class Data < private static Listdata; private static long count = 5; static < data = new ArrayList(); 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 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); >> 

Тут мы просто создаем List и заполняем его несколькими записями. Далее метод getData() возвращает нам ссылку на сам List. Что делают остальные методы легко догадаться по их названиям.

Теперь сам сервис

Сам сервис это ресурс-класс с аннотациями в котором указывается по какому URI какой ресурс или какое действие будет происходить в зависимости от типа запроса. Основные аннотации это
Path(«путь к данному ресурсу») данная аннотация указывает на конкретный адрес класса или метода.
так же есть разновидность Path(«») которая нам говорит о том что id это некая переменная которую мы сможем передать в наш метод.
Consumes (MediaType) и Produces(MediaType) будут говорить нам о получаемых и отправляемых данных соответственно. В нашем случае я выбрал APPLICATION_XML. Не спрашивайте почему не JSON, просто по мне так проще. Не зря же придумали JAX-Bind.
GET — говорит что этот метод будет отправлять данные от сервиса клиенту.
PUT/POST — говорит что этот метод будет добавлять/обновлять данные в наш сервис.
Лично я использую PUT для добавления, а POST для обновления.
DELETE — говорит о том что этот метод будет удалять данные из нашего хранилища

Вот листинг класса ресурса
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 ListgetAllMessages() < Listmessages = Data.getData(); if (messages == null) < throw new RuntimeException("Can't load all messages"); >return messages; > @GET @Path("") @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 can't add mesage with ") public void deleteMessage(@PathParam("id") long id) < if (Data.deleteMessageById(id) != true) < throw new RuntimeException("can't delete mesage with id can't update mesage with id http://localhost">localhost:8080/[имя проекта]/rest/message при запросе типа GET нам будет возвращаться список всех записей. при запросе POST с параметром Message у нас будет обновляться элемент Message. При запросе типа PUT c параметром Message у нас будет добавляться новый message в наше хранилище данных. При обращении к адресу методом типа GET localhost:8080/[имя проекта]/rest/message/id нам будет возвращаться message имеющий номер равный id. А если также обратиться методом DELETE то будет удален message с номером id. 

Заключение


Небольшой пример клиента

Для тестирования данного сервиса я написал небольшой Java клиент. Обычное java приложение которое демонстрирует обращение по всем методам и выводит результаты на консоль.

Вот его листинг

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 genericType = new GenericType() < >; List 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 message = service.path("rest").path("message") .path(String.valueOf(id)).accept(MediaType.APPLICATION_XML) .get(Message.class); System.out.println("Message with 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 delete message with 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(); >> 

Самые интересные строчки тут это

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

genericType это грубо говоря тип List, он нужен чтобы передавать и получать более сложные структуры используя JAX-B. И все данные мы передаем как JAXBElement.

В данный клиент необходимо включить так же библиотеки Jersey и включить файл описания класса Message.

И в заключении

В заключении хотелось бы сказать, что я буду очень рад если эта статья кому то поможет. Так же я готов выслушать любые предложения и любую критику. Я ведь только учусь.

Код клиента и код сервиса доступны на моем гитхабе ЗДЕСЬ.

Данные которыми я пользовался это статья Vogella и интернет.

Источник

Оцените статью