All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jcustenborder.kafka.connect.client.KafkaConnectClientImpl Maven / Gradle / Ivy

The newest version!
/**
 * Copyright © 2019 Jeremy Custenborder ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.jcustenborder.kafka.connect.client;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.jcustenborder.kafka.connect.client.model.ConnectorInfo;
import com.github.jcustenborder.kafka.connect.client.model.ConnectorPlugin;
import com.github.jcustenborder.kafka.connect.client.model.ConnectorStatus;
import com.github.jcustenborder.kafka.connect.client.model.CreateConnectorRequest;
import com.github.jcustenborder.kafka.connect.client.model.CreateConnectorResponse;
import com.github.jcustenborder.kafka.connect.client.model.ServerInfo;
import com.github.jcustenborder.kafka.connect.client.model.TaskConfig;
import com.github.jcustenborder.kafka.connect.client.model.TaskStatus;
import com.github.jcustenborder.kafka.connect.client.model.ValidateResponse;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

class KafkaConnectClientImpl implements AsyncKafkaConnectClient, KafkaConnectClient {
  static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
  static final RequestBody EMPTY = RequestBody.create(null, new byte[]{});
  static final TypeReference> CONFIG_TYPE = new TypeReference>() {
  };
  static final TypeReference> CONNECTORS_TYPE = new TypeReference>() {
  };
  static final TypeReference> TASKCONFIG_TYPE = new TypeReference>() {
  };
  static final TypeReference> CONNECTOR_PLUGIN_TYPE = new TypeReference>() {
  };
  private static final Logger log = LoggerFactory.getLogger(KafkaConnectClientImpl.class);
  final AbstractSettings settings;

  public KafkaConnectClientImpl(AbstractSettings settings) {
    this.settings = settings;
  }

  static HttpUrl addPathSegments(HttpUrl baseUrl, Iterable parts) {
    HttpUrl.Builder builder = baseUrl.newBuilder();
    for (String part : parts) {
      builder = builder.addPathSegment(part);
    }
    return builder.build();
  }

  static  T get(Future future) throws IOException {
    try {
      return future.get();
    } catch (InterruptedException e) {
      throw new IOException(e);
    } catch (ExecutionException ex) {
      if (ex.getCause() instanceof KafkaConnectException) {
        throw (KafkaConnectException) ex.getCause();
      } else {
        throw new IOException(ex);
      }
    }
  }

  HttpUrl baseUrl(String... parts) {
    return addPathSegments(this.settings.baseUrl(), Arrays.asList(parts));
  }

  HttpUrl connectorsUrl(String... parts) {
    List segments = new ArrayList<>();
    segments.add("connectors");
    segments.addAll(Arrays.asList(parts));
    return addPathSegments(this.settings.baseUrl(), segments);
  }

  HttpUrl connectorsPluginsUrl(String... parts) {
    List segments = new ArrayList<>();
    segments.add("connector-plugins");
    segments.addAll(Arrays.asList(parts));
    return addPathSegments(this.settings.baseUrl(), segments);
  }

  void checkConnectorConfig(Map config) {
    //TODO: Comeback and do some validation.
//    Preconditions.checkNotNull("config", "config cannot be null");
//    Preconditions.checkState(config.containsKey("connector.class"), "connector.class must be specified.");
//    Preconditions.checkState(config.containsKey("tasks.max"), "tasks.max must be specified.");
  }

  @Override
  public List connectors() throws IOException {
    CompletableFuture> future = connectorsAsync();
    return get(future);
  }

  @Override
  public CreateConnectorResponse createConnector(CreateConnectorRequest request) throws IOException {
    return get(createConnectorAsync(request));
  }


  @Override
  public ConnectorInfo createOrUpdate(String name, Map config) throws IOException {
    CompletableFuture future = createOrUpdateAsync(name, config);
    return get(future);
  }

  @Override
  public ConnectorInfo info(String name) throws IOException {
    CompletableFuture future = infoAsync(name);
    return get(future);
  }

  @Override
  public Map config(String name) throws IOException {
    CompletableFuture> future = configAsync(name);
    return get(future);

  }

  @Override
  public void delete(String name) throws IOException {
    CompletableFuture future = deleteAsync(name);
    get(future);
  }

  @Override
  public void restart(String name) throws IOException {
    CompletableFuture future = restartAsync(name);
    get(future);
  }

  @Override
  public void pause(String name) throws IOException {
    CompletableFuture future = pauseAsync(name);
    get(future);
  }

  @Override
  public void resume(String name) throws IOException {
    CompletableFuture future = resumeAsync(name);
    get(future);
  }

  @Override
  public ConnectorStatus status(String name) throws IOException {
    CompletableFuture future = statusAsync(name);
    return get(future);
  }

  @Override
  public TaskStatus status(String name, int taskId) throws IOException {
    CompletableFuture future = statusAsync(name, taskId);
    return get(future);
  }

  @Override
  public void restart(String name, int taskId) throws IOException {
    CompletableFuture future = restartAsync(name, taskId);
    get(future);
  }


  @Override
  public List connectorPlugins() throws IOException {
    CompletableFuture> future = connectorPluginsAsync();
    return get(future);
  }

  @Override
  public ValidateResponse validate(String name, Map config) throws IOException {
    CompletableFuture future = validateAsync(name, config);
    return get(future);
  }

  @Override
  public ServerInfo serverInfo() throws IOException {
    CompletableFuture future = serverInfoAsync();
    return get(future);
  }

  private  CompletableFuture executeRequest(Request request, TypeReference type) {
    final CompletableFuture futureResult = new CompletableFuture<>();
    final TypeReferenceCallback callback = new TypeReferenceCallback<>(
        this.settings,
        futureResult,
        type
    );

    callback.newCall(request);
    return futureResult;
  }

  private  CompletableFuture executeRequest(Request request, Class type) {
    final CompletableFuture futureResult = new CompletableFuture<>();
    final ClassCallback callback = new ClassCallback<>(
        this.settings,
        futureResult,
        type
    );
    callback.newCall(request);
    return futureResult;
  }

  Request.Builder newBuilder() {
    Request.Builder result = new Request.Builder();
    if (this.settings.hasCredentials()) {
      result.addHeader("Authorization", this.settings.credentials());
    }
    return result;
  }

  @Override
  public CompletableFuture> connectorsAsync() {
    HttpUrl url = connectorsUrl();
    log.trace("connectorsAsync() - url = '{}'", url);

    Request request = newBuilder()
        .url(url)
        .get()
        .build();
    CompletableFuture> result = executeRequest(request, CONNECTORS_TYPE);
    return result;
  }

  @Override
  public CompletableFuture createConnectorAsync(CreateConnectorRequest request) {
    HttpUrl url = connectorsUrl();
    log.trace("createConnector() url = '{}' request = '{}'", url, request);
    Request httpRequest = newBuilder()
        .url(url)
        .post(body(request))
        .build();
    return executeRequest(httpRequest, CreateConnectorResponse.class);
  }

  protected RequestBody body(Object o) {
    try {
      byte[] body = this.settings.objectMapper().writeValueAsBytes(o);
      RequestBody requestBody = RequestBody.create(body, JSON);
      return requestBody;
    } catch (JsonProcessingException e) {
      throw new IllegalStateException(e);
    }
  }

  @Override
  public CompletableFuture createOrUpdateAsync(String name, Map config) {
//    Preconditions.checkNotNull(name, "name cannot be null");
    checkConnectorConfig(config);
    HttpUrl connectorUrl = connectorsUrl(name, "config");
    log.trace("createOrUpdateAsync() - url = '{}'", connectorUrl);
    Request request = newBuilder()
        .url(connectorUrl)
        .put(body(config))
        .build();
    return executeRequest(request, ConnectorInfo.class);
  }

  @Override
  public CompletableFuture infoAsync(String name) {
//    Preconditions.checkNotNull(name, "name cannot be null");
    HttpUrl connectorUrl = connectorsUrl(name);
    Request request = newBuilder()
        .get()
        .url(connectorUrl)
        .build();
    return executeRequest(request, ConnectorInfo.class);
  }


  @Override
  public CompletableFuture> configAsync(String name) {
    HttpUrl url = connectorsUrl(name, "config");
    log.trace("connectors() - url = '{}'", url);
    Request request = newBuilder()
        .url(url)
        .get()
        .build();
    return executeRequest(request, CONFIG_TYPE);
  }

  @Override
  public CompletableFuture deleteAsync(String name) {
//    Preconditions.checkNotNull(name, "name cannot be null");
    HttpUrl connectorUrl = connectorsUrl(name);
    Request request = newBuilder()
        .url(connectorUrl)
        .delete()
        .build();
    return executeRequest(request, Void.class);
  }

  @Override
  public CompletableFuture restartAsync(String name) {
    HttpUrl connectorUrl = connectorsUrl(name, "restart");
    Request request = newBuilder()
        .url(connectorUrl)
        .post(EMPTY)
        .build();
    return executeRequest(request, Void.class);
  }

  @Override
  public CompletableFuture pauseAsync(String name) {
    HttpUrl connectorUrl = connectorsUrl(name, "pause");
    Request request = newBuilder()
        .url(connectorUrl)
        .put(EMPTY)
        .build();
    return executeRequest(request, Void.class);
  }

  @Override
  public CompletableFuture resumeAsync(String name) {
    HttpUrl connectorUrl = connectorsUrl(name, "resume");
    Request request = newBuilder()
        .url(connectorUrl)
        .put(EMPTY)
        .build();
    return executeRequest(request, Void.class);
  }

  @Override
  public CompletableFuture statusAsync(String name) {
    HttpUrl connectorUrl = connectorsUrl(name, "status");
    Request request = newBuilder()
        .url(connectorUrl)
        .build();
    return executeRequest(request, ConnectorStatus.class);
  }

  @Override
  public CompletableFuture statusAsync(String name, int taskId) {
    HttpUrl connectorUrl = connectorsUrl(name, "tasks", Integer.toString(taskId), "status");
    Request request = newBuilder()
        .url(connectorUrl)
        .build();
    return executeRequest(request, TaskStatus.class);
  }

  @Override
  public CompletableFuture restartAsync(String name, int taskId) {
    HttpUrl connectorUrl = connectorsUrl(name, "tasks", Integer.toString(taskId), "restart");
    Request request = newBuilder()
        .url(connectorUrl)
        .post(EMPTY)
        .build();
    return executeRequest(request, Void.class);
  }

  @Override
  public List taskConfigs(String name) throws IOException {
    CompletableFuture> future = taskConfigsAsync(name);
    return get(future);
  }

  @Override
  public CompletableFuture> connectorPluginsAsync() {
    HttpUrl pluginsUrl = connectorsPluginsUrl();
    Request request = newBuilder()
        .url(pluginsUrl)
        .build();
    return executeRequest(request, CONNECTOR_PLUGIN_TYPE);
  }

  @Override
  public CompletableFuture validateAsync(String name, Map config) {
    HttpUrl url = connectorsPluginsUrl(
        name,
        "config",
        "validate"
    );
    Request request = newBuilder()
        .url(url)
        .put(body(config))
        .build();
    return executeRequest(request, ValidateResponse.class);
  }

  @Override
  public CompletableFuture serverInfoAsync() {
    Request request = newBuilder()
        .url(this.settings.baseUrl())
        .build();
    return executeRequest(request, ServerInfo.class);
  }

  @Override
  public CompletableFuture> taskConfigsAsync(String name) {
    HttpUrl connectorUrl = connectorsUrl(name, "tasks");
    Request request = newBuilder()
        .get()
        .url(connectorUrl)
        .build();
    return executeRequest(request, TASKCONFIG_TYPE);
  }


  @Override
  public void close() throws Exception {
    if (this.settings.shutdownSchedulerOnClose()) {
      this.settings.scheduler().shutdown();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy