public final class SingleUrlParameters implements UrlParameters {

	private final String key;
	private final String value;

	public SingleUrlParameters(String key) {
		this.key = key;
		this.value = null;
	}

	public SingleUrlParameters(String key, String value) {
		this.key = key;
		this.value = value;
	}

	@Override
	public List<String> toUrlParameters() {
		if (value != null) {
			return Collections.singletonList(key + "=" + Utils.encodeValue(value));
		} else {
			return Collections.singletonList(key);
		}
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof SingleUrlParameters)) {
			return false;
		}
		SingleUrlParameters that = (SingleUrlParameters) o;
		return Objects.equals(key, that.key) &&
			Objects.equals(value, that.value);
	}

	@Override
	public int hashCode() {
		return Objects.hash(key, value);
	}
} public interface UrlParameters {

	public List<String> toUrlParameters();

} public class GsonFactory {

	private static final Gson GSON = new Gson();

	public static Gson getGson() {
		return GSON;
	}

} public final class HttpResponse {

	private final int statusCode;
	private final String statusMessage;

	private final String content;

	private final Long consulIndex;
	private final Boolean consulKnownLeader;
	private final Long consulLastContact;

	public HttpResponse(int statusCode, String statusMessage, String content, Long consulIndex, Boolean consulKnownLeader, Long consulLastContact) {
		this.statusCode = statusCode;
		this.statusMessage = statusMessage;
		this.content = content;
		this.consulIndex = consulIndex;
		this.consulKnownLeader = consulKnownLeader;
		this.consulLastContact = consulLastContact;
	}

	public int getStatusCode() {
		return statusCode;
	}

	public String getStatusMessage() {
		return statusMessage;
	}

	public String getContent() {
		return content;
	}

	public Long getConsulIndex() {
		return consulIndex;
	}

	public Boolean isConsulKnownLeader() {
		return consulKnownLeader;
	}

	public Long getConsulLastContact() {
		return consulLastContact;
	}
} public final class TLSConfig {

	public enum KeyStoreInstanceType {
		JKS, JCEKS, PKCS12, PKCS11, DKS
	}

	private final KeyStoreInstanceType keyStoreInstanceType;
	private final String certificatePath;
	private final String certificatePassword;
	private final String keyStorePath;
	private final String keyStorePassword;

	public TLSConfig(KeyStoreInstanceType keyStoreInstanceType, String certificatePath, String certificatePassword, String keyStorePath,
					 String keyStorePassword) {
		this.keyStoreInstanceType = keyStoreInstanceType;
		this.certificatePath = certificatePath;
		this.certificatePassword = certificatePassword;
		this.keyStorePath = keyStorePath;
		this.keyStorePassword = keyStorePassword;
	}

	public KeyStoreInstanceType getKeyStoreInstanceType() {
		return keyStoreInstanceType;
	}

	public String getCertificatePath() {
		return certificatePath;
	}

	public String getCertificatePassword() {
		return certificatePassword;
	}

	public String getKeyStorePath() {
		return keyStorePath;
	}

	public String getKeyStorePassword() {
		return keyStorePassword;
	}
} public class ConsulRawClient {

	public static final String DEFAULT_HOST = "localhost";
	public static final int DEFAULT_PORT = 8500;
	public static final String DEFAULT_PATH = "";

	// one real HTTP client for all instances
	private static final HttpTransport DEFAULT_HTTP_TRANSPORT = new DefaultHttpTransport();

	private final HttpTransport httpTransport;
	private final String agentAddress;

	public static final class Builder {
		private String agentHost;
		private int agentPort;
		private String agentPath;
		private HttpTransport httpTransport;

		public static ConsulRawClient.Builder builder() {
			return new ConsulRawClient.Builder();
		}

		private Builder() {
			this.agentHost = DEFAULT_HOST;
			this.agentPort = DEFAULT_PORT;
			this.agentPath = DEFAULT_PATH;
			this.httpTransport = DEFAULT_HTTP_TRANSPORT;
		}

		public Builder setHost(String host) {
			this.agentHost = host;
			return this;
		}

		public Builder setPort(int port) {
			this.agentPort = port;
			return this;
		}

		public Builder setPath(String path) {
			this.agentPath = path;
			return this;
		}

		public Builder setTlsConfig(TLSConfig tlsConfig) {
			this.httpTransport = new DefaultHttpsTransport(tlsConfig);
			return this;
		}

		public Builder setHttpClient(HttpClient httpClient) {
			this.httpTransport = new DefaultHttpTransport(httpClient);
			return this;
		}

		public ConsulRawClient build() {
			return new ConsulRawClient(httpTransport, agentHost, agentPort, agentPath);
		}
	}

	public ConsulRawClient() {
		this(DEFAULT_HOST);
	}

	public ConsulRawClient(TLSConfig tlsConfig) {
		this(DEFAULT_HOST, tlsConfig);
	}

	public ConsulRawClient(String agentHost) {
		this(agentHost, DEFAULT_PORT);
	}

	public ConsulRawClient(String agentHost, TLSConfig tlsConfig) {
		this(agentHost, DEFAULT_PORT, tlsConfig);
	}

	public ConsulRawClient(String agentHost, int agentPort) {
		this(DEFAULT_HTTP_TRANSPORT, agentHost, agentPort, DEFAULT_PATH);
	}

	public ConsulRawClient(HttpClient httpClient) {
		this(DEFAULT_HOST, httpClient);
	}

	public ConsulRawClient(String agentHost, HttpClient httpClient) {
		this(new DefaultHttpTransport(httpClient), agentHost, DEFAULT_PORT, DEFAULT_PATH);
	}

	public ConsulRawClient(String agentHost, int agentPort, HttpClient httpClient) {
		this(new DefaultHttpTransport(httpClient), agentHost, agentPort, DEFAULT_PATH);
	}

	public ConsulRawClient(String agentHost, int agentPort, TLSConfig tlsConfig) {
		this(new DefaultHttpsTransport(tlsConfig), agentHost, agentPort, DEFAULT_PATH);
	}

	public ConsulRawClient(HttpClient httpClient, String host, int port, String path) {
		this(new DefaultHttpTransport(httpClient), host, port, path);
	}

	// hidden constructor, for tests
	ConsulRawClient(HttpTransport httpTransport, String agentHost, int agentPort, String path) {
		this.httpTransport = httpTransport;

		// check that agentHost has scheme or not
		String agentHostLowercase = agentHost.toLowerCase();
		if (!agentHostLowercase.startsWith("https://") && !agentHostLowercase.startsWith("http://")) {
			// no scheme in host, use default 'http'
			agentHost = "http://" + agentHost;
		}

		this.agentAddress = Utils.assembleAgentAddress(agentHost, agentPort, path);
	}

	public HttpResponse makeGetRequest(String endpoint, UrlParameters... urlParams) {
		return makeGetRequest(endpoint, Arrays.asList(urlParams));
	}

	public HttpResponse makeGetRequest(String endpoint, List<UrlParameters> urlParams) {
		String url = prepareUrl(agentAddress + endpoint);
		url = Utils.generateUrl(url, urlParams);

		HttpRequest request = HttpRequest.Builder.newBuilder()
			.setUrl(url)
			.build();

		return httpTransport.makeGetRequest(request);
	}

	public HttpResponse makeGetRequest(Request request) {
		String url = prepareUrl(agentAddress + request.getEndpoint());
		url = Utils.generateUrl(url, request.getUrlParameters());

		HttpRequest httpRequest = HttpRequest.Builder.newBuilder()
			.setUrl(url)
			.addHeaders(Utils.createTokenMap(request.getToken()))
			.build();

		return httpTransport.makeGetRequest(httpRequest);
	}

	public HttpResponse makePutRequest(String endpoint, String content, UrlParameters... urlParams) {
		String url = prepareUrl(agentAddress + endpoint);
		url = Utils.generateUrl(url, urlParams);

		HttpRequest request = HttpRequest.Builder.newBuilder()
			.setUrl(url)
			.setContent(content)
			.build();

		return httpTransport.makePutRequest(request);
	}

	public HttpResponse makePutRequest(Request request) {
		//,  String endpoint, byte[] binaryContent, UrlParameters... urlParams
		String url = prepareUrl(agentAddress + request.getEndpoint());
		url = Utils.generateUrl(url, request.getUrlParameters());

		HttpRequest httpRequest = HttpRequest.Builder.newBuilder()
			.setUrl(url)
			.setBinaryContent(request.getBinaryContent())
			.addHeaders(Utils.createTokenMap(request.getToken()))
			.build();

		return httpTransport.makePutRequest(httpRequest);
	}

	public HttpResponse makeDeleteRequest(Request request) {
		String url = prepareUrl(agentAddress + request.getEndpoint());
		url = Utils.generateUrl(url, request.getUrlParameters());

		HttpRequest httpRequest = HttpRequest.Builder.newBuilder()
			.setUrl(url)
			.addHeaders(Utils.createTokenMap(request.getToken()))
			.build();

		return httpTransport.makeDeleteRequest(httpRequest);
	}

	private String prepareUrl(String url) {
		if (url.contains(" ")) {
			// temp hack for old clients who did manual encoding and just use %20
			// TODO: Remove it in 2.0
			return Utils.encodeUrl(url);
		} else {
			return url;
		}
	}

} public final class OperationException extends ConsulException {

	private final int statusCode;
	private final String statusMessage;
	private final String statusContent;

	public OperationException(int statusCode, String statusMessage, String statusContent) {
		super("OperationException(statusCode=" + statusCode + ", statusMessage='" + statusMessage + "', statusContent='" + statusContent + "')");
		this.statusCode = statusCode;
		this.statusMessage = statusMessage;
		this.statusContent = statusContent;
	}

	public OperationException(HttpResponse httpResponse) {
		this(httpResponse.getStatusCode(), httpResponse.getStatusMessage(), httpResponse.getContent());
	}

	public int getStatusCode() {
		return statusCode;
	}

	public String getStatusMessage() {
		return statusMessage;
	}

	public String getStatusContent() {
		return statusContent;
	}

	@Override
	public String toString() {
		return "OperationException{" +
				"statusCode=" + statusCode +
				", statusMessage='" + statusMessage + '\'' +
				", statusContent='" + statusContent + '\'' +
				'}';
	}
} public final class Response<T> {

	private final T value;

	private final Long consulIndex;
	private final Boolean consulKnownLeader;
	private final Long consulLastContact;

	public Response(T value, Long consulIndex, Boolean consulKnownLeader, Long consulLastContact) {
		this.value = value;
		this.consulIndex = consulIndex;
		this.consulKnownLeader = consulKnownLeader;
		this.consulLastContact = consulLastContact;
	}

	public Response(T value, HttpResponse httpResponse) {
		this(value, httpResponse.getConsulIndex(), httpResponse.isConsulKnownLeader(), httpResponse.getConsulLastContact());
	}

	public T getValue() {
		return value;
	}

	public Long getConsulIndex() {
		return consulIndex;
	}

	public Boolean isConsulKnownLeader() {
		return consulKnownLeader;
	}

	public Long getConsulLastContact() {
		return consulLastContact;
	}

	@Override
	public String toString() {
		return "Response{" +
				"value=" + value +
				", consulIndex=" + consulIndex +
				", consulKnownLeader=" + consulKnownLeader +
				", consulLastContact=" + consulLastContact +
				'}';
	}
} public class Acl {

	@SerializedName("CreateIndex")
	private long createIndex;

	@SerializedName("ModifyIndex")
	private long modifyIndex;

	@SerializedName("ID")
	private String id;

	@SerializedName("Name")
	private String name;

	@SerializedName("Type")
	private AclType type;

	@SerializedName("Rules")
	private String rules;

	public long getCreateIndex() {
		return createIndex;
	}

	public void setCreateIndex(long createIndex) {
		this.createIndex = createIndex;
	}

	public long getModifyIndex() {
		return modifyIndex;
	}

	public void setModifyIndex(long modifyIndex) {
		this.modifyIndex = modifyIndex;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public AclType getType() {
		return type;
	}

	public void setType(AclType type) {
		this.type = type;
	}

	public String getRules() {
		return rules;
	}

	public void setRules(String rules) {
		this.rules = rules;
	}

	@Override
	public String toString() {
		return "Acl{" +
				"createIndex=" + createIndex +
				", modifyIndex=" + modifyIndex +
				", id='" + id + '\'' +
				", name='" + name + '\'' +
				", type=" + type +
				", rules='" + rules + '\'' +
				'}';
	}
} public class NewAcl {

	@SerializedName("Name")
	private String name;

	@SerializedName("Type")
	private AclType type;

	@SerializedName("Rules")
	private String rules;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public AclType getType() {
		return type;
	}

	public void setType(AclType type) {
		this.type = type;
	}

	public String getRules() {
		return rules;
	}

	public void setRules(String rules) {
		this.rules = rules;
	}

	@Override
	public String toString() {
		return "NewAcl{" +
				"name='" + name + '\'' +
				", type=" + type +
				", rules='" + rules + '\'' +
				'}';
	}
} public class UpdateAcl {

	@SerializedName("ID")
	private String id;

	@SerializedName("Name")
	private String name;

	@SerializedName("Type")
	private AclType type;

	@SerializedName("Rules")
	private String rules;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public AclType getType() {
		return type;
	}

	public void setType(AclType type) {
		this.type = type;
	}

	public String getRules() {
		return rules;
	}

	public void setRules(String rules) {
		this.rules = rules;
	}

	@Override
	public String toString() {
		return "UpdateAcl{" +
				"id='" + id + '\'' +
				", name='" + name + '\'' +
				", type=" + type +
				", rules='" + rules + '\'' +
				'}';
	}
}
import java.util.List;
import java.util.Map;
import com.ecwid.consul.ConsulException;
import com.ecwid.consul.SingleUrlParameters;
import com.ecwid.consul.UrlParameters;
import com.ecwid.consul.json.GsonFactory;
import com.ecwid.consul.transport.HttpResponse;
import com.ecwid.consul.transport.TLSConfig;
import com.ecwid.consul.v1.ConsulRawClient;
import com.ecwid.consul.v1.OperationException;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.acl.model.Acl;
import com.ecwid.consul.v1.acl.model.NewAcl;
import com.ecwid.consul.v1.acl.model.UpdateAcl;
import com.google.gson.reflect.TypeToken;
package com.ecwid.consul.v1.acl;



/**
 * @author Vasily Vasilkov (vgv@ecwid.com)
 */
public final class AclConsulClient implements AclClient {

	private final ConsulRawClient rawClient;

	public AclConsulClient(ConsulRawClient rawClient) {
		this.rawClient = rawClient;
	}

	public AclConsulClient() {
		this(new ConsulRawClient());
	}

	public AclConsulClient(TLSConfig tlsConfig) {
		this(new ConsulRawClient(tlsConfig));
	}

	public AclConsulClient(String agentHost) {
		this(new ConsulRawClient(agentHost));
	}

	public AclConsulClient(String agentHost, TLSConfig tlsConfig) {
		this(new ConsulRawClient(agentHost, tlsConfig));
	}

	public AclConsulClient(String agentHost, int agentPort) {
		this(new ConsulRawClient(agentHost, agentPort));
	}

	public AclConsulClient(String agentHost, int agentPort, TLSConfig tlsConfig) {
		this(new ConsulRawClient(agentHost, agentPort, tlsConfig));
	}

	@Override
	public Response<String> aclCreate(NewAcl newAcl, String token) {
		UrlParameters tokenParams = token != null ? new SingleUrlParameters("token", token) : null;
		String json = GsonFactory.getGson().toJson(newAcl);
		HttpResponse httpResponse = rawClient.makePutRequest("/v1/acl/create", json, tokenParams);

		if (httpResponse.getStatusCode() == 200) {
			Map<String, String> value = GsonFactory.getGson().fromJson(httpResponse.getContent(), new TypeToken<Map<String, String>>() {
			}.getType());
			return new Response<String>(value.get("ID"), httpResponse);
		} else {
			throw new OperationException(httpResponse);
		}
	}

	@Override
	public Response<Void> aclUpdate(UpdateAcl updateAcl, String token) {