Ejemplo n.º 1
1
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    resp.getWriter().write(super.getServletName());

    AsyncContext asyncContext = req.startAsync();
    asyncContext.setTimeout(0);

    /*
     * O metodo createListener() cria um listener da classe informada que
     * deve extender AsyncListener. Este metodo adiciona suporte para a
     * notacoes e injecao de dependencias dentro do listener criado, deste
     * modo, objetos sao injetados dentro desse listener depois que ele é
     * criado.
     */
    MyAsyncListener myAsyncListener = asyncContext.createListener(MyAsyncListener.class);

    /*
     * O metodo addListener(AsyncListener) registra o listener informado ao
     * AsyncContext em questao.
     */
    asyncContext.addListener(myAsyncListener);

    /*
     * O metodo addListener() é sobrecarregado para receber um
     * ServletRequest e um ServletResponse, que podem ser recuperados mais
     * tarde dentro do listener atraves dos metodos getSuppliedRequest() e
     * getSuppliedResponse() de AsyncEvent.
     */
    // asyncContext.addListener(myAsyncListener, null, null);

    AsyncTask task = new AsyncTask(asyncContext);
    asyncContext.start(task);
  }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
      resp.getWriter().write("DispatchingServletGet-");
      resp.flushBuffer();

      final boolean first = TrackingServlet.first;
      TrackingServlet.first = false;

      final AsyncContext ctxt = req.startAsync();
      TrackingListener listener = new TrackingListener(false, true, null);
      ctxt.addListener(listener);
      ctxt.setTimeout(3000);

      Runnable run =
          new Runnable() {
            @Override
            public void run() {
              if (first) {
                ctxt.dispatch("/stage1");
              } else {
                ctxt.dispatch("/stage2");
              }
            }
          };
      if ("y".equals(req.getParameter("useThread"))) {
        new Thread(run).start();
      } else {
        run.run();
      }
    }
 SampleAsycListener(HttpServletRequest request, HttpServletResponse response)
     throws IOException {
   asyncContext = request.startAsync();
   asyncContext.setTimeout(10000L);
   asyncContext.addListener(this);
   servletOutputStream = response.getOutputStream();
   servletOutputStream.setWriteListener(this);
   this.response = response;
 }
Ejemplo n.º 4
0
  /* ------------------------------------------------------------ */
  @Override
  public void suspend() {
    _resumed = false;
    _expired = false;
    _context = _request.startAsync();
    _context.setTimeout(_timeoutMs);

    for (AsyncListener listener : _listeners) _context.addListener(listener);
    _listeners.clear();
  }
    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
        throws ServletException, IOException {

      AsyncContext actxt = req.startAsync();
      actxt.setTimeout(3000);
      resp.setContentType("text/plain");
      resp.getWriter().print("OK");
      actxt.complete();
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
      if (req.isAsyncSupported()) {
        resp.getWriter().print("TimeoutServletGet-");
        final AsyncContext ac = req.startAsync();
        ac.setTimeout(3000);

        ac.addListener(new TrackingListener(false, completeOnTimeout, dispatchUrl));
      } else resp.getWriter().print("FAIL: Async unsupported");
    }
Ejemplo n.º 7
0
  /* ------------------------------------------------------------ */
  @Override
  public void suspend(ServletResponse response) {
    _response = response;
    _responseWrapped = response instanceof ServletResponseWrapper;
    _resumed = false;
    _expired = false;
    _context = _request.startAsync();
    _context.setTimeout(_timeoutMs);

    for (AsyncListener listener : _listeners) _context.addListener(listener);
    _listeners.clear();
  }
