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

io.stargate.sdk.StargateClient Maven / Gradle / Ivy

The newest version!
/*
 * Copyright DataStax, Inc.
 *
 * 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 io.stargate.sdk;

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import com.datastax.oss.driver.api.core.config.TypedDriverOption;
import io.stargate.sdk.audit.ServiceCallObserver;
import io.stargate.sdk.data.DataApiClient;
import io.stargate.sdk.doc.StargateDocumentApiClient;
import io.stargate.sdk.gql.StargateGraphQLApiClient;
import io.stargate.sdk.grpc.ServiceGrpc;
import io.stargate.sdk.grpc.StargateGrpcApiClient;
import io.stargate.sdk.http.RetryHttpClient;
import io.stargate.sdk.http.ServiceHttp;
import io.stargate.sdk.rest.StargateRestApiClient;
import io.stargate.sdk.utils.AnsiUtils;
import io.stargate.sdk.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import static io.stargate.sdk.utils.AnsiUtils.green;

/**
 * Global Client to interact with a Stargate instance.
 *
 * @author Cedrick LUNVEN (@clunven)
 */
public class StargateClient implements Closeable {

    /** Logger for our Client. */
    private static final Logger LOGGER = LoggerFactory.getLogger(StargateClient.class);

    /** Configuration. */
    private final StargateClientBuilder conf;

    /** The Cql Session to interact with Cassandra through the DataStax CQL Drivers. */
    private CqlSession cqlSession;
    
    /** 
     * Local DC. (load balancing on the nodes there and failover only if not available. 
     */
    protected String currentDatacenter;
    
    /** 
     * Wrapping Api REST DATA resources.
     */
    protected StargateRestApiClient apiRestClient;
    
    /**
     * Wrapping Api Document resources.
     */
    protected StargateDocumentApiClient apiDocumentClient;
    
    /**
     * Wrapping Api GraphQL resources.
     */
    protected StargateGraphQLApiClient apiGraphQLClient;
    
    /**
     * Wrapping Api GRPC resources.
     */
    protected StargateGrpcApiClient apiGrpcClient;

    /**
     * Wrapping JSON Api Client
     */
    protected DataApiClient apiDataClient;

    // ------------------------------------------------
    // ---------------- Initializing   ----------------
    // ------------------------------------------------

    /**
     * Default constructor
     */
    public StargateClient() {
        this(builder());
    }

    /**
     * Initialize the client from builder parameters.
     *
     * @param config
     *      current builder.
     */
    public StargateClient(StargateClientBuilder config) {
        this.conf = config;

        // ------------- CqlSession ------------------

        // Initialize local data center
        this.currentDatacenter = resolveDataCenterName(config);

        // Initialize session
        if (config.isEnabledCql()) {
            cqlSession = initCqlSession();
            LOGGER.info("+ API Cql      :[" + green("ENABLED") + "]");
        }

        // ------------- HTTP API ------------------

        if (config.getStargateNodesDC().isEmpty()) {
            this.apiRestClient      = new StargateRestApiClient();
            this.apiDocumentClient  = new StargateDocumentApiClient();
            this.apiGraphQLClient   = new StargateGraphQLApiClient();
            this.apiDataClient = new DataApiClient();
            if (config.isEnabledGrpc()) {
                this.apiGrpcClient = new StargateGrpcApiClient();
            }
        } else {
            // 4 Stateless services
            ServiceDeployment restDeploy = new ServiceDeployment<>();
            ServiceDeployment docDeploy  = new ServiceDeployment<>();
            ServiceDeployment gqlDeploy  = new ServiceDeployment<>();
            ServiceDeployment jsonDeploy = new ServiceDeployment<>();
            config.getStargateNodesDC().values().forEach(dc -> {
                restDeploy.addDatacenter(new ServiceDatacenter<>(dc.getId(), dc.getTokenProvider(), dc.getRestNodes()));
                docDeploy.addDatacenter(new ServiceDatacenter<>(dc.getId(), dc.getTokenProvider(), dc.getDocNodes()));
                gqlDeploy.addDatacenter(new ServiceDatacenter<>(dc.getId(), dc.getTokenProvider(), dc.getGraphqlNodes()));
                jsonDeploy.addDatacenter(new ServiceDatacenter<>(dc.getId(), dc.getTokenProvider(), dc.getJsonNodes()));
            });
            this.apiRestClient = new StargateRestApiClient(restDeploy);
            this.apiDocumentClient = new StargateDocumentApiClient(docDeploy);
            this.apiGraphQLClient = new StargateGraphQLApiClient(gqlDeploy);
            this.apiDataClient = new DataApiClient(jsonDeploy);

            // grpc service if needed
            if (config.isEnabledGrpc()) {
                ServiceDeployment grpcDeploy = new ServiceDeployment<>();
                config.getStargateNodesDC().values().forEach(dc -> {
                    grpcDeploy.addDatacenter(new ServiceDatacenter<>(dc.getId(), dc.getTokenProvider(), dc.getGrpcNodes()));
                });
                this.apiGrpcClient = new StargateGrpcApiClient(grpcDeploy);
            }
        }

        // ------------- HTTP ---------------------
        
        if (config.getRetryConfig() != null) {
            RetryHttpClient.withRetryConfig(config.getRetryConfig());
        }
        if (config.getRequestConfig() != null) {
            RetryHttpClient.withRequestConfig(config.getRequestConfig());
        }
        if (!config.getObservers().isEmpty()) {
            for (Map.Entry obs : config.getObservers().entrySet()) {
                RetryHttpClient.registerListener(obs.getKey(), obs.getValue());
            }
        }
    }

    /**
     * Datacenter name can be retrieved on multiple ways:
     * 1. Explicitly populated
     * 2. As a cql property
     * 3. In the Stargate node topology
     * 4. Default Value dc1
     *
     * @param config
     *      current configuration
     * @return
     *      datacenter name
     */
    private String resolveDataCenterName(StargateClientBuilder config) {
        // If not #1...
        if (!Utils.hasLength(config.getLocalDatacenter())) {
            // #2. Read from CQL
            String cqlDc = config.getCqlOptions().get(TypedDriverOption.LOAD_BALANCING_LOCAL_DATACENTER);
            if (Utils.hasLength(cqlDc)) {
                config.withLocalDatacenter(cqlDc);
                LOGGER.info("Datacenter value defined in cql configuration {}", cqlDc);
            // #3. Read from node topology
            } else if (!config.getStargateNodesDC().isEmpty()) {
                Set< String > dcs = config.getStargateNodesDC().keySet();
                String dcPicked = dcs.iterator().next();
                config.withLocalDatacenter(dcPicked);
                LOGGER.info("Datacenter from node topology '{}' ( '{}' dc found)", dcPicked, dcs.size());
            // #4. Default
            } else {
                LOGGER.info("Datacenter name is " + green("'{}'"), StargateClientBuilder.DEFAULT_DATACENTER);
                config.withLocalDatacenter(StargateClientBuilder.DEFAULT_DATACENTER);
            }
        }
        return config.getLocalDatacenter();
    }
    
