@Override
 public String getEndpointUrl() {
   return config
       .getEndpoint()
       .map(EndpointDescription::getEndpointUrl)
       .orElse(config.getEndpointUrl().orElse(""));
 }
  public UaTcpStackClient(UaTcpStackClientConfig config) {
    this.config = config;

    application =
        new ApplicationDescription(
            config.getApplicationUri(),
            config.getProductUri(),
            config.getApplicationName(),
            ApplicationType.Client,
            null,
            null,
            null);

    secureChannel = new ClientSecureChannel(SecurityPolicy.None, MessageSecurityMode.None);

    channelManager = new ChannelManager(this);
  }
  /**
   * 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);
  }
 @Override
 public ExecutorService getExecutorService() {
   return config.getExecutor();
 }
 @Override
 public Optional<EndpointDescription> getEndpoint() {
   return config.getEndpoint();
 }
 @Override
 public UInteger getChannelLifetime() {
   return config.getChannelLifetime();
 }
 @Override
 public ChannelConfig getChannelConfig() {
   return config.getChannelConfig();
 }
 @Override
 public Optional<KeyPair> getKeyPair() {
   return config.getKeyPair();
 }
 @Override
 public Optional<X509Certificate> getCertificate() {
   return config.getCertificate();
 }