Ejemplo n.º 8
0
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    response.setContentType("text/event-stream");
    response.setCharacterEncoding("UTF-8");

    AsyncContext ac = request.startAsync();
    ac.setTimeout(300000);
    ac.addListener(
        new AsyncListener() {
          @Override
          public void onTimeout(AsyncEvent ae) throws IOException {
            activeClients.remove(ae.getAsyncContext());
            publishToActiveClients(
                ae.getAsyncContext().getRequest().getRemoteHost()
                    + ":"
                    + ae.getAsyncContext().getRequest().getRemotePort()
                    + " has timedout...");
          }

          @Override
          public void onError(AsyncEvent ae) throws IOException {
            activeClients.remove(ae.getAsyncContext());
            publishToActiveClients(
                ae.getAsyncContext().getRequest().getRemoteHost()
                    + ":"
                    + ae.getAsyncContext().getRequest().getRemotePort()
                    + " has errors...");
          }

          @Override
          public void onComplete(AsyncEvent ae) throws IOException {
            activeClients.remove(ae.getAsyncContext());
            publishToActiveClients(
                ae.getAsyncContext().getRequest().getRemoteHost()
                    + ":"
                    + ae.getAsyncContext().getRequest().getRemotePort()
                    + " completed...");
          }

          @Override
          public void onStartAsync(AsyncEvent ae) throws IOException {
            publishToActiveClients(
                ae.getAsyncContext().getRequest().getRemoteHost()
                    + ":"
                    + ae.getAsyncContext().getRequest().getRemotePort()
                    + " started...");
          }
        });
    activeClients.add(ac);
    ac.getResponse().getWriter().flush();
    publishToActiveClients(getRequestDescription(request) + " joined...");
  }
Ejemplo n.º 9
0
  /**
   * Caso o response ja esteja fechado ou o metodo startAsync() ja tenha sido chamado, uma nova
   * invocacao resultara em uma IllegalStateException.
   */
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    PrintWriter writer = resp.getWriter();

    writer.println("Antes de iniciar a tarefa assincrona");

    /*
     * Para iniciar uma requisicao assincrona, basta chamar o metodo
     * startAsync de ServletRequest. O metodo startAsync é sobrecarregado
     * para receber um ServletRequest e um ServletResponse, desta forma é
     * possivel passar wrappers para ele. Caso o metodo startAsync seja
     * chamado, os objetos ServletRequest e Response originais serao
     * utilizados.
     */
    AsyncContext asyncContext = req.startAsync();

    MyAsyncTask asyncTask = new MyAsyncTask(asyncContext, writer);

    /*
     * O tempo padrao de time out depende do container, geralmente está
     * entre 5 e 10 segundos. Para desabilitar o time out basta informar um
     * valor negativo ou 0. Esse metodo pode ser chamado antes ou depois do
     * metodo start().
     */
    asyncContext.setTimeout(-1);

    /*
     * A Interface AsyncContext possui metodos para configurar a tarefa
     * assincrona e inicializar a mesma. O metodo estart recebe um runnable
     * que sera responsavel por realizar o processamento da tarefa
     * assincrona. Quando esse metodo é chamado, o processamento é realizado
     * por outra Thread do pool de Threads do servidor.
     */
    asyncContext.start(asyncTask);

    /*
     * Depois do start() da tarefa assincrona, o fluxo continua normalmente,
     * e quando o metodo service() é encerrado, a resposta nao é commitada
     * enquanto o metodo complete() de AsyncContext não for chamado. Desta
     * forma, o container processa a tarefa assincrona um background em
     * outra Thread.
     */
    writer.println("Depois de iniciar a tarefa assincrona");

    /*
     * Retorna o AsyncContext inicializado para esse request. Caso o metodo
     * startAsync() ainda nao tenha sido chamado, uma IllegalStateException
     * ocorrera.
     */
    req.getAsyncContext();
  }
Ejemplo n.º 10
0
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
   AsyncContext asyncContext = (AsyncContext) request.getAttribute(AsyncContext.class.getName());
   if (asyncContext == null) {
     AsyncContext context = request.startAsync();
     context.setTimeout(0);
     request.setAttribute(AsyncContext.class.getName(), context);
     context.addListener(this);
   } else {
     throw new ServletException();
   }
 }
