Статьи

Автономное Java-приложение с Джерси и Джетти

Вступление

Я создал  небольшой пример  запуска отдельного Java-приложения, которое обслуживает статический HTML, JavaScript, CSS-контент, а также публикует веб-сервис REST. В примере используются Джерси и Причал. Этот пример, вероятно, заслуживает нескольких публикаций, чтобы дать достаточно времени, чтобы объяснить, как эти части сочетаются друг с другом, поэтому я начну с основных частей Java, которые составляют приложение JAX-RS.

Пару лет назад, когда я работал над некоторыми слайдами, чтобы преподавать класс Java (в основном Java EE и Spring), я создал веб-сервис REST с использованием Spring WebMVC. Я написал несколько постов (начиная  здесь ) о том, как это работает.

Недавно я снова преподавал этот класс, обновляя до более поздних версий Java и более поздних версий библиотек. Когда я добрался до класса Spring WebMVC, я хотел научить его, потому что я все еще думаю, что это хороший выбор для приложения, использующего среду Spring (потому что оно интегрируется с внедрением зависимостей Spring). Но, конечно, теперь у нас есть Java API для RESTful Web Services (JAX-RS) в качестве опции.

Поэтому я адаптировал предыдущий пример к JAX-RS. К счастью, некоторые из маленьких хитростей, используемых с приложением Spring WebMVC, все еще применяются.

Класс провайдера

JAX-RS делит работу веб-службы REST на один или несколько классов провайдеров. Каждый класс провайдера обрабатывает набор путей во всем приложении, и каждый класс провайдера может иметь несколько методов, которые обрабатывают определенные пути и методы HTTP.

Классы провайдеров — это простые старые объекты Java (POJO), использующие аннотации для указания параметров JAX-RS. Вот класс провайдера для этого приложения.

@Path("/calculator")
 public class Calculator {

      @GET
      @Path("/calc/{op}/{left}/{right}")
      public Calculation calculate(@PathParam("op") String op, @PathParam("left") Integer left,
              @PathParam("right") Integer right) {
          Calculation result = new Calculation();
          result.setOperation(op);
         result.setLeft(left);
         result.setRight(right);
         return doCalc(result);
     }

     @POST
     @Path("/calc2")
     public Calculation calculate(Calculation calc) {
         return doCalc(calc);
     }

     private Calculation doCalc(Calculation c) {
         String op = c.getOperation();
         int left = c.getLeft();
         int right = c.getRight();
         if (op.equalsIgnoreCase("subtract")) {
             c.setResult(left - right);
         } else if (op.equalsIgnoreCase("multiply")) {
             c.setResult(left * right);
         } else if (op.equalsIgnoreCase("divide")) {
             c.setResult(left / right);
         } else {
             c.setResult(left + right);
         }
         return c;
     }

 }

В этом примере проиллюстрированы  методы как HTTP, так  GET и  POSTHTTP, а также передача параметров либо через компоненты URL, либо через тело запроса.

Ключевые аннотации, используемые выше:

  • @Path: Добавляет компонент пути для сопоставления входящего URL. Компоненты пути являются кумулятивными, поэтому аннотация класса и аннотация метода работают вместе. Путь может содержать шаблоны, используемые для предоставления параметров.
  • @GET: Указывает метод, который обрабатывает запросы HTTP GET.
  • @POST: Указывает метод, который обрабатывает запросы HTTP POST.
  • @PathParam: Связывает шаблон в URL с параметром метода.

В этом примере не показано  @QueryParam, что работает аналогично, @PathParam но вместо этого соответствует вводу формы (либо закодированному в URL-адресе в виде  ?name1=value1&name2=value2 пар, либо в  name=value списке с переносами строк в теле запроса).

Для тех, кто знаком с Spring WebMVC, обратите внимание, что аннотации довольно похожи. Также обратите внимание, что некоторые аннотации, такие как  @RequestBody и @ResponseBody для указания того, что тело запроса должно быть преобразовано в параметр, или возвращенный объект Java должен стать телом ответа, предполагаются, а не указываются.

Приложение JAX-RS

Класс приложения JAX-RS позволяет, помимо прочего, настраивать, какие пакеты проверяются на наличие поставщиков.

 public class CalculatorApp extends ResourceConfig {

     public CalculatorApp() {
         packages("org.anvard.jaxrs");
     }
}

ResourceConfig класс Джерси, который выходит за пределы стандартного Application класса JAX-RS и обеспечивает сканирование пакетов и другие вспомогательные приложения. Это также обеспечивает реализацию стандарта  getClasses()и  getSingletons() методов, которые мы должны были бы предоставить сами.

web.xml

Хотя Servlet 3.0 позволяет развертывать приложения без использования дескриптора развертывания, он облегчает соединение Jetty с Джерси при использовании плагина Maven Jetty.

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="3.0" 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">

    <servlet>
        <servlet-name>Calculator</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>org.anvard.jaxrs.server.CalculatorApp</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Calculator</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

Эта конфигурация использует сервлет, предоставленный Джерси, для делегирования классу приложения JAX-RS, который мы определили ранее. Это также позволяет нам указать первый компонент пути для служб JAX-RS (чтобы они отличались от статических файлов, которые мы также хотим обслуживать).

Развертывание и запуск

Файл Maven POM обрабатывает создание JAR (для автономного приложения, обсуждаемого в следующем посте) и WAR (для развертывания в контейнере сервлетов). Он также включает в себя плагин Maven Jetty, позволяющий запускать приложение из командной строки с помощью  mvn jetty:run.

Требования к клиенту

Пример включает в себя как JavaScript, так и Java-клиенты, о которых я расскажу в другом посте. Разумеется, можно использовать любой веб-клиент, но обратите внимание, что эта служба REST тщательно реагирует на запросы клиентов.

Мы перечисляем зависимость от плагина Джерси для Джексона, чтобы мы могли перемещаться между Java и JSON. Однако клиент должен указать заголовок  Accept: application/json; в противном случае сервер по умолчанию использует XML. Кроме того, при подаче данных на сервер для запроса POST клиент также должен указать Content-Type: application/json заголовок.

Следующие шаги

В следующем посте будет подробно рассказано о запуске службы в обычном приложении Java с использованием встроенного сервера Jetty.