    /**
     * Initializing  CqlSession based on current parameters.
     * - Parameters can be provided by the Builder populating Values
     * - Parameters can be provided by the configuration keys in Spring application.yaml
     *
     * @return
     *      a new cqlSession
     */
    public CqlSession initCqlSession() {

        /* ---------------------------------------
         * Close and clean up existing session:
         * This behaviour occurs when you fail over from one DC to another in Astra
         * ---------------------------------------*/
        if (null != cqlSession && !cqlSession.isClosed()) {
            cqlSession.close();
            cqlSession = null;
        }
        
        // Only create if CQL Session is enabled
        if (conf.isEnabledCql()) {

            // A CQL Session has been provided, we will reuse it
            if (conf.getCqlSession() != null) {
                cqlSession = conf.getCqlSession();
                
            } else {
                
                // Connectivity to Astra with a SCB
                try {
                    String scb = conf.getCqlOptions().get(this.currentDatacenter, TypedDriverOption.CLOUD_SECURE_CONNECT_BUNDLE);
                    if (Utils.hasLength(scb)) {
                        conf.withCqlCloudSecureConnectBundle(scb);
                        conf.setLocalDatacenter(currentDatacenter);
                    }
                    // Connectivity to local Cassandra with contact points
                    List configContactPointsDC = conf.getCqlOptions().get(this.currentDatacenter, TypedDriverOption.CONTACT_POINTS);
                    if (configContactPointsDC != null && !configContactPointsDC.isEmpty()) {
                        conf.withCqlContactPoints(configContactPointsDC.toArray(new String[0]));
                        conf.withLocalDatacenter(currentDatacenter);
                    }
                } catch(NullPointerException npe) {
                    System.out.println("No DC profile");
                }
                
                // Configuration through Map values
                DriverConfigLoader configLoader = conf.getCqlDriverConfigLoaderBuilder().build();
                CqlSessionBuilder sessionBuilder = CqlSession.builder().withConfigLoader(configLoader);
                // Expand configuration
                if (null != conf.getCqlMetricsRegistry()) {
                    sessionBuilder.withMetricRegistry(conf.getCqlMetricsRegistry());
                }
                // Request Tracking
                if (null != conf.getCqlRequestTracker()) {
                    sessionBuilder.withRequestTracker(conf.getCqlRequestTracker());
                }
                cqlSession = sessionBuilder.build();
            }
        }
        
        // Testing CqlSession
        if (cqlSession != null) {
            String currentDC = Objects
                    .requireNonNull(cqlSession.execute("SELECT data_center from system.local").one())
                    .getString("data_center");
            if (cqlSession.getKeyspace().isPresent()) {
                LOGGER.info("+ CqlSession   :[" + green("ENABLED") + "] with keyspace [" + AnsiUtils.cyan("{}")  +"] and dc [" + AnsiUtils.cyan("{}")  + "]",
                        cqlSession.getKeyspace().get(), currentDC);
            } else {
                LOGGER.info("+ CqlSession   :[" + green("ENABLED") + "]");
            }
                
            // As we opened a cqlSession we may want to close it properly at application shutdown.
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    if (!cqlSession.isClosed()) {
                        cqlSession.close();
                        LOGGER.info("Closing CqlSession.");
                    }
                } 
            });
        } else {
            LOGGER.info("+ CqlSession   :[" + AnsiUtils.red("DISABLED") + "]");
        }
        return cqlSession;
    }
    
    /**
     * Builder Pattern
     * @return StargateClientBuilder
     */
    public static StargateClientBuilder builder() {
        return new StargateClientBuilder();
    }
    
    // ------------------------------------------------
    // ---------------- Utilities   -------------------
    // ------------------------------------------------
    
    /** {@inheritDoc} */
    @Override
    public void close() {
        if (null != cqlSession && !cqlSession.isClosed()) {
            cqlSession.close();
            LOGGER.info("Closing CqlSession.");
        }
    }
    
    // ------------------------------------------------
    // ---------------- Accessors   -------------------
    // ------------------------------------------------
    
    /**
     * Accessing Cql Session.
     * @return CqlSession
     */
    public Optional cqlSession() {
        return Optional.ofNullable(cqlSession);
    }

    /**
     * Retrieve API GraphQL, doing load balancing, failover and retries.
     *
     * @return
     *      Api graphQL client
     */
    public DataApiClient apiData() {
        if (apiDataClient == null) {
            throw new IllegalStateException("Json Api is not available please provide a service deployment for Json");
        }
        return this.apiDataClient;
    }
    
    /**
     * Retrieve API Document, doing load balancing, failover and retries.
     *
     * @return
     *      current API Document
     */
    public StargateDocumentApiClient apiDocument() {
        if (apiDocumentClient == null) {
            throw new IllegalStateException("Document Api is not available please provide a service deployment for Documentxs");
        }
       return this.apiDocumentClient;
    }
    
    /**
     * Retrieve API Data, doing load balancing, failover and retries.
     *
     * @return
     *      Api REST DATA client
     */
    public StargateRestApiClient apiRest() {
        if (apiRestClient == null) {
            throw new IllegalStateException("REST Data Api is not available please provide a service deployment for Rest Data");
        }
        return this.apiRestClient;
    }
    
    /**
     * Retrieve API GraphQL, doing load balancing, failover and retries.
     * 
     * @return
     *      Api graphQL client
     */
    public StargateGraphQLApiClient apiGraphQL() {
        if (apiGraphQLClient == null) {
            throw new IllegalStateException("GraphQL Api is not available please provide a service deployment for GraphQL");
        }
        return this.apiGraphQLClient;
    }
    
    /**
     * Retrieve items using the gRPC interface
     * 
     * @return
     *      grpc interface
     */
    public StargateGrpcApiClient apiGrpc() {
        if (apiGrpcClient == null) {
            throw new IllegalStateException("GRPC Api is not available please provide a service deployment for gRPC");
        }
        return this.apiGrpcClient;
    }

    /**
     * Set value for currentDatacenter
     *
     * @param currentDatacenter new value for currentDatacenter
     */
    public void setCurrentDatacenter(String currentDatacenter) {
        this.currentDatacenter = currentDatacenter;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy