com.datastax.driver.dse.DseCluster Maven / Gradle / Ivy
/*
* Copyright DataStax, Inc.
*
* This software can be used solely with DataStax Enterprise. Please consult the license at
* http://www.datastax.com/terms/datastax-dse-driver-license-terms
*/
package com.datastax.driver.dse;
import static com.google.common.base.Preconditions.checkArgument;
import com.datastax.driver.core.AuthProvider;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.DelegatingCluster;
import com.datastax.driver.core.EndPointFactory;
import com.datastax.driver.core.Host;
import com.datastax.driver.core.NettyOptions;
import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.ProtocolOptions;
import com.datastax.driver.core.ProtocolVersion;
import com.datastax.driver.core.QueryOptions;
import com.datastax.driver.core.SSLOptions;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SocketOptions;
import com.datastax.driver.core.ThreadingOptions;
import com.datastax.driver.core.TimestampGenerator;
import com.datastax.driver.core.TypeCodec;
import com.datastax.driver.core.exceptions.AuthenticationException;
import com.datastax.driver.core.exceptions.CodecNotFoundException;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import com.datastax.driver.core.policies.AddressTranslator;
import com.datastax.driver.core.policies.LoadBalancingPolicy;
import com.datastax.driver.core.policies.Policies;
import com.datastax.driver.core.policies.ReconnectionPolicy;
import com.datastax.driver.core.policies.RetryPolicy;
import com.datastax.driver.core.policies.SpeculativeExecutionPolicy;
import com.datastax.driver.dse.auth.DsePlainTextAuthProvider;
import com.datastax.driver.dse.geometry.codecs.LineStringCodec;
import com.datastax.driver.dse.geometry.codecs.PointCodec;
import com.datastax.driver.dse.geometry.codecs.PolygonCodec;
import com.datastax.driver.dse.graph.GraphOptions;
import com.datastax.driver.dse.graph.GraphStatement;
import com.datastax.driver.dse.search.DateRangeCodec;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.Future;
/**
* Information and known state of a DSE cluster.
*
* This is the main entry point of the DSE driver. It extends the CQL driver's {@link Cluster}
* object with DSE-specific features. A simple example of access to a DSE cluster would be:
*
*
* DseCluster cluster = DseCluster.builder().addContactPoint("192.168.0.1").build();
* DseSession session = cluster.connect("db1");
*
* for (Row row : session.execute("SELECT * FROM table1"))
* // do something ...
*
*/
public class DseCluster extends DelegatingCluster {
/**
* Returns the current version of the DSE driver.
*
* This is intended for products that wrap or extend the driver, as a way to check
* compatibility if end-users override the driver version in their application.
*
*
This method is an alias for {@link Cluster#getDriverVersion()}; both return exactly the same
* version.
*
* @return the DSE driver version.
*/
public static String getDseDriverVersion() {
return Cluster.getDriverVersion();
}
private final Cluster delegate;
/** Helper class to build {@link DseCluster} instances. */
public static class Builder extends Cluster.Builder {
private GraphOptions graphOptions;
private boolean geospatialCodecs = true;
private boolean searchCodecs = true;
private String applicationName = null;
private String applicationVersion = null;
private UUID id = null;
public Builder() {
this.withLoadBalancingPolicy(DseConfiguration.defaultLoadBalancingPolicy());
}
/**
* Sets the default options to use with graph queries.
*
*
If you don't provide this, the new cluster will get a {@link GraphOptions} instance with
* the default values.
*
* @param graphOptions the graph options.
* @return this builder (for method chaining).
*/
public DseCluster.Builder withGraphOptions(GraphOptions graphOptions) {
this.graphOptions = graphOptions;
return this;
}
/**
* Prevents the registration of {@link com.datastax.driver.dse.geometry.codecs geospatial
* codecs} with the new cluster.
*
*
If this method is not called, those codecs will be registered by default.
*
* @return this builder (for method chaining).
* @see CodecRegistry
* @see TypeCodec
*/
public DseCluster.Builder withoutGeospatialCodecs() {
this.geospatialCodecs = false;
return this;
}
/**
* Prevents the registration of search codecs with the new cluster.
*
*
Currently only {@link DateRangeCodec} is affected by this method. If this method is not
* called, this codec will be registered by default.
*
* @return this builder (for method chaining).
* @see CodecRegistry
* @see TypeCodec
*/
public DseCluster.Builder withoutSearchCodecs() {
this.searchCodecs = false;
return this;
}
@Override
public DseCluster.Builder withClusterName(String name) {
return (DseCluster.Builder) super.withClusterName(name);
}
/**
* An optional configuration for providing a unique identifier for the created client instance.
*
*
If not provided, an id will generated.
*
*
This value is passed to DSE and is useful as metadata for describing a client connection.
*
* @param id the id to assign to this client instance.
* @return this Builder.
* @deprecated Use {@link #withClientId(UUID)}} instead. This method was intended to give a
* client id not cluster id from the beginning. The withClientId name is more appropriate.
*/
@Deprecated
public DseCluster.Builder withClusterId(UUID id) {
withClientId(id);
return this;
}
/**
* An optional configuration for providing a unique identifier for the created client instance.
*
*
If not provided, an id will generated.
*
*
This value is passed to DSE and is useful as metadata for describing a client connection.
*
* @param id the id to assign to this client instance.
* @return this Builder.
*/
public DseCluster.Builder withClientId(UUID id) {
this.id = id;
return this;
}
/**
* An optional configuration identifying the name of the application using this cluster.
*
*
This value is passed to DSE and is useful as metadata for describing a client connection.
*
* @param name the name of the application using this cluster.
* @return this Builder.
*/
public DseCluster.Builder withApplicationName(String name) {
this.applicationName = name;
return this;
}
/**
* An optional configuration identifying the version of the application using this cluster.
*
*
This value is to passed to DSE and is useful as metadata for describing a client
* connection.
*
* @param version the version of the application using this cluster.
* @return this Builder.
*/
public DseCluster.Builder withApplicationVersion(String version) {
this.applicationVersion = version;
return this;
}
@Override
public DseCluster.Builder withPort(int port) {
return (DseCluster.Builder) super.withPort(port);
}
@Override
public DseCluster.Builder allowBetaProtocolVersion() {
return (DseCluster.Builder) super.allowBetaProtocolVersion();
}
@Override
public DseCluster.Builder withMaxSchemaAgreementWaitSeconds(int maxSchemaAgreementWaitSeconds) {
return (DseCluster.Builder)
super.withMaxSchemaAgreementWaitSeconds(maxSchemaAgreementWaitSeconds);
}
@Override
public DseCluster.Builder withProtocolVersion(ProtocolVersion version) {
return (DseCluster.Builder) super.withProtocolVersion(version);
}
@Override
public Builder withMonitorReporting(boolean enabled) {
return (DseCluster.Builder) super.withMonitorReporting(enabled);
}
@Override
public DseCluster.Builder addContactPoint(String address) {
return (Builder) super.addContactPoint(address);
}
@Override
public DseCluster.Builder addContactPoints(String... addresses) {
return (Builder) super.addContactPoints(addresses);
}
@Override
public DseCluster.Builder addContactPoints(InetAddress... addresses) {
return (Builder) super.addContactPoints(addresses);
}
@Override
public DseCluster.Builder addContactPoints(Collection addresses) {
return (Builder) super.addContactPoints(addresses);
}
@Override
public DseCluster.Builder addContactPointsWithPorts(InetSocketAddress... addresses) {
return (DseCluster.Builder) super.addContactPointsWithPorts(addresses);
}
@Override
public DseCluster.Builder addContactPointsWithPorts(Collection addresses) {
return (Builder) super.addContactPointsWithPorts(addresses);
}
/**
* Configures the load balancing policy to use for the new DSE cluster.
*
* If you use graph analytics queries, it should be wrapped into a {@link
* DseLoadBalancingPolicy} to ensure that queries are routed optimally.
*
*
If no load balancing policy is set through this method, the default is to use a {@link
* Policies#defaultLoadBalancingPolicy()} wrapped into a {@code TargetingLoadBalancingPolicy}.
*
* @param policy the load balancing policy to use.
* @return this builder.
*/
@Override
public DseCluster.Builder withLoadBalancingPolicy(LoadBalancingPolicy policy) {
return (DseCluster.Builder) super.withLoadBalancingPolicy(policy);
}
@Override
public DseCluster.Builder withReconnectionPolicy(ReconnectionPolicy policy) {
return (DseCluster.Builder) super.withReconnectionPolicy(policy);
}
@Override
public DseCluster.Builder withRetryPolicy(RetryPolicy policy) {
return (DseCluster.Builder) super.withRetryPolicy(policy);
}
@Override
public DseCluster.Builder withAddressTranslator(AddressTranslator translator) {
return (DseCluster.Builder) super.withAddressTranslator(translator);
}
@Override
public DseCluster.Builder withTimestampGenerator(TimestampGenerator timestampGenerator) {
return (DseCluster.Builder) super.withTimestampGenerator(timestampGenerator);
}
@Override
public DseCluster.Builder withSpeculativeExecutionPolicy(SpeculativeExecutionPolicy policy) {
return (DseCluster.Builder) super.withSpeculativeExecutionPolicy(policy);
}
@Override
public DseCluster.Builder withCodecRegistry(CodecRegistry codecRegistry) {
return (DseCluster.Builder) super.withCodecRegistry(codecRegistry);
}
@Override
public DseCluster.Builder withCredentials(String username, String password) {
return withAuthProvider(new DsePlainTextAuthProvider(username, password));
}
@Override
public DseCluster.Builder withAuthProvider(AuthProvider authProvider) {
return (DseCluster.Builder) super.withAuthProvider(authProvider);
}
@Override
public DseCluster.Builder withCompression(ProtocolOptions.Compression compression) {
return (DseCluster.Builder) super.withCompression(compression);
}
@Override
public DseCluster.Builder withoutMetrics() {
return (DseCluster.Builder) super.withoutMetrics();
}
@Override
public DseCluster.Builder withSSL() {
return (DseCluster.Builder) super.withSSL();
}
@Override
public DseCluster.Builder withSSL(SSLOptions sslOptions) {
return (DseCluster.Builder) super.withSSL(sslOptions);
}
@Override
public DseCluster.Builder withInitialListeners(Collection listeners) {
return (DseCluster.Builder) super.withInitialListeners(listeners);
}
@Override
public DseCluster.Builder withoutJMXReporting() {
return (DseCluster.Builder) super.withoutJMXReporting();
}
@Override
public DseCluster.Builder withPoolingOptions(PoolingOptions options) {
return (DseCluster.Builder) super.withPoolingOptions(options);
}
@Override
public DseCluster.Builder withSocketOptions(SocketOptions options) {
return (DseCluster.Builder) super.withSocketOptions(options);
}
@Override
public DseCluster.Builder withQueryOptions(QueryOptions options) {
return (DseCluster.Builder) super.withQueryOptions(options);
}
@Override
public DseCluster.Builder withThreadingOptions(ThreadingOptions options) {
return (DseCluster.Builder) super.withThreadingOptions(options);
}
@Override
public DseCluster.Builder withNettyOptions(NettyOptions nettyOptions) {
return (DseCluster.Builder) super.withNettyOptions(nettyOptions);
}
@Override
public DseCluster.Builder withNoCompact() {
return (DseCluster.Builder) super.withNoCompact();
}
@Override
public DseCluster.Builder withEndPointFactory(EndPointFactory endPointFactory) {
return (DseCluster.Builder) super.withEndPointFactory(endPointFactory);
}
@Override
public DseCluster.Builder withCloudSecureConnectBundle(File cloudConfigFile) {
return (DseCluster.Builder) super.withCloudSecureConnectBundle(cloudConfigFile);
}
@Override
public DseCluster.Builder withCloudSecureConnectBundle(URL cloudConfigUrl) {
return (DseCluster.Builder) super.withCloudSecureConnectBundle(cloudConfigUrl);
}
@Override
public DseCluster.Builder withCloudSecureConnectBundle(InputStream cloudConfigInputStream) {
return (DseCluster.Builder) super.withCloudSecureConnectBundle(cloudConfigInputStream);
}
@Override
public DseCluster build() {
DseCluster dseCluster = new DseCluster(super.build());
CodecRegistry codecRegistry = dseCluster.getConfiguration().getCodecRegistry();
if (geospatialCodecs) {
registerGeospatialCodecs(codecRegistry);
}
if (searchCodecs) {
registerSearchCodecs(codecRegistry);
}
return dseCluster;
}
@Override
public DseConfiguration getConfiguration() {
return new DseConfiguration(
super.getConfiguration(),
graphOptions != null ? graphOptions : new GraphOptions(),
applicationName,
applicationVersion,
id);
}
private static void registerGeospatialCodecs(CodecRegistry codecRegistry) {
registerCodecIfNotPresent(codecRegistry, LineStringCodec.INSTANCE);
registerCodecIfNotPresent(codecRegistry, PointCodec.INSTANCE);
registerCodecIfNotPresent(codecRegistry, PolygonCodec.INSTANCE);
}
private static void registerSearchCodecs(CodecRegistry codecRegistry) {
registerCodecIfNotPresent(codecRegistry, DateRangeCodec.INSTANCE);
}
private static void registerCodecIfNotPresent(CodecRegistry codecRegistry, TypeCodec> codec) {
try {
codecRegistry.codecFor(codec.getCqlType(), codec.getJavaType());
} catch (CodecNotFoundException e) {
codecRegistry.register(codec);
}
}
}
/**
* Creates a new {@link DseCluster.Builder} instance.
*
* This is a convenience method for {@code new GraphCluster.Builder()}.
*
* @return the new cluster builder.
*/
public static DseCluster.Builder builder() {
return new DseCluster.Builder();
}
private DseCluster(Cluster delegate) {
checkArgument(
delegate.getConfiguration() instanceof DseConfiguration,
"Cannot create a GraphCluster without a GraphConfiguration");
this.delegate = delegate;
}
@Override
public DseConfiguration getConfiguration() {
return (DseConfiguration) super.getConfiguration();
}
@Override
protected Cluster delegate() {
return delegate;
}
/**
* Creates a new DSE session on this cluster but does not initialize it.
*
*
Because this method does not perform any initialization, it cannot fail. The initialization
* of the session (the connection of the session to the Cassandra nodes) will occur if either the
* {@link DseSession#init} method is called explicitly, or whenever the returned object is used.
*
*
Once a session returned by this method gets initialized (see above), it will be set to no
* keyspace. If you want to set such session to a keyspace, you will have to explicitly execute a
* 'USE mykeyspace' query.
*
*
Note that if you do not particularly need to defer initialization, it is simpler to use one
* of the {@code connect()} methods of this class.
*
* @return a new, non-initialized DSE session on this cluster.
*/
@Override
public DseSession newSession() {
return new DefaultDseSession(super.newSession(), this);
}
/**
* Creates a new DSE session on this cluster and initializes it.
*
*
This method will initialize the newly created session, trying to connect to the Cassandra
* nodes before returning. If you only want to create a session without initializing it right
* away, see {@link #newSession}.
*
* @return a new session on this cluster set to no keyspace.
* @throws NoHostAvailableException if the Cluster has not been initialized yet ({@link #init} has
* not be called and this is the first connect call) and no host amongst the contact points
* can be reached.
* @throws AuthenticationException if an authentication error occurs while contacting the initial
* contact points.
* @throws IllegalStateException if the Cluster was closed prior to calling this method. This can
* occur either directly (through {@link #close()} or {@link #closeAsync()}), or as a result
* of an error while initializing the Cluster.
*/
@Override
public DseSession connect() {
return new DefaultDseSession(super.connect(), this);
}
/**
* Creates a new DSE session on this cluster, initializes it and sets the keyspace to the provided
* one.
*
*
This method will initialize the newly created session, trying to connect to the Cassandra
* nodes before returning. If you only want to create a session without initializing it right
* away, see {@link #newSession}.
*
*
The keyspace set through this method is only valid for CQL queries; for graph queries, the
* graph to target is determined by the graph name specified via {@link
* GraphStatement#setGraphName(String)}} or {@link GraphOptions#setGraphName(String)}.
*
* @param keyspace the name of the keyspace to use for the created {@link DseSession}.
* @return a new session on this cluster set to keyspace {@code keyspace}.
* @throws NoHostAvailableException if the Cluster has not been initialized yet ({@link #init} has
* not be called and this is the first connect call) and no host amongst the contact points
* can be reached, or if no host can be contacted to set the {@code keyspace}.
* @throws AuthenticationException if an authentication error occurs while contacting the initial
* contact points.
* @throws InvalidQueryException if the keyspace does not exist.
* @throws IllegalStateException if the Cluster was closed prior to calling this method. This can
* occur either directly (through {@link #close()} or {@link #closeAsync()}), or as a result
* of an error while initializing the Cluster.
*/
@Override
public DseSession connect(String keyspace) {
return new DefaultDseSession(super.connect(keyspace), this);
}
/**
* Creates a new DSE session on this cluster and initializes it asynchronously.
*
*
This will also initialize the {@code Cluster} if needed; note that cluster initialization
* happens synchronously on the thread that called this method. Therefore it is recommended to
* initialize the cluster at application startup, and not rely on this method to do it.
*
*
The {@link Session} object returned by the future's {@link Future#get() get} method can be
* safely cast to {@link DseSession}.
*
* @return a future that will complete when the session is fully initialized.
* @throws NoHostAvailableException if the Cluster has not been initialized yet ({@link #init} has
* not been called and this is the first connect call) and no host amongst the contact points
* can be reached.
* @throws IllegalStateException if the Cluster was closed prior to calling this method. This can
* occur either directly (through {@link #close()} or {@link #closeAsync()}), or as a result
* of an error while initializing the Cluster.
* @see #connect()
*/
@Override
public ListenableFuture connectAsync() {
return Futures.transform(
super.connectAsync(),
new Function() {
@Override
public Session apply(Session input) {
return new DefaultDseSession(input, DseCluster.this);
}
});
}
/**
* Creates a new DSE session on this cluster, and initializes it to the given keyspace
* asynchronously.
*
* This will also initialize the {@code Cluster} if needed; note that cluster initialization
* happens synchronously on the thread that called this method. Therefore it is recommended to
* initialize the cluster at application startup, and not rely on this method to do it.
*
*
The keyspace set through this method is only valid for CQL queries; for graph queries, the
* graph to target is determined by the graph name specified via {@link
* GraphStatement#setGraphName(String)}} or {@link GraphOptions#setGraphName(String)}.
*
*
The {@link Session} object returned by the future's {@link Future#get() get} method can be
* safely cast to {@link DseSession}.
*
* @param keyspace The name of the keyspace to use for the created {@code Session}.
* @return a future that will complete when the session is fully initialized.
* @throws NoHostAvailableException if the Cluster has not been initialized yet ({@link #init} has
* not been called and this is the first connect call) and no host amongst the contact points
* can be reached.
* @throws IllegalStateException if the Cluster was closed prior to calling this method. This can
* occur either directly (through {@link #close()} or {@link #closeAsync()}), or as a result
* of an error while initializing the Cluster.
*/
@Override
public ListenableFuture connectAsync(String keyspace) {
return Futures.transform(
super.connectAsync(keyspace),
new Function() {
@Override
public Session apply(Session input) {
return new DefaultDseSession(input, DseCluster.this);
}
});
}
}