Настройка приложения OSGI Enroute REST для разрешения асинхронных запросов

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

Некоторый код

@RequireBootstrapWebResource(resource="css/bootstrap.css")
@RequireWebServerExtender
@RequireHttpImplementation
@RequireConfigurerExtender
@Component(name="my.web", property={"debug=true"})
public final class MyApplication implements REST
{
   public void getBatching(RESTRequest request)
   {
      User user = authenticate(request);

      HttpServletResponse response = request._response();
      response.setStatus(HttpServletResponse.SC_OK);
      response.setContentType("text/event-stream");
      response.setCharacterEncoding(StandardCharsets.UTF_8.name());
      try
      {
         AsyncContext async = (AsyncContext)request._request().startAsync();
         async.addListener(new InternalAsyncListener());
         sseConnectionMap.put(UUID.randomUUID(), async);

         response.flushBuffer();
      }
      catch (Exception e)
      {
         System.out.println("Message=" + e.getMessage());
         e.printStackTrace();
      }
   }

   @SuppressWarnings("unused")
   @Activate
   void activate(ComponentContext cc, BundleContext bc, Map<String, Object> config)
   {
      System.out.println(this.getClass() + " started");

      if ("true".equals(config.get("debug")))
      {
         debug = true;
      }

      System.out.println("Link Start");
   }
}

В настоящее время это печатает следующее:

Message=null
java.lang.IllegalStateException
at org.apache.felix.http.base.internal.dispatch.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:314)
at my.application.MyApplication.getBatching(WebApplication.java:399)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at osgi.enroute.rest.simple.provider.Function.invoke(Function.java:203)
at osgi.enroute.rest.simple.provider.RestMapper.execute(RestMapper.java:333)
at osgi.enroute.rest.simple.provider.RestServlet.service(RestServlet.java:66)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.felix.http.base.internal.handler.ServletHandler.handle(ServletHandler.java:85)
at org.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:79)
at my.application.CORSFilter.doFilter(CORSFilter.java:75)
at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:135)
at org.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:74)
at org.apache.felix.http.base.internal.dispatch.Dispatcher.dispatch(Dispatcher.java:124)
at org.apache.felix.http.base.internal.DispatcherServlet.service(DispatcherServlet.java:61)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:583)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:224)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1160)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1092)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
at org.eclipse.jetty.server.Server.handle(Server.java:518)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:308)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:244)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceAndRun(ExecuteProduceConsume.java:246)
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:156)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
at java.lang.Thread.run(Unknown Source)

куда authenticate() в настоящее время изменяет заголовки в режиме отладки, чтобы включить CORS при разработке приложения, и пытается получить User от токена, переданного в Authorization заголовок... однако даже если я поставлю startAsync() линия выше, что я все еще получаю то же самое IllegalStateException так что я не думаю, что это проблема модификации заголовка.

Некоторое прибегая к помощи поднял, что ISE на этой конкретной линии ServletRequestWrapper.java:314 указывает, что базовый сервер Felix/Jetty не настроен на разрешение асинхронных запросов.

Я также попытался просто установить заголовок text/event-stream и продолжать писать в ответ, но, конечно, он был закрыт при выходе из метода REST.

Дальнейшее исследование предоставило другие решения, в том числе запуск второго сервлета, который настроен для обработки асинхронных запросов и привязки его к отдельному шаблону URL. Это, конечно, собственное усилие, и я не уверен, насколько хорошо несколько сервлетов будут играть вместе. Желательно, чтобы я мог просто открывать асинхронные ответы из одного приложения Enroute.

Если есть возможность настроить приложение enroute для разрешения асинхронных ответов, какой параметр я пропускаю?

1 ответ

Решение

Вздох... нашел ответ, испытывая трудности с получением @WebServlet чтобы начать и продолжал возвращаться к доске. Поиском "whiteboard async Java" стало HttpWhiteboardConstants установка: HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED:

@Component(..., property={...,
     HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED+"=true", ...}...)

Это, однако, не работает на @Component реализации REST вместо этого я смог заставить его работать только в отдельном компоненте следующим образом:

@RequireHttpImplementation
@Component(name="sse", property={
    HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN+"=/sse/*",
    HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED+"=true",
    HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED+"=true"},
    service=Servlet.class)
public final class ServerSideEventServlet extends HttpServlet
{
   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response) 
       throws ServletException, IOException
   {
   }
}

и это Servlet запускается параллельно с моим вопросом. Когда я попытался перенести эту конфигурацию на REST сервер, я все еще получил то же исключение. Это может быть потому, что request затрагивается вне метода REST, что имеет смысл.

Другие вопросы по тегам