private void returnKeepAlive(ServiceRequest<PublishRequest, PublishResponse> service) {
    ResponseHeader header = service.createResponseHeader();

    UInteger sequenceNumber = uint(currentSequenceNumber());

    NotificationMessage notificationMessage =
        new NotificationMessage(sequenceNumber, DateTime.now(), new ExtensionObject[0]);

    UInteger[] available = getAvailableSequenceNumbers();

    UInteger requestHandle = service.getRequest().getRequestHeader().getRequestHandle();
    StatusCode[] acknowledgeResults = subscriptionManager.getAcknowledgeResults(requestHandle);

    PublishResponse response =
        new PublishResponse(
            header,
            subscriptionId,
            available,
            moreNotifications,
            notificationMessage,
            acknowledgeResults,
            new DiagnosticInfo[0]);

    service.setResponse(response);

    logger.debug(
        "[id={}] returned keep-alive NotificationMessage sequenceNumber={}.",
        subscriptionId,
        sequenceNumber);
  }
  /**
   * Query the GetEndpoints service at the given endpoint URL.
   *
   * @param endpointUrl the endpoint URL to get endpoints from.
   * @return the {@link EndpointDescription}s returned by the GetEndpoints service.
   */
  public static CompletableFuture<EndpointDescription[]> getEndpoints(String endpointUrl) {
    UaTcpStackClientConfig config =
        UaTcpStackClientConfig.builder().setEndpointUrl(endpointUrl).build();

    UaTcpStackClient client = new UaTcpStackClient(config);

    GetEndpointsRequest request =
        new GetEndpointsRequest(
            new RequestHeader(null, DateTime.now(), uint(1), uint(0), null, uint(5000), null),
            endpointUrl,
            null,
            new String[] {Stack.UA_TCP_BINARY_TRANSPORT_URI});

    return client
        .<GetEndpointsResponse>sendRequest(request)
        .whenComplete((r, ex) -> client.disconnect())
        .thenApply(GetEndpointsResponse::getEndpoints);
  }
  /**
   * Query the FindServers service at the given endpoint URL.
   *
   * <p>The endpoint URL(s) for each server {@link ApplicationDescription} returned can then be used
   * in a {@link #getEndpoints(String)} call to discover the endpoints for that server.
   *
   * @param endpointUrl the endpoint URL to find servers at.
   * @return the {@link ApplicationDescription}s returned by the FindServers service.
   */
  public static CompletableFuture<ApplicationDescription[]> findServers(String endpointUrl) {
    UaTcpStackClientConfig config =
        UaTcpStackClientConfig.builder().setEndpointUrl(endpointUrl).build();

    UaTcpStackClient client = new UaTcpStackClient(config);

    FindServersRequest request =
        new FindServersRequest(
            new RequestHeader(null, DateTime.now(), uint(1), uint(0), null, uint(5000), null),
            endpointUrl,
            null,
            null);

    return client
        .<FindServersResponse>sendRequest(request)
        .whenComplete((r, ex) -> client.disconnect())
        .thenApply(FindServersResponse::getServers);
  }