Ejemplo n.º 11
0
 synchronized void processPendingResponses(HttpServletRequest req, HttpServletResponse resp)
     throws IOException {
   JSONArray responses = new JSONArray();
   JSONStreamAware pendingResponse;
   while ((pendingResponse = pendingResponses.poll()) != null) {
     responses.add(pendingResponse);
   }
   if (responses.size() > 0) {
     JSONObject combinedResponse = new JSONObject();
     combinedResponse.put("responses", responses);
     if (asyncContext != null) {
       asyncContext.getResponse().setContentType("text/plain; charset=UTF-8");
       try (Writer writer = asyncContext.getResponse().getWriter()) {
         combinedResponse.writeJSONString(writer);
       }
       asyncContext.complete();
       asyncContext = req.startAsync();
       asyncContext.addListener(new UserAsyncListener());
       asyncContext.setTimeout(5000);
     } else {
       resp.setContentType("text/plain; charset=UTF-8");
       try (Writer writer = resp.getWriter()) {
         combinedResponse.writeJSONString(writer);
       }
     }
   } else {
     if (asyncContext != null) {
       asyncContext.getResponse().setContentType("text/plain; charset=UTF-8");
       try (Writer writer = asyncContext.getResponse().getWriter()) {
         JSON.emptyJSON.writeJSONString(writer);
       }
       asyncContext.complete();
     }
     asyncContext = req.startAsync();
     asyncContext.addListener(new UserAsyncListener());
     asyncContext.setTimeout(5000);
   }
 }
    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
        throws ServletException, IOException {

      String echo = req.getParameter("echo");
      AsyncContext actxt = req.startAsync();
      resp.setContentType("text/plain");
      resp.getWriter().print("OK");
      if (echo != null) {
        resp.getWriter().print("-" + echo);
      }
      // Speed up the test by reducing the timeout
      actxt.setTimeout(1000);
    }
Ejemplo n.º 13
0
  private synchronized void poll(
      HttpServletRequest request, HttpServletResponse response, String username)
      throws IOException {
    Map<String, Member> room = _rooms.get(request.getPathInfo());
    if (room == null) {
      response.sendError(503);
      return;
    }
    final Member member = room.get(username);
    if (member == null) {
      response.sendError(503);
      return;
    }

    synchronized (member) {
      if (member._queue.size() > 0) {
        // Send one chat message
        response.setContentType("text/json;charset=utf-8");
        StringBuilder buf = new StringBuilder();

        buf.append("{\"action\":\"poll\",");
        buf.append("\"from\":\"");
        buf.append(member._queue.poll());
        buf.append("\",");

        String message = member._queue.poll();
        int quote = message.indexOf('"');
        while (quote >= 0) {
          message = message.substring(0, quote) + '\\' + message.substring(quote);
          quote = message.indexOf('"', quote + 2);
        }
        buf.append("\"chat\":\"");
        buf.append(message);
        buf.append("\"}");
        byte[] bytes = buf.toString().getBytes("utf-8");
        response.setContentLength(bytes.length);
        response.getOutputStream().write(bytes);
      } else {
        AsyncContext async = request.startAsync();
        async.setTimeout(10000);
        async.addListener(member);
        if (!member._async.compareAndSet(null, async)) throw new IllegalStateException();
      }
    }
  }
Ejemplo n.º 14
0
  /* (non-Javadoc)
   * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    resp.setContentType("text/html;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    String str1 = "进入Servlet的时间:" + new Date() + ".";
    out.write(str1);
    System.out.println(str1);
    out.flush();

    // 在子线程中执行业务调用,并由其负责输出响应,主线程退出
    final AsyncContext ctx = req.startAsync();
    ctx.setTimeout(200000);
    new Work(ctx).start();
    ctx.addListener(new AsynServletListener());
    String str2 = "结束Servlet的时间:" + new Date() + ".";
    out.println(str2);
    out.flush();
    System.out.println(str2);
  }
Ejemplo n.º 15
0
  @RequestMapping("/log")
  @NeedLogin
  public void log(HttpServletRequest request, HttpServletResponse response) {

    /*String filename = request.getParameter("filename");
    File file = new File(filename);
    if(!file.exists()){
        return "file.not.exist";
    }*/

    final AsyncContext asyncContext = request.startAsync();
    asyncContext.setTimeout(-1);

    Thread thread =
        new Thread() {

          boolean flag = true;

          public void run() {
            while (flag) {
              try {
                Thread.sleep(1000);
                asyncContext
                    .getResponse()
                    .getWriter()
                    .println("currentTIme == " + System.currentTimeMillis());
              } catch (Exception e) {
                e.printStackTrace();
                flag = false;
              }
            }
          }
        };

    thread.start();
  }
Ejemplo n.º 16
0
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    boolean accepted = false;
    try {
      Boolean suspended = (Boolean) request.getAttribute(_suspended);
      if (suspended == null) {
        accepted = _passes.tryAcquire(getWaitMs(), TimeUnit.MILLISECONDS);
        if (accepted) {
          request.setAttribute(_suspended, Boolean.FALSE);
          if (LOG.isDebugEnabled()) LOG.debug("Accepted {}", request);
        } else {
          request.setAttribute(_suspended, Boolean.TRUE);
          int priority = getPriority(request);
          AsyncContext asyncContext = request.startAsync();
          long suspendMs = getSuspendMs();
          if (suspendMs > 0) asyncContext.setTimeout(suspendMs);
          asyncContext.addListener(_listeners[priority]);
          _queues[priority].add(asyncContext);
          if (LOG.isDebugEnabled()) LOG.debug("Suspended {}", request);
          return;
        }
      } else {
        if (suspended) {
          request.setAttribute(_suspended, Boolean.FALSE);
          Boolean resumed = (Boolean) request.getAttribute(_resumed);
          if (resumed == Boolean.TRUE) {
            _passes.acquire();
            accepted = true;
            if (LOG.isDebugEnabled()) LOG.debug("Resumed {}", request);
          } else {
            // Timeout! try 1 more time.
            accepted = _passes.tryAcquire(getWaitMs(), TimeUnit.MILLISECONDS);
            if (LOG.isDebugEnabled()) LOG.debug("Timeout {}", request);
          }
        } else {
          // Pass through resume of previously accepted request.
          _passes.acquire();
          accepted = true;
          if (LOG.isDebugEnabled()) LOG.debug("Passthrough {}", request);
        }
      }

      if (accepted) {
        chain.doFilter(request, response);
      } else {
        if (LOG.isDebugEnabled()) LOG.debug("Rejected {}", request);
        ((HttpServletResponse) response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
      }
    } catch (InterruptedException e) {
      ((HttpServletResponse) response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
    } finally {
      if (accepted) {
        for (int p = _queues.length - 1; p >= 0; --p) {
          AsyncContext asyncContext = _queues[p].poll();
          if (asyncContext != null) {
            ServletRequest candidate = asyncContext.getRequest();
            Boolean suspended = (Boolean) candidate.getAttribute(_suspended);
            if (suspended == Boolean.TRUE) {
              candidate.setAttribute(_resumed, Boolean.TRUE);
              asyncContext.dispatch();
              break;
            }
          }
        }
        _passes.release();
      }
    }
  }
Ejemplo n.º 17
0
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    final boolean isSmile = QueryResource.APPLICATION_SMILE.equals(request.getContentType());
    final ObjectMapper objectMapper = isSmile ? smileMapper : jsonMapper;

    String host = hostFinder.getDefaultHost();
    Query inputQuery = null;
    boolean hasContent = request.getContentLength() > 0 || request.getContentType() != null;
    boolean isQuery = request.getMethod().equals(HttpMethod.POST.asString());
    long startTime = System.currentTimeMillis();

    // queries only exist for POST
    if (isQuery) {
      try {
        inputQuery = objectMapper.readValue(request.getInputStream(), Query.class);
        if (inputQuery != null) {
          host = hostFinder.getHost(inputQuery);
          if (inputQuery.getId() == null) {
            inputQuery = inputQuery.withId(UUID.randomUUID().toString());
          }
        }
      } catch (IOException e) {
        log.warn(e, "Exception parsing query");
        final String errorMessage = e.getMessage() == null ? "no error message" : e.getMessage();
        requestLogger.log(
            new RequestLogLine(
                new DateTime(),
                request.getRemoteAddr(),
                null,
                new QueryStats(
                    ImmutableMap.<String, Object>of("success", false, "exception", errorMessage))));
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.setContentType(QueryResource.APPLICATION_JSON);
        objectMapper.writeValue(response.getOutputStream(), ImmutableMap.of("error", errorMessage));

        return;
      } catch (Exception e) {
        handleException(response, objectMapper, e);
        return;
      }
    }

    URI rewrittenURI = rewriteURI(host, request);
    if (rewrittenURI == null) {
      onRewriteFailed(request, response);
      return;
    }

    final Request proxyRequest =
        getHttpClient()
            .newRequest(rewrittenURI)
            .method(request.getMethod())
            .version(HttpVersion.fromString(request.getProtocol()));

    // Copy headers
    for (Enumeration<String> headerNames = request.getHeaderNames();
        headerNames.hasMoreElements(); ) {
      String headerName = headerNames.nextElement();

      if (HttpHeader.TRANSFER_ENCODING.is(headerName)) {
        hasContent = true;
      }

      for (Enumeration<String> headerValues = request.getHeaders(headerName);
          headerValues.hasMoreElements(); ) {
        String headerValue = headerValues.nextElement();
        if (headerValue != null) {
          proxyRequest.header(headerName, headerValue);
        }
      }
    }

    // Add proxy headers
    addViaHeader(proxyRequest);

    addXForwardedHeaders(proxyRequest, request);

    final AsyncContext asyncContext = request.startAsync();
    // We do not timeout the continuation, but the proxy request
    asyncContext.setTimeout(0);
    proxyRequest.timeout(getTimeout(), TimeUnit.MILLISECONDS);

    if (hasContent) {
      if (inputQuery != null) {
        proxyRequest.content(new BytesContentProvider(jsonMapper.writeValueAsBytes(inputQuery)));
      } else {
        proxyRequest.content(proxyRequestContent(proxyRequest, request));
      }
    }

    customizeProxyRequest(proxyRequest, request);

    if (isQuery) {
      proxyRequest.send(
          newMetricsEmittingProxyResponseListener(request, response, inputQuery, startTime));
    } else {
      proxyRequest.send(newProxyResponseListener(request, response));
    }
  }
