Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Дополнительные возможности клиентского API

В этом разделе описываются некоторые дополнительные функции клиентского API JAX-RS.

Здесь рассматриваются следующие темы:

Настройка запроса клиента

Дополнительные параметры конфигурации могут быть добавлены в запрос клиента после его создания, но до его вызова.

Здесь рассматриваются следующие темы:

Установка заголовков сообщений в клиентском запросе

Вы можете установить заголовки HTTP для запроса, вызвав метод Invocation.Builder.header.

Client client = ClientBuilder.newClient();
WebTarget myResource = client.target("http://example.com/webapi/read");
String response = myResource.request(MediaType.TEXT_PLAIN)
        .header("myHeader", "The header value")
        .get(String.class);

Если нужно установить несколько заголовков в запросе, вызовите метод Invocation.Builder.headers и передайте объект javax.ws.rs.core.MultivaluedMap с парами имён-значений заголовков HTTP. Вызов метода headers заменяет все существующие заголовки на заголовки, предоставленные в объекте MultivaluedMap.

Client client = ClientBuilder.newClient();
WebTarget myResource = client.target("http://example.com/webapi/read");
MultivaluedMap<String, Object> myHeaders =
    new MultivaluedMap<>("myHeader", "The header value");
myHeaders.add(...);
String response = myResource.request(MediaType.TEXT_PLAIN)
        .headers(myHeaders)
        .get(String.class);

Интерфейс MultivaluedMap позволяет вам указать несколько значений для данного ключа.

MultivaluedMap<String, Object> myHeaders =
    new MultivaluedMap<String, Object>();
