Давайте в этой статье мы рассмотрим технологию web service JAX-RS, которая позволяет нам при помощи аннотаций
превращать наш класс в RESTful веб службу, а потом с помощью
протокола HTTP формировать запросы и ответы. Я как обычно буду использовать сервер
приложений wildfly-8.2.0.Final, в него
встроены библиотеки apache cxf. Основной протокол по которому контактирует наш
веб сервис это HTTP, по этому нам нужно собрать проект как web application, т. е. в файл с расширением *.war и соблюсти правильность развертывания
проекта для сервера приложений wildfly. Я думаю надо рассказать как появился JAX-RS, с начало был протокол JAX-WS (Web service). У протокола JAX-WS есть параметр action который
помогал выбирать и выполнять определенную операцию (operation) , т.е.
посредством параметра action мы могли выбирать одну из функций java. Функции обычно подразделялись на просмотр данных, удаление данных,
добавление новых данных или редактирование информации, в простонародье это
называется CRUD операции Create, Read, Update, Delete. Имена параметров Action при этом могли выбираться случайно, не
было стандарта именования. Как мы все знаем,
web серверы используют
протокол HTTP, и у него есть 8 стандартных основных операций: GET, PUT, DELETE, POST, HEAD, OPTION, CONNECT,TRACE. Вот ими и предложили заменить параметр action. Теперь был
стандарт протокола и каждой основной операции протокола HTTP, сопоставили одну из операций удаления, вставки, создания или
редактирования, так появился стандарт JAX-RS или как его еще называют RESTful веб служба. GET, HEAD используются при
чтении, DELETE при удаление, PUT вставка или
изменение записей, POST при вставке, при этом могут
задействоваться такие механизмы как параметры cookies, параметры
сессии, параметры запроса http GET, параметры заголовка Header и т.д. Приступим к реализации
простого приложения. Для работы RESTful веб службы нам нужно перенаправить все наши HTTP запросы на сервлет javax.ws.rs.core.Application.
Посредством этого сервлета мы будем вызывать наши операции. Добавим в файл web.xml сервлет, в примере сервлет помечен
желтым цветом:
<?xml
version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Web Application</display-name>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>120</session-timeout>
</session-config>
</web-app>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Web Application</display-name>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>120</session-timeout>
</session-config>
</web-app>
Надо
сделать небольшое отступление и сказать пару слов о RESTful веб службе, есть описания спецификаций
стандарта JSR и несколько известных
реализации его на языке Java.
По умолчанию эталонной считается реализация Metro в сервере приложений Glassfish, но так же есть еще две
реализации от Apache это CXF и Axis2. Я буду пользоваться
библиотеками CXF,
собираю приложение посредством gradle с такой зависимостью CXF + зависимости EJB:
dependencies { providedCompile group: 'org.apache.cxf', name: 'cxf-rt-rs-client', version: '3.1.10' providedCompile 'javax:javaee-web-api:6.0' }
Напишем интерфейс RESTful веб сервиса Hello.java,
аннотацией @Path мы указываем путь куда будем размещать наш сервис, аннотация
@GET подсказывает
нам каким запросом протокола HTTP мы сможем получить
доступ к нашей операции веб сервиса(функции java) :
package com.lopanov; import javax.ws.rs.GET; import javax.ws.rs.Path; @Path(value = "/rest") public interface Hello { @GET @Path("/hello") public String getPerson(); }
И реализуем наш интефейс в классе HelloImpl:
package com.lopanov; public class HelloImpl implements Hello { @Override public String getPerson() { return "Hello"; } }
Собираем
все в файл hello-1.0.war
и помещаем его в каталог JBOSS_HOME/Standalone/deployments, как устанавливать и запускать wildfly я писал в одной их прошлых статей, после деплоя, в логах сервера wildfly появятся строчки примерно
такого вида:
18:47:27,758 INFO
[org.jboss.as.server.deployment] (MSC service thread 1-8) JBAS015876:
Starting deployment of "hello-1.0.war" (runtime-name:
"hello-1.0.war")
18:47:28,148 INFO
[org.wildfly.extension.undertow] (MSC service thread 1-1) JBAS017534:
Registered web context: /hello-1.0
18:47:28,288 INFO
[org.jboss.as.server] (DeploymentScanner-threads - 2) JBAS018559:
Deployed "hello-1.0.war" (runtime-name : "hello-1.0.war")
18:47:36,244 INFO
[org.hibernate.validator.internal.util.Version] (default task-1)
HV000001: Hibernate Validator 5.1.3.Final
И так
проверим наш сервис, загрузите свой любимый web браузер и перейдите по ссылке http://127.0.0.1:8080/hello-1.0/rest/hello вы
получите строку Hello. Url
формируется путем сложения адреса серавера (http://127.0.0.1:8080),
контекста web
приложения (/hello-1.0) и двух путей (@Path(value = "/rest") ) и (@Path("/hello")) . Для
того чтобы посмотреть что же происходит внутри протокола HTTP загрузите и установите утилиту SoapUI, создайте
REST проект, создайте GET запрос http://127.0.0.1:8080/hello-1.0/rest/hello, и выполните
его. Вы получите две RAW
вкладки
с расшифровкой протокола HTTP. Смотрите
рисунок, на вкладке 1 запрос нашей
службы, на вкладке 2 ответ от нее.
Продолжим, есть еще один способ написать rest веб службу, если вам не нравиться возиться с файлом web.xml нужно
просто расширить класс javax.ws.rs.core.Application. Плюс добавим нововведение, напишем новую службу которая будет
интегрироваться с EJB. Реализуем наш класс HelloApplication.java, аннотацией @ApplicationPath(value = "rest") укажем путь
для сервлета куда будет развертываться наше приложение:
package com.lopanov;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath(value = "rest")
public class HelloApplication extends Application{
}
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath(value = "rest")
public class HelloApplication extends Application{
}
Реализуем наш EJB с двумя методами которые будут нам возвращать типы данных xml и json.
Тип возвращаемой инфорамации указывается в аннотации @Produces({"application/json"}) для json
и @Produces({"application/xml"})для xml:
package com.lopanov;
import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Stateless
@Path("/")
public class HelloEJB {
@GET @Path("/json") @Produces({"application/json"})
public String getJsonEjb() {
return new String("{\"result\":\""+"Hello Json"+"\"}");
}
@GET @Path("/xml") @Produces({"application/xml"})
public String getXmlEjb() {
return new String("<xml><result>" +"Hello Xml"+"</result></xml>");
}
}
import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Stateless
@Path("/")
public class HelloEJB {
@GET @Path("/json") @Produces({"application/json"})
public String getJsonEjb() {
return new String("{\"result\":\""+"Hello Json"+"\"}");
}
@GET @Path("/xml") @Produces({"application/xml"})
public String getXmlEjb() {
return new String("<xml><result>" +"Hello Xml"+"</result></xml>");
}
}
Соберем наше приложение wild_hello-1.0.war, поместим его в каталог JBOSS_HOME/Standalone/deployments для деплоя приложения, как появятся строчки в логе сервера
... 14:54:40,366 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-6) JNDI bindings for session bean named HelloEJB in deployment unit deployment
"wild_hello-1.0.war" are as follows:
java:global/wild_hello-1.0/HelloEJB!com.lopanov.HelloEJB
java:app/wild_hello-1.0/HelloEJB!com.lopanov.HelloEJB
java:module/HelloEJB!com.lopanov.HelloEJB
java:global/wild_hello-1.0/HelloEJB
java:app/wild_hello-1.0/HelloEJB
java:module/HelloEJB
...
14:54:41,458 INFO [org.jboss.resteasy.spi.ResteasyDeployment] (MSC service thread 1-4) Deploying javax.ws.rs.core.Application: class com.lopanov.HelloApplication
14:54:41,474 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) JBAS017534: Registered web context: /wild_hello-1.0
Можно считать, что удачно у нас развернулись ejb и web приложение, нужно сказать пару слов о ejb бинах, RESTful сервисы могут быть описаны только в @Statless и @Singleton бинах. И так наберите путь в браузере http://127.0.0.1:8080/wild_hello-1.0/rest/xml для получения xml документа или http://127.0.0.1:8080/wild_hello-1.0/rest/json для получения json документа. Осталось только написать два простеньких клиента на java, которые будут использовать наши RESTful сервисы. Первый клиент напишем посредством класса javax.ws.rs.client.Client который входит в стандарт JAX-RS:
package com.lopanov; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Invocation; import javax.ws.rs.client.WebTarget; import javax.ws.rs.client.Client; import javax.ws.rs.core.Response; public class Main { public static void main(String[] args) {
//подготавливаем нашего клиента Client client = ClientBuilder.newBuilder().newClient(); WebTarget target = client.target("http://127.0.0.1:8080/hello-1.0/rest/hello"); Invocation.Builder builder = target.request(); Response response = builder.get();//выполняем запрос GET String s = response.readEntity(String.class);//получим результат System.out.println(response.getStatus());//выведем код возврата протокола HTTP который нам возвратился System.out.println(" s = " + s);//выведем результат работы RESTful сервиса client.close();// закрываем соединение, если этого не сделать соединение по протоколу tcp не будет закрыто
}
}
}
выполнив эту программу мы получим результат:
200 s = Hello Process finished with exit code 0
Напишем код стандартными средствами java, при помощи которого получим доступ к xml документу:
package com.lopanov;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class Main1 {
public static void main(String args[]) {
new Main1();
}
public Main1() {
try {
URL url = new URL("http://127.0.0.1:8080/wild_hello-1.0/rest/xml");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.setRequestProperty("Accept", "Application/xml");
if (connection.getResponseCode() != 200) {
throw new RuntimeException();
}
System.out.printf("%s\r\n", connection.getContentType());
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String str = br.readLine();
while (str != null) {
System.out.printf("%s\r\n", str);
str = br.readLine();
}
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class Main1 {
public static void main(String args[]) {
new Main1();
}
public Main1() {
try {
URL url = new URL("http://127.0.0.1:8080/wild_hello-1.0/rest/xml");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.setRequestProperty("Accept", "Application/xml");
if (connection.getResponseCode() != 200) {
throw new RuntimeException();
}
System.out.printf("%s\r\n", connection.getContentType());
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String str = br.readLine();
while (str != null) {
System.out.printf("%s\r\n", str);
str = br.readLine();
}
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Думаю, комментировать код не имеет смысла, здесь мы просто открываем соединение к RESTful веб сервису, получаем результат из потока чтения и выводим его
на экран:application/xmlПодведем итоги. В статье я показал вам, как можно развернуть RESTful веб сервисы двумя способами, показал каким образом вы можете выбрать тип возвращаемой информации xml или json, и в конце мы написали два простеньких клиента которые обращаются к нашим RESTful веб сервисам. В скользь я упомянул про утилиту SoapUI, которой можно пользоваться для быстрого тестирования сервисов.Process finished with exit code 0 Hello Xml