code: /*
   * Copyright (c) 2019 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.stack.server;
  
  import java.security.cert.X509Certificate;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.function.Supplier;
  
  import com.google.common.base.MoreObjects;
  import com.google.common.base.Objects;
  import com.google.common.collect.ImmutableList;
  import org.eclipse.milo.opcua.stack.core.Stack;
  import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
  import org.eclipse.milo.opcua.stack.core.transport.TransportProfile;
  import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode;
  import org.eclipse.milo.opcua.stack.core.types.enumerated.UserTokenType;
  import org.eclipse.milo.opcua.stack.core.types.structured.UserTokenPolicy;
  import org.jetbrains.annotations.Nullable;
  
  public class EndpointConfiguration {
  
      private final TransportProfile transportProfile;
      private final String bindAddress;
      private final int bindPort;
      private final String hostname;
      private final String path;
      private final Supplier<X509Certificate> certificateSupplier;
      private final SecurityPolicy securityPolicy;
      private final MessageSecurityMode securityMode;
      private final ImmutableList<UserTokenPolicy> tokenPolicies;
  
      private EndpointConfiguration(
          TransportProfile transportProfile,
          String bindAddress,
          int bindPort,
          String hostname,
          String path,
          Supplier<X509Certificate> certificateSupplier,
          SecurityPolicy securityPolicy,
          MessageSecurityMode securityMode,
          List<UserTokenPolicy> tokenPolicies) {
  
          this.transportProfile = transportProfile;
          this.bindAddress = bindAddress;
          this.bindPort = bindPort;
          this.hostname = hostname;
          this.path = path;
          this.certificateSupplier = certificateSupplier;
          this.securityPolicy = securityPolicy;
          this.securityMode = securityMode;
          this.tokenPolicies = ImmutableList.copyOf(tokenPolicies);
      }
  
      public TransportProfile getTransportProfile() {
          return transportProfile;
      }
  
      public String getBindAddress() {
          return bindAddress;
      }
  
      public int getBindPort() {
          return bindPort;
      }
  
      public String getHostname() {
          return hostname;
      }
  
      public String getPath() {
          return path;
      }
  
      @Nullable
      public X509Certificate getCertificate() {
          return certificateSupplier.get();
      }
  
      public SecurityPolicy getSecurityPolicy() {
          return securityPolicy;
      }
  
      public MessageSecurityMode getSecurityMode() {
          return securityMode;
      }
  
      public ImmutableList<UserTokenPolicy> getTokenPolicies() {
          return tokenPolicies;
      }
  
      public String getEndpointUrl() {
          String scheme = transportProfile.getScheme();
          String p = path.isEmpty() || path.startsWith("/") ? path : "/" + path;
  
          return String.format("%s://%s:%s%s", scheme, hostname, bindPort, p);
      }
  
  
      @Override
      public boolean equals(Object o) {
          if (this == o) return true;
          if (o == null || getClass() != o.getClass()) return false;
          EndpointConfiguration that = (EndpointConfiguration) o;
          return bindPort == that.bindPort &&
              transportProfile == that.transportProfile &&
              Objects.equal(bindAddress, that.bindAddress) &&
              Objects.equal(hostname, that.hostname) &&
              Objects.equal(path, that.path) &&
              Objects.equal(getCertificate(), that.getCertificate()) &&
              securityPolicy == that.securityPolicy &&
              securityMode == that.securityMode &&
              Objects.equal(tokenPolicies, that.tokenPolicies);
      }
  
      @Override
      public int hashCode() {
          return Objects.hashCode(
              transportProfile,
              bindAddress,
              bindPort,
              hostname,
              path,
              getCertificate(),
              securityPolicy,
              securityMode,
              tokenPolicies
          );
      }
  
      @Override
      public String toString() {
          return MoreObjects.toStringHelper(this)
              .add("transportProfile", transportProfile)
              .add("bindAddress", bindAddress)
              .add("bindPort", bindPort)
              .add("hostname", hostname)
              .add("path", path)
              .add("certificate", getCertificate())
              .add("securityPolicy", securityPolicy)
              .add("securityMode", securityMode)
              .add("tokenPolicies", tokenPolicies)
              .toString();
      }
  
      public static EndpointConfiguration.Builder newBuilder() {
          return new Builder();
      }
  
      public static class Builder {
  
          /**
           * A {@link UserTokenPolicy} for anonymous access.
           */
          static final UserTokenPolicy USER_TOKEN_POLICY_ANONYMOUS = new UserTokenPolicy(
              "anonymous",
              UserTokenType.Anonymous,
              null,
              null,
              null
          );
  
          TransportProfile transportProfile = TransportProfile.TCP_UASC_UABINARY;
          String bindAddress = "localhost";
          int bindPort = Stack.DEFAULT_TCP_PORT;
          String hostname = "localhost";
          String path = "";
          Supplier<X509Certificate> certificateSupplier = () -> null;
          SecurityPolicy securityPolicy = SecurityPolicy.None;
          MessageSecurityMode securityMode = MessageSecurityMode.None;
          List<UserTokenPolicy> tokenPolicies = new ArrayList<>();
  
          public Builder setTransportProfile(TransportProfile transportProfile) {
              this.transportProfile = transportProfile;
              return this;
          }
  
          public Builder setBindAddress(String bindAddress) {
              this.bindAddress = bindAddress;
              return this;
          }
  
          public Builder setBindPort(int bindPort) {
              this.bindPort = bindPort;
              return this;
          }
  
          public Builder setHostname(String hostname) {
              this.hostname = hostname;
              return this;
          }
  
          public Builder setPath(String path) {
              this.path = path;
              return this;
          }
  
          public Builder setCertificate(@Nullable X509Certificate certificate) {
              this.certificateSupplier = () -> certificate;
              return this;
          }
  
          public Builder setCertificate(Supplier<X509Certificate> certificateSupplier) {
              this.certificateSupplier = certificateSupplier;
              return this;
          }
  
          public Builder setSecurityPolicy(SecurityPolicy securityPolicy) {
              this.securityPolicy = securityPolicy;
              return this;
          }
  
          public Builder setSecurityMode(MessageSecurityMode securityMode) {
              this.securityMode = securityMode;
              return this;
          }
  
          public Builder addTokenPolicy(UserTokenPolicy tokenPolicy) {
              tokenPolicies.add(tokenPolicy);
              return this;
          }
  
          public Builder addTokenPolicies(UserTokenPolicy... tokenPolicies) {
              Collections.addAll(this.tokenPolicies, tokenPolicies);
              return this;
          }
  
          private Builder addTokenPolicies(List<UserTokenPolicy> tokenPolicies) {
              this.tokenPolicies.addAll(tokenPolicies);
              return this;
          }
  
          public Builder copy() {
              return new Builder()
                  .setTransportProfile(transportProfile)
                  .setBindAddress(bindAddress)
                  .setBindPort(bindPort)
                  .setHostname(hostname)
                  .setPath(path)
                  .setCertificate(certificateSupplier)
                  .setSecurityPolicy(securityPolicy)
                  .setSecurityMode(securityMode)
                  .addTokenPolicies(tokenPolicies);
          }
  
          public EndpointConfiguration build() {
              if (securityPolicy != SecurityPolicy.None ||
                  securityMode != MessageSecurityMode.None) {
  
                  if (securityPolicy == SecurityPolicy.None) {
                      throw new IllegalArgumentException("securityPolicy: " + securityPolicy);
                  }
                  if (securityMode == MessageSecurityMode.None) {
                      throw new IllegalArgumentException("securityMode: " + securityMode);
                  }
                  if (certificateSupplier.get() == null) {
                      throw new IllegalStateException("security requires certificate");
                  }
              }
  
              switch (transportProfile) {
                  case HTTPS_UAXML:
                  case HTTPS_UAJSON:
                  case WSS_UASC_UABINARY:
                  case WSS_UAJSON:
                      throw new IllegalArgumentException(
                          "unsupported transport: " + transportProfile);
  
                  default:
                      break;
              }
  
              List<UserTokenPolicy> tokenPolicies = this.tokenPolicies;
  
              if (tokenPolicies.isEmpty()) {
                  tokenPolicies.add(USER_TOKEN_POLICY_ANONYMOUS);
              }
  
              return new EndpointConfiguration(
                  transportProfile,
                  bindAddress,
                  bindPort,
                  hostname,
                  path,
                  certificateSupplier,
                  securityPolicy,
                  securityMode,
                  tokenPolicies
              );
          }
  
      }
  }
  