List<String> values = new ArrayList<>();
values.add(...)
myHeaders.add("myHeader", values

Установка файлов cookie в запросе клиента

Вы можете добавить cookies HTTP в запрос, вызвав метод Invocation.Builder.cookie, который принимает пару имя-значение в качестве параметров.

Client client = ClientBuilder.newClient();
WebTarget myResource = client.target("http://example.com/webapi/read");
String response = myResource.request(MediaType.TEXT_PLAIN)
        .cookie("myCookie", "The cookie value")
        .get(String.class);

Класс javax.ws.rs.core.Cookie инкапсулирует атрибуты cookies HTTP, такие как в имя, значение, путь, домен и версию cookies в соответствии со спецификацией RFC. В следующем примере объект Cookie настроен парой имя-значение, путём и доменом.

Client client = ClientBuilder.newClient();
WebTarget myResource = client.target("http://example.com/webapi/read");
Cookie myCookie = new Cookie("myCookie", "The cookie value",
    "/webapi/read", "example.com");
String response = myResource.request(MediaType.TEXT_PLAIN)
        .cookie(myCookie)
        .get(String.class);

Добавление фильтров к клиенту

Пользовательские фильтры можно зарегистрировать с помощью клиентского запроса или ответа, полученного от целевого ресурса. Чтобы зарегистрировать классы фильтров при создании объекта Client, вызовите метод Client.register.

Client client = ClientBuilder.newClient().register(MyLoggingFilter.class);

В предыдущем примере для всех вызовов, которые используют этот объект Client, зарегистрирован фильтр MyLoggingFilter.

Вы также можете зарегистрировать классы фильтров на цели, вызвав WebTarget.register.

Client client = ClientBuilder.newClient().register(MyLoggingFilter.class);
WebTarget target = client.target("http://example.com/webapi/secure")
        .register(MyAuthenticationFilter.class);

В предыдущем примере фильтры MyLoggingFilter и MyAuthenticationFilter присоединяются к вызову.

Классы фильтра запросов и ответов реализуют интерфейсы javax.ws.rs.client.ClientRequestFilter и javax.ws.rs.client.ClientResponseFilter соответственно. Оба этих интерфейса определяют метод filter. Все фильтры должны быть помечены javax.ws.rs.ext.Provider.

Следующий класс представляет собой фильтр журнала для клиентских запросов и ответов.

@Provider
public class MyLoggingFilter implements ClientRequestFilter,
        ClientResponseFilter {
    static final Logger logger = Logger.getLogger(...);

    // реализация метода ClientRequestFilter.filter
    @Override
    public void filter(ClientRequestContext requestContext)
            throws IOException {
        logger.log(...);
        ...
    }

    // реализация метода ClientResponseFilter.filter
    @Override
    public void filter(ClientRequestContext requestContext,
           ClientResponseContext responseContext) throws IOException {
        logger.log(...);
        ...
    }
}

Если требуется остановить выполнение вызова, при активном фильтре вызовите метод abortWith объекта контекста и с объектом javax.ws.rs.core.Response в качестве параметра.

@Override
public void filter(ClientRequestContext requestContext) throws IOException {
    ...
    Response response = new Response();
    response.status(500);
    requestContext.abortWith(response);
}

Асинхронные вызовы в клиентском API

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

В клиентском API JAX-RS метод Invocation.Builder.async используется при создании запроса клиента, чтобы указать, что вызов сервиса должен выполняться асинхронно. Асинхронный вызов немедленно возвращает управление вызывающей стороне вместе с объектом типа java.util.concurrent.Future<T> (часть API параллелизма Java SE) и с типом, установленным на возвращаемый тип вызова сервиса. Объекты Future<T> имеют методы для проверки завершения асинхронного вызова, получения окончательного результата, отмены вызова и проверки отмены вызова.

В следующем примере показано, как вызвать асинхронный запрос к ресурсу.

Client client = ClientBuilder.newClient();
WebTarget myResource = client.target("http://example.com/webapi/read");
Future<String> response = myResource.request(MediaType.TEXT_PLAIN)
        .async()
        .get(String.class);

Использование кастомных Callback-вызовов в асинхронных вызовах

Интерфейс InvocationCallback определяет два метода: complete и failed, которые вызываются, когда асинхронный вызов завершается соответственно либо успешно, либо неудачей. Объект InvocationCallback может быть зарегистрирован при формировании запроса.

В следующем примере показано, как зарегистрировать объект Callback-вызова при асинхронном вызове.

Client client = ClientBuilder.newClient();
WebTarget myResource = client.target("http://example.com/webapi/read");
Future<Customer> fCustomer = myResource.request(MediaType.TEXT_PLAIN)
        .async()
        .get(new InvocationCallback<Customer>() {
            @Override
            public void completed(Customer customer) {
            // Выполнение действия с объектом заказчика
            }
            @Override
             public void failed(Throwable throwable) {
            // Обработка ошибки
            }
    });

Использование реактивного подхода в асинхронных вызовах

Использование кастомных Callback-вызовов в асинхронных вызовах легко в простых случаях и в случаях множества независимых вызовов. В случае вложенных вызовов использование кастомных Callback-вызовов становится очень трудным для реализации, отладки и обслуживания.

JAX-RS определяет новый тип инициатора запроса — RxInvoker — и реализация этого типа по умолчанию CompletionStageRxInvoker. Новый метод rx используется как в следующем примере:

CompletionStage<String> csf = client.target("forecast/{destination}") resolveTemplate("destination", "mars").request().rx().get(String.class);
csf.thenAccept(System.out::println);

В этом примере создаётся асинхронная обработка интерфейса CompletionStage<String>, которая ожидает его завершения и отображения результата. Возвращённый CompletionStage может затем использоваться только для получения результата, как показано в приведённом выше примере, или может быть объединён с другими этапами завершения для упрощения и улучшения обработки асинхронных задач.

Использование серверных событий

Технология серверных событий (Server-sent Events — SSE) используется для асинхронной отправки уведомлений клиенту по стандартным протоколам HTTP или HTTPS. Клиенты могут подписаться на уведомления о событиях, которые отправляются на сервер. Сервер генерирует события и отправляет уведомления об этих событиях клиентам, которые подписаны на получение уведомлений. Соединение с односторонним каналом связи устанавливается клиентом. Как только соединение установлено, сервер отправляет уведомления клиенту всякий раз, когда доступны новые данные.

Канал связи, установленный клиентом, действует до тех пор, пока клиент не закроет соединение. Он же используется сервером для отправки уведомлений о событиях.

Обзор SSE API

SSE API определён в пакете javax.ws.rs.sse и включает интерфейсы SseEventSink, SseEvent, Sse и SseEventSource. Чтобы принимать подключения и отправлять уведомления одному или нескольким клиентам, вставьте SseEventSink в метод ресурса, который создаёт ответы типа MIME text/event-stream.

В следующем примере показано, как принимать подключения SSE и отправлять уведомления клиентам:

@GET
@Path("eventStream")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void eventStream(@Context SseEventSink eventSink,
@Context Sse sse) {
  executor.execute(() -> {
    try (SseEventSink sink = eventSink) {
      eventSink.send(sse.newEvent("event1"));
      eventSink.send(sse.newEvent("event2"));
      eventSink.send(sse.newEvent("event3"));
    }
  });
}

SseEventSink инъецируется в метод ресурса, а основное клиентское соединение остается открытым и используется для отправки уведомлений. Соединение сохраняется до тех пор, пока клиент не отключится от сервера. Метод send возвращает объект CompletionStage<T>, который указывает, что действие асинхронной отправки сообщения клиенту включено.

Уведомления, передающиеся клиентам, могут содержать такие подробности, как event, data, id, retry и comment.

Широковещательная рассылка с использованием SSE

Широковещательная рассылка — это действие по отправке уведомления одновременно нескольким клиентам. JAX-RS SSE API предоставляет SseBroadcaster для регистрации всех объектов SseEventSink и отправки уведомлений всем зарегистрированным клиентам. Жизненный цикл и область видимости SseBroadcaster полностью контролируются приложениями, а не средой выполнения JAX-RS. В следующем примере показано использование широковещательной рассылки:

@Path("/")
@Singleton
public class SseResource {
  @Context
  private Sse sse;
  private volatile SseBroadcaster sseBroadcaster;
  @PostConstruct
  public init() {
    this.sseBroadcaster = sse.newBroadcaster();
  }

  @GET
  @Path("register")
  @Produces(MediaType.SERVER_SENT_EVENTS)
  public void register(@Context SseEventSink eventSink) {
    eventSink.send(sse.newEvent("welcome!"));
    sseBroadcaster.register(eventSink);
  }

  @POST
  @Path("broadcast")
  @Consumes(MediaType.MULTIPART_FORM_DATA)
  public void broadcast(@FormParam("event") String event) {
    sseBroadcaster.broadcast(sse.newEvent(event));
  }
}

Аннотация @Singleton определена для класса ресурса и делает невозможным создание нескольких объектов класса. Метод register используется для добавления нового SseEventSink. Метод broadcast используется для отправки уведомления SSE всем зарегистрированным клиентам.

Ожидание получение уведомлений о серверных событиях

JAX-RS SSE предоставляет клиенту интерфейс SseEventSource для подписки на уведомления. Клиент может получать асинхронные уведомления о входящих событиях, вызывая один из методов subscribe в javax.ws.rs.sse.SseEventSource.

В следующем примере показано, как использовать API SseEventSource для открытия соединения SSE и чтения сообщений за период:

WebTarget target = client.target("http://...");
try (SseEventSource source = SseEventSource.target(target).build()) {
  source.register(System.out::println);
  source.open();
  Thread.sleep(500); // Получение сообщений только за 500 мс
  source.close();
} catch (InterruptedException e) {
  // обработка ошибки
}

Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)