Ejemplo n.º 18
0
 /* ------------------------------------------------------------ */
 @Override
 public void setTimeout(long timeoutMs) {
   _timeoutMs = timeoutMs;
   if (_context != null) _context.setTimeout(timeoutMs);
 }
Ejemplo n.º 19
0
  protected void doFilter(
      HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws IOException, ServletException {
    if (!isEnabled()) {
      filterChain.doFilter(request, response);
      return;
    }

    // Look for the rate tracker for this request.
    RateTracker tracker = (RateTracker) request.getAttribute(__TRACKER);
    if (tracker == null) {
      // This is the first time we have seen this request.
      if (LOG.isDebugEnabled()) LOG.debug("Filtering {}", request);

      // Get a rate tracker associated with this request, and record one hit.
      tracker = getRateTracker(request);

      // Calculate the rate and check it is over the allowed limit
      final boolean overRateLimit = tracker.isRateExceeded(System.currentTimeMillis());

      // Pass it through if  we are not currently over the rate limit.
      if (!overRateLimit) {
        if (LOG.isDebugEnabled()) LOG.debug("Allowing {}", request);
        doFilterChain(filterChain, request, response);
        return;
      }

      // We are over the limit.

      // So either reject it, delay it or throttle it.
      long delayMs = getDelayMs();
      boolean insertHeaders = isInsertHeaders();
      switch ((int) delayMs) {
        case -1:
          {
            // Reject this request.
            LOG.warn(
                "DOS ALERT: Request rejected ip={}, session={}, user={}",
                request.getRemoteAddr(),
                request.getRequestedSessionId(),
                request.getUserPrincipal());
            if (insertHeaders) response.addHeader("DoSFilter", "unavailable");
            response.sendError(getTooManyCode());
            return;
          }
        case 0:
          {
            // Fall through to throttle the request.
            LOG.warn(
                "DOS ALERT: Request throttled ip={}, session={}, user={}",
                request.getRemoteAddr(),
                request.getRequestedSessionId(),
                request.getUserPrincipal());
            request.setAttribute(__TRACKER, tracker);
            break;
          }
        default:
          {
            // Insert a delay before throttling the request,
            // using the suspend+timeout mechanism of AsyncContext.
            LOG.warn(
                "DOS ALERT: Request delayed={}ms, ip={}, session={}, user={}",
                delayMs,
                request.getRemoteAddr(),
                request.getRequestedSessionId(),
                request.getUserPrincipal());
            if (insertHeaders) response.addHeader("DoSFilter", "delayed");
            request.setAttribute(__TRACKER, tracker);
            AsyncContext asyncContext = request.startAsync();
            if (delayMs > 0) asyncContext.setTimeout(delayMs);
            asyncContext.addListener(new DoSTimeoutAsyncListener());
            return;
          }
      }
    }

    if (LOG.isDebugEnabled()) LOG.debug("Throttling {}", request);

    // Throttle the request.
    boolean accepted = false;
    try {
      // Check if we can afford to accept another request at this time.
      accepted = _passes.tryAcquire(getMaxWaitMs(), TimeUnit.MILLISECONDS);
      if (!accepted) {
        // We were not accepted, so either we suspend to wait,
        // or if we were woken up we insist or we fail.
        Boolean throttled = (Boolean) request.getAttribute(__THROTTLED);
        long throttleMs = getThrottleMs();
        if (throttled != Boolean.TRUE && throttleMs > 0) {
          int priority = getPriority(request, tracker);
          request.setAttribute(__THROTTLED, Boolean.TRUE);
          if (isInsertHeaders()) response.addHeader("DoSFilter", "throttled");
          AsyncContext asyncContext = request.startAsync();
          request.setAttribute(_suspended, Boolean.TRUE);
          if (throttleMs > 0) asyncContext.setTimeout(throttleMs);
          asyncContext.addListener(_listeners[priority]);
          _queues[priority].add(asyncContext);
          if (LOG.isDebugEnabled()) LOG.debug("Throttled {}, {}ms", request, throttleMs);
          return;
        }

        Boolean resumed = (Boolean) request.getAttribute(_resumed);
        if (resumed == Boolean.TRUE) {
          // We were resumed, we wait for the next pass.
          _passes.acquire();
          accepted = true;
        }
      }

      // If we were accepted (either immediately or after throttle)...
      if (accepted) {
        // ...call the chain.
        if (LOG.isDebugEnabled()) LOG.debug("Allowing {}", request);
        doFilterChain(filterChain, request, response);
      } else {
        // ...otherwise fail the request.
        if (LOG.isDebugEnabled()) LOG.debug("Rejecting {}", request);
        if (isInsertHeaders()) response.addHeader("DoSFilter", "unavailable");
        response.sendError(getTooManyCode());
      }
    } catch (InterruptedException e) {
      LOG.ignore(e);
      response.sendError(getTooManyCode());
    } finally {
      if (accepted) {
        try {
          // Wake up the next highest priority request.
          for (int p = _queues.length - 1; p >= 0; --p) {
            AsyncContext asyncContext = _queues[p].poll();
            if (asyncContext != null) {
              ServletRequest candidate = asyncContext.getRequest();
              Boolean suspended = (Boolean) candidate.getAttribute(_suspended);
              if (suspended == Boolean.TRUE) {
                if (LOG.isDebugEnabled()) LOG.debug("Resuming {}", request);
                candidate.setAttribute(_resumed, Boolean.TRUE);
                asyncContext.dispatch();
                break;
              }
            }
          }
        } finally {
          _passes.release();
        }
      }
    }
  }