package me.prettyprint.cassandra.service;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import me.prettyprint.cassandra.connection.*;
import me.prettyprint.cassandra.connection.factory.HClientFactory;
import me.prettyprint.cassandra.connection.factory.HThriftClientFactoryImpl;
import me.prettyprint.hector.api.ClockResolution;
import me.prettyprint.hector.api.factory.HFactory;
import org.apache.commons.lang.StringUtils;
public final class CassandraHostConfigurator implements Serializable {
// update this if you make changes
private static final long serialVersionUID = -5798876803582956262L;
public static final ClockResolution DEF_CLOCK_RESOLUTION = HFactory.createClockResolution(ClockResolution.MICROSECONDS_SYNC);
private static ClockResolution clockResolution = DEF_CLOCK_RESOLUTION;
private String hosts;
private int port = CassandraHost.DEFAULT_PORT;
private int maxActive = CassandraHost.DEFAULT_MAX_ACTIVE;
private boolean lifo = CassandraHost.DEFAULT_LIFO;
private long maxWaitTimeWhenExhausted = CassandraHost.DEFAULT_MAX_WAITTIME_WHEN_EXHAUSTED;
private long maxExhaustedTimeBeforeMarkingAsDown = CassandraHost.DEFAULT_MAX_EXHAUSTED_TIME_BEFORE_MARKING_AS_DOWN;
private int cassandraThriftSocketTimeout;
private boolean useThriftFramedTransport = CassandraHost.DEFAULT_USE_FRAMED_THRIFT_TRANSPORT;
private int maxFrameSize = CassandraHost.DEFAULT_MAX_FRAME_SIZE;
private boolean retryDownedHosts = true;
private int retryDownedHostsQueueSize = CassandraHostRetryService.DEF_QUEUE_SIZE;
private int retryDownedHostsDelayInSeconds = CassandraHostRetryService.DEF_RETRY_DELAY;
private boolean autoDiscoverHosts = false;
private int autoDiscoveryDelayInSeconds = NodeAutoDiscoverService.DEF_AUTO_DISCOVERY_DELAY;
private List<String> autoDiscoveryDataCenters;
private LoadBalancingPolicy loadBalancingPolicy = new RoundRobinBalancingPolicy();
private int hostTimeoutCounter = HostTimeoutTracker.DEF_TIMEOUT_COUNTER;
private int hostTimeoutWindow = HostTimeoutTracker.DEF_TIMEOUT_WINDOW;
private int hostTimeoutSuspensionDurationInSeconds = HostTimeoutTracker.DEF_NODE_SUSPENSION_DURATION_IN_SECONDS;
private int hostTimeoutUnsuspendCheckDelay = HostTimeoutTracker.DEF_NODE_UNSUSPEND_CHECK_DELAY_IN_SECONDS;
private boolean useHostTimeoutTracker = false;
private boolean runAutoDiscoveryAtStartup = false;
private boolean useSocketKeepalive = false;
private HOpTimer opTimer = new NullOpTimer();
private Class<? extends HClientFactory> clientFactoryClass = HThriftClientFactoryImpl.class;
private long maxConnectTimeMillis = CassandraHost.DEFAULT_MAX_CONNECT_TIME;
private long maxLastSuccessTimeMillis = CassandraHost.DEFAULT_MAX_LAST_SUCCESS_TIME;
public CassandraHostConfigurator() {
this.hosts = null;
}
/**
* Creates a new {@code CassandraHostConfigurator} from the specified hosts String, formatted as
* {@code host[:port][,host[:port]...]}.
* @param hosts The hosts to create {@link CassandraHost}s from.
*/
public CassandraHostConfigurator(String hosts) {
this.hosts = hosts;
}
public CassandraHost[] buildCassandraHosts() {
if (this.hosts == null) {
throw new IllegalArgumentException("Need to define at least one host in order to apply configuration.");
}
String[] hostVals = hosts.split(",");
CassandraHost[] cassandraHosts = new CassandraHost[hostVals.length];
for (int x=0; x<hostVals.length; x++) {
CassandraHost cassandraHost = this.port == CassandraHost.DEFAULT_PORT ? new CassandraHost(hostVals[x].trim()) : new CassandraHost(hostVals[x], this.port);
applyConfig(cassandraHost);
cassandraHosts[x] = cassandraHost;
}
return cassandraHosts;
}
public void applyConfig(CassandraHost cassandraHost) {
cassandraHost.setMaxActive(maxActive);
cassandraHost.setLifo(lifo);
cassandraHost.setMaxWaitTimeWhenExhausted(maxWaitTimeWhenExhausted);
cassandraHost.setMaxExhaustedTimeBeforeMarkingAsDown(maxExhaustedTimeBeforeMarkingAsDown);
cassandraHost.setUseThriftFramedTransport(useThriftFramedTransport);
cassandraHost.setMaxFrameSize(maxFrameSize);
cassandraHost.setUseSocketKeepalive(useSocketKeepalive);
cassandraHost.setMaxConnectTimeMillis(maxConnectTimeMillis);
cassandraHost.setMaxLastSuccessTimeMillis(maxLastSuccessTimeMillis);
// this is special as it can be passed in as a system property
if (cassandraThriftSocketTimeout > 0) {
cassandraHost.setCassandraThriftSocketTimeout(cassandraThriftSocketTimeout);
}
}
/**
* Specifies the hosts String, formatted as
* {@code host[:port][,host[:port]...]}.
* @param hosts The hosts to create {@link CassandraHost}s from.
*/
public void setHosts(String hosts) {
this.hosts = hosts;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public void setMaxWaitTimeWhenExhausted(long maxWaitTimeWhenExhausted) {
this.maxWaitTimeWhenExhausted = maxWaitTimeWhenExhausted;
}
public void setMaxExhaustedTimeBeforeMarkingAsDown(long maxExhaustedTimeBeforeMarkingAsDown) {
this.maxExhaustedTimeBeforeMarkingAsDown = maxExhaustedTimeBeforeMarkingAsDown;
}
/**
* The value (in milliseconds) which gets passed down to {@link java.net.Socket#setSoTimeout(int)}
* used by the underlying Thrift transport.
*/
public void setCassandraThriftSocketTimeout(int cassandraThriftSocketTimeout) {
this.cassandraThriftSocketTimeout = cassandraThriftSocketTimeout;
}
public boolean getRetryDownedHosts() {
return this.retryDownedHosts;
}
public void setRetryDownedHosts(boolean retryDownedHosts) {
this.retryDownedHosts = retryDownedHosts;
}
public void setRetryDownedHostsQueueSize(int retryDownedHostsQueueSize) {
this.retryDownedHostsQueueSize = retryDownedHostsQueueSize;
}
public int getRetryDownedHostsQueueSize() {
return retryDownedHostsQueueSize;
}
public void setRetryDownedHostsDelayInSeconds(int retryDownedHostsDelayInSeconds) {
this.retryDownedHostsDelayInSeconds = retryDownedHostsDelayInSeconds;
}
public int getRetryDownedHostsDelayInSeconds() {
return retryDownedHostsDelayInSeconds;
}
/**
* Sets this Clock for all clusters administered by Hector.
* Notice this is a class method and ideally should be called from the CHC class just once.
*
* @param resolutionString one of "SECONDS", "MILLISECONDS", "MICROSECONDS" or "MICROSECONDS_SYNC"
*/
public static void setClockResolution(String resolutionString) {
clockResolution = HFactory.createClockResolution(resolutionString);
}
public HOpTimer getOpTimer() {
return opTimer;
}
public void setOpTimer(HOpTimer opTimer) {
this.opTimer = opTimer;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("CassandraHostConfigurator<");
s.append("clockResolution=");
s.append(clockResolution);
s.append("&cassandraThriftSocketTimeout=");
s.append(cassandraThriftSocketTimeout);
s.append("&maxWaitTimeWhenExhausted=");
s.append(maxWaitTimeWhenExhausted);
s.append("&maxExhaustedTimeBeforeMarkingAsDown=");
s.append(maxExhaustedTimeBeforeMarkingAsDown);
s.append("&maxActive=");
s.append(maxActive);
s.append("&hosts=");
s.append(hosts);
s.append("&useThriftFramedTransport=");
s.append(useThriftFramedTransport);
s.append("&maxFrameSize=");
s.append(maxFrameSize);
s.append("&retryDownedHosts=");
s.append(retryDownedHosts);
s.append("&opTimer=");
s.append(opTimer);
s.append(">");
return s.toString();
}
public boolean getLifo() {
return lifo;
}
public void setLifo(boolean lifo) {
this.lifo = lifo;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public void setUseThriftFramedTransport(boolean useThriftFramedTransport) {
this.useThriftFramedTransport = useThriftFramedTransport;
}
public void setMaxFrameSize(int maxFrameSize) {
this.maxFrameSize = maxFrameSize;
}
public static ClockResolution getClockResolution() {
return CassandraHostConfigurator.clockResolution;
}
/**
* Sets this Clock for all clusters administered by Hector.
* Notice this is a class method and ideally should be called from the CHC class just once.
* @param clockResolution a specific ClockResolution
*/
public static void setClockResolution(ClockResolution clockResolution) {
CassandraHostConfigurator.clockResolution = clockResolution;
}
public boolean getAutoDiscoverHosts() {
return autoDiscoverHosts;
}
public void setAutoDiscoverHosts(boolean autoDiscoverHosts) {
this.autoDiscoverHosts = autoDiscoverHosts;
}
public int getAutoDiscoveryDelayInSeconds() {
return autoDiscoveryDelayInSeconds;
}
public void setAutoDiscoveryDelayInSeconds(int autoDiscoveryDelayInSeconds) {
this.autoDiscoveryDelayInSeconds = autoDiscoveryDelayInSeconds;
}
/**
* Sets the local datacenter for the DiscoveryService. Nodes out of this
* datacenter will be discarded. For configuration simplicity, you can provide
* an empty or null string with the effect being the same as if you had not set
* this property.
* @param dataCenter DataCenter name
*/
public void setAutoDiscoveryDataCenter(String dataCenter) {
if (StringUtils.isNotBlank(dataCenter)) {
this.autoDiscoveryDataCenters = Arrays.asList(dataCenter);
}
}
/**
* Sets the datacenters for the DiscoveryService. Nodes out of these
* datacenters will be discarded.
*/
public void setAutoDiscoveryDataCenter(List<String> dataCenters) {
this.autoDiscoveryDataCenters = dataCenters;
}
/**
* Retrieves the 'local' datacenter names that the DiscoveryService recognizes as valid
* in order to discover new hosts.
* @return a list of 'local' datacenter names
*/
public List<String> getAutoDiscoveryDataCenters() {
return autoDiscoveryDataCenters;
}
public LoadBalancingPolicy getLoadBalancingPolicy() {
return loadBalancingPolicy;
}
public void setLoadBalancingPolicy(LoadBalancingPolicy loadBalancingPolicy) {
this.loadBalancingPolicy = loadBalancingPolicy;
}
public int getHostTimeoutCounter() {
return hostTimeoutCounter;
}
public void setHostTimeoutCounter(int hostTimeoutCounter) {
this.hostTimeoutCounter = hostTimeoutCounter;
}
public int getHostTimeoutWindow() {
return hostTimeoutWindow;
}
public void setHostTimeoutWindow(int hostTimeoutWindow) {
this.hostTimeoutWindow = hostTimeoutWindow;
}
public int getHostTimeoutSuspensionDurationInSeconds() {
return hostTimeoutSuspensionDurationInSeconds;
}
public void setHostTimeoutSuspensionDurationInSeconds(int hostTimeoutSuspensionDurationInSeconds) {
this.hostTimeoutSuspensionDurationInSeconds = hostTimeoutSuspensionDurationInSeconds;
}
public int getHostTimeoutUnsuspendCheckDelay() {
return hostTimeoutUnsuspendCheckDelay;
}
public void setHostTimeoutUnsuspendCheckDelay(int hostTimeoutUnsuspendCheckDelay) {
this.hostTimeoutUnsuspendCheckDelay = hostTimeoutUnsuspendCheckDelay;
}
public boolean getUseHostTimeoutTracker() {
return useHostTimeoutTracker;
}
public void setUseHostTimeoutTracker(boolean useHostTimeoutTracker) {
this.useHostTimeoutTracker = useHostTimeoutTracker;
}
public boolean getRunAutoDiscoveryAtStartup() {
return runAutoDiscoveryAtStartup;
}
/**
* Set to true to run {@link NodeAutoDiscoverService} at startup.
* You must also call {@link CassandraHostConfigurator#setAutoDiscoverHosts(boolean)}
* to true for this to have an effect.
* @param runAutoDiscoveryAtStartup
*/
public void setRunAutoDiscoveryAtStartup(boolean runAutoDiscoveryAtStartup) {
this.runAutoDiscoveryAtStartup = runAutoDiscoveryAtStartup;
}
public boolean getUseSocketKeepalive() {
return useSocketKeepalive;
}
/**
* Enable SO_KEEPALIVE on the underlying socket. OFF by default (per java.net.Socket).<br/>
* Enabling the socket keepalive is the usual way to work around a firewall problem
* (that is, a firewall that cuts idle connections between the Hector's host and the Cassandra
* nodes, Hector being unaware of these cuts). To do so, this method must be used
* in accordance with the appropriate "tcp_keepalive" settings of the machine that runs Hector,
* to deal with the firewall own settings (that is, how long a connection should be detected
* idle before the firewall cuts it).
*/
public void setUseSocketKeepalive(boolean useSocketKeepalive) {
this.useSocketKeepalive = useSocketKeepalive;
}
public void setClientFactoryClass(String cls) {
String className = cls.contains(".") ? cls : "me.prettyprint.cassandra.connection.factory." + cls;
Class<? extends HClientFactory> temp;
try {
temp = Class.forName(className).asSubclass(HClientFactory.class);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(String.format("Unable to find '%s' class.", className), e);
} catch (ClassCastException e) {
throw new IllegalArgumentException("ClientFactoryClass should be extended from HClientFactory.", e);
}
clientFactoryClass = temp;
}
public Class<? extends HClientFactory> getClientFactoryClass() {
return clientFactoryClass;
}
/**
* The maximum time in milliseconds that we'll allow a connection to stay open to a host. A negative
* value indicates indefinitely (and is the default).
*
* @return the number of milliseconds
*/
public long getMaxConnectTimeMillis() {
return maxConnectTimeMillis;
}
/**
* Set the maximum time in milliseconds that we'll allow a connection to stay open to a host. A negative
* value indicates indefinitely. This setting is useful if you you need to work around a firewall that
* forcefully closes connections after a fixed amount of time regardless of activity.
*
* @param maxConnectTimeMillis the maximum time to use a connection
*/
public void setMaxConnectTimeMillis(long maxConnectTimeMillis) {
this.maxConnectTimeMillis = maxConnectTimeMillis;
}
/**
* The maximum time in milliseconds that we'll allow a connection to stay idle to a host. A negative
* value indicates indefinitely (and is the default).
*
* @return the number of milliseconds
*/
public long getMaxLastSuccessTimeMillis() {
return this.maxLastSuccessTimeMillis;
}
/**
* Set the maximum time in milliseconds that we'll allow a connection to stay idle to a host. A negative
* value indicates indefinitely. <br/>
* This setting is useful if you you need to work around a firewall that forcefully closes connections
* after a fixed amount of idle time. Example: if your firewall cuts connections after an idle time
* of 30 mn, one could set this property with the duration 29 mn 30s to have a margin. <br/>
* But before using <code>setMaxLastSuccessTimeMillis</code>, the first way, that is, the most used way,
* to try to work around a firewall problem should be to use the socket "keepalive" mechanism, see
* <code>setUseSocketKeepalive</code> method. The current method <code>setMaxLastSuccessTimeMillis</code>
* is generally used for dealing with firewalls when the "keepalive" mechanism could not be used.
*
* @param maxLastSuccessTimeMillis the maximum idle time for a connection
*/
public void setMaxLastSuccessTimeMillis(long maxLastSuccessTimeMillis) {
this.maxLastSuccessTimeMillis = maxLastSuccessTimeMillis;
}
}