/**
* Copyright 2014 Impetus Infotech.
*
* 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.impetus.kundera.client.cassandra.dsdriver;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Cluster.Builder;
import com.datastax.driver.core.HostDistance;
import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.ProtocolOptions.Compression;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SocketOptions;
import com.datastax.driver.core.policies.ConstantReconnectionPolicy;
import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy;
import com.datastax.driver.core.policies.DowngradingConsistencyRetryPolicy;
import com.datastax.driver.core.policies.ExponentialReconnectionPolicy;
import com.datastax.driver.core.policies.FallthroughRetryPolicy;
import com.datastax.driver.core.policies.LatencyAwarePolicy;
import com.datastax.driver.core.policies.LoadBalancingPolicy;
import com.datastax.driver.core.policies.LoggingRetryPolicy;
import com.datastax.driver.core.policies.RoundRobinPolicy;
import com.datastax.driver.core.policies.TokenAwarePolicy;
import com.impetus.client.cassandra.common.CassandraClientFactory;
import com.impetus.client.cassandra.common.CassandraConstants;
import com.impetus.client.cassandra.config.CassandraPropertyReader;
import com.impetus.client.cassandra.query.CassandraEntityReader;
import com.impetus.client.cassandra.schemamanager.CassandraSchemaManager;
import com.impetus.client.cassandra.service.CassandraHost;
import com.impetus.client.cassandra.service.CassandraHostConfiguration;
import com.impetus.kundera.Constants;
import com.impetus.kundera.PersistenceProperties;
import com.impetus.kundera.client.Client;
import com.impetus.kundera.configure.schema.api.SchemaManager;
import com.impetus.kundera.metadata.model.PersistenceUnitMetadata;
import com.impetus.kundera.service.Host;
import com.impetus.kundera.utils.InvalidConfigurationException;
/**
* Data stax java driver client factory.
*
* @author vivek.mishra
*
*/
public class DSClientFactory extends CassandraClientFactory
{
/** The logger. */
private static Logger logger = LoggerFactory.getLogger(DSClientFactory.class);
private CassandraHostConfiguration configuration;
private String keyspace;
private Session session;
/*
* (non-Javadoc)
*
* @see
* com.impetus.kundera.loader.ClientFactory#getSchemaManager(java.util.Map)
*/
@Override
public SchemaManager getSchemaManager(Map<String, Object> puProperties)
{
if (schemaManager == null)
{
initializePropertyReader();
externalProperties.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0);
schemaManager = new CassandraSchemaManager(this.getClass().getName(), externalProperties, kunderaMetadata);
}
return schemaManager;
}
/*
* (non-Javadoc)
*
* @see com.impetus.kundera.loader.ClientLifeCycleManager#destroy()
*/
@Override
public void destroy()
{
if (indexManager != null)
{
indexManager.close();
}
if (schemaManager != null)
{
schemaManager.dropSchema();
}
schemaManager = null;
externalProperties = null;
releaseConnection(this.session);
((Cluster) getConnectionPoolOrConnection()).closeAsync();
}
/*
* (non-Javadoc)
*
* @see
* com.impetus.client.cassandra.common.CassandraClientFactory#addCassandraHost
* (com.impetus.client.cassandra.service.CassandraHost)
*/
@Override
public boolean addCassandraHost(CassandraHost host)
{
// No need to setup this, as it is part of Kundera powered retry
// service.
return false;
}
/*
* (non-Javadoc)
*
* @see
* com.impetus.kundera.loader.GenericClientFactory#initialize(java.util.Map)
*/
@Override
public void initialize(Map<String, Object> externalProperty)
{
reader = new CassandraEntityReader(kunderaMetadata);
initializePropertyReader();
setExternalProperties(externalProperty);
configuration = new CassandraHostConfiguration(externalProperties, CassandraPropertyReader.csmd,
getPersistenceUnit(), kunderaMetadata);
// initialize timestamp generator.
initializeTimestampGenerator(externalProperty);
}
/*
* (non-Javadoc)
*
* @see
* com.impetus.kundera.loader.GenericClientFactory#createPoolOrConnection()
*/
@Override
protected Object createPoolOrConnection()
{
// AuthInfoProvider,LoadBalancingPolicy, ReconnectionPolicy,
// RetryPolicy,ProtocolOptions.Compression, SSLOptions,
// PoolingOptions,SocketOptions
if (logger.isDebugEnabled())
{
logger.debug("Intiatilzing connection");
}
Properties connectionProperties = CassandraPropertyReader.csmd.getConnectionProperties();
Builder connectionBuilder = Cluster.builder();
// add host/port and AuthInfoProvider
for (Host host : configuration.getCassandraHosts())
{
connectionBuilder.addContactPoint(host.getHost()).withPort(host.getPort());
if (host.getUser() != null)
{
connectionBuilder.withCredentials(host.getUser(), host.getPassword());
}
}
// add policy configuration
String loadBalancingPolicy = connectionProperties.getProperty(Constants.LOADBALANCING_POLICY);
if (!StringUtils.isBlank(loadBalancingPolicy))
{
LoadBalancingPolicy policy = getPolicyInstance(BalancingPolicy.getPolicy(loadBalancingPolicy),
connectionProperties);
if (policy != null)
{
connectionBuilder.withLoadBalancingPolicy(policy);
}
}
// compression
String compression = connectionProperties.getProperty("compression");
if (!StringUtils.isBlank(compression))
{
connectionBuilder.withCompression(Compression.valueOf(compression));
}
// ReconnectionPolicy
String reconnectionPolicy = connectionProperties.getProperty("reconnection.policy");
if (!StringUtils.isBlank(reconnectionPolicy))
{
com.datastax.driver.core.policies.ReconnectionPolicy policy = getPolicy(
ReconnectionPolicy.getPolicy(reconnectionPolicy), connectionProperties);
if (policy != null)
{
connectionBuilder.withReconnectionPolicy(policy);
}
}
// , RetryPolicy
String retryPolicy = connectionProperties.getProperty("retry.policy");
if (!StringUtils.isBlank(retryPolicy))
{
com.datastax.driver.core.policies.RetryPolicy policy = getPolicy(RetryPolicy.getPolicy(retryPolicy),
connectionProperties);
if (policy != null)
{
connectionBuilder.withRetryPolicy(policy);
}
}
// TODO::: SSLOptions? Not sure how to add it.
// SocketOptions
connectionBuilder.withSocketOptions(getSocketOptions(connectionProperties));
// PoolingOptions,
connectionBuilder.withPoolingOptions(getPoolingOptions(connectionProperties));
// finally build cluster.
Cluster cluster = connectionBuilder.build();
PersistenceUnitMetadata persistenceUnitMetadata = kunderaMetadata.getApplicationMetadata()
.getPersistenceUnitMetadata(getPersistenceUnit());
Properties props = persistenceUnitMetadata.getProperties();
if (externalProperties != null)
{
keyspace = (String) externalProperties.get(PersistenceProperties.KUNDERA_KEYSPACE);
}
if (keyspace == null)
{
keyspace = (String) props.get(PersistenceProperties.KUNDERA_KEYSPACE);
}
setSessionObject(cluster);
return cluster;
}
/*
* (non-Javadoc)
*
* @see
* com.impetus.kundera.loader.GenericClientFactory#instantiateClient(java
* .lang.String)
*/
@Override
protected Client instantiateClient(String persistenceUnit)
{
return new DSClient(this, persistenceUnit, externalProperties, kunderaMetadata, reader, timestampGenerator);
}
void setSessionObject(Cluster cluster)
{
this.session = cluster.connect("\"" + keyspace + "\"");
}
Session getConnection()
{
return this.session != null ? this.session : ((Cluster) this.getConnectionPoolOrConnection()).connect("\""
+ keyspace + "\"");
}
void releaseConnection(Session session)
{
if (session != null)
{
session.closeAsync();
}
}
/*
* (non-Javadoc)
*
* @see com.impetus.kundera.loader.GenericClientFactory#isThreadSafe()
*/
@Override
public boolean isThreadSafe()
{
return false;
}
/*
* (non-Javadoc)
*
* @see
* com.impetus.kundera.loader.GenericClientFactory#initializeLoadBalancer
* (java.lang.String)
*/
@Override
protected void initializeLoadBalancer(String loadBalancingPolicyName)
{
throw new UnsupportedOperationException("Method not supported for datastax java driver");
}
/**
*
*/
private void initializePropertyReader()
{
if (propertyReader == null)
{
propertyReader = new CassandraPropertyReader(externalProperties, kunderaMetadata.getApplicationMetadata()
.getPersistenceUnitMetadata(getPersistenceUnit()));
propertyReader.read(getPersistenceUnit());
}
}
enum ReconnectionPolicy
{
ConstantReconnectionPolicy, ExponentialReconnectionPolicy;
static ReconnectionPolicy getPolicy(String name)
{
for (ReconnectionPolicy p : ReconnectionPolicy.values())
{
if (p.name().equalsIgnoreCase(name))
{
return p;
}
}
logger.error("Invalid policy name {} provided, supported policies are {}", name,
ReconnectionPolicy.values());
throw new InvalidConfigurationException("Invalid policy name " + name + " provided!");
}
}
enum BalancingPolicy
{
DCAwareRoundRobinPolicy, RoundRobinPolicy;
static BalancingPolicy getPolicy(String name)
{
for (BalancingPolicy p : BalancingPolicy.values())
{
if (p.name().equalsIgnoreCase(name))
{
return p;
}
}
logger.error("Invalid policy name {} provided, supported policies are {}", name, BalancingPolicy.values());
throw new InvalidConfigurationException("Invalid policy name " + name + " provided!");
}
}
enum RetryPolicy
{
DowngradingConsistencyRetryPolicy, FallthroughRetryPolicy;
static RetryPolicy getPolicy(String name)
{
for (RetryPolicy p : RetryPolicy.values())
{
if (p.name().equalsIgnoreCase(name))
{
return p;
}
}
logger.error("Invalid policy name {} provided, supported policies are {}", name, RetryPolicy.values());
throw new InvalidConfigurationException("Invalid policy name " + name + " provided!");
}
}
private LoadBalancingPolicy getPolicyInstance(BalancingPolicy policy, Properties conProperties)
{
LoadBalancingPolicy loadBalancingPolicy = null;
String isTokenAware = (String) conProperties.get("isTokenAware");
String isLatencyAware = (String) conProperties.get("isLatencyAware");
// Policy.v
switch (policy)
{
case DCAwareRoundRobinPolicy:
String usedHostsPerRemoteDc = (String) conProperties.get("usedHostsPerRemoteDc");
String localdc = (String) conProperties.get("localdc");
loadBalancingPolicy = new DCAwareRoundRobinPolicy(localdc == null ? "DC1" : localdc,
usedHostsPerRemoteDc != null ? Integer.parseInt(usedHostsPerRemoteDc) : 0);
break;
// case RoundRobinPolicy:
// loadBalancingPolicy = new RoundRobinPolicy();
// break;
default:
// default is RoundRobinPolicy
loadBalancingPolicy = new RoundRobinPolicy();
break;
}
if (loadBalancingPolicy != null && Boolean.valueOf(isTokenAware))
{
loadBalancingPolicy = new TokenAwarePolicy(loadBalancingPolicy);
}
else if (loadBalancingPolicy != null && Boolean.valueOf(isLatencyAware))
{
loadBalancingPolicy = LatencyAwarePolicy.builder(loadBalancingPolicy).build();
}
return loadBalancingPolicy;
}
private com.datastax.driver.core.policies.ReconnectionPolicy getPolicy(ReconnectionPolicy policy, Properties props)
{
com.datastax.driver.core.policies.ReconnectionPolicy reconnectionPolicy = null;
switch (policy)
{
case ConstantReconnectionPolicy:
String property = props.getProperty("constantDelayMs");
long constantDelayMs = property != null ? new Long(property) : 0l;
reconnectionPolicy = new ConstantReconnectionPolicy(constantDelayMs);
break;
case ExponentialReconnectionPolicy:
String baseDelayMsAsStr = props.getProperty("baseDelayMs");
String maxDelayMsAsStr = props.getProperty("maxDelayMs");
if (!StringUtils.isBlank(baseDelayMsAsStr) && !StringUtils.isBlank(maxDelayMsAsStr))
{
long baseDelayMs = new Long(baseDelayMsAsStr);
long maxDelayMs = new Long(maxDelayMsAsStr);
reconnectionPolicy = new ExponentialReconnectionPolicy(baseDelayMs, maxDelayMs);
}
break;
default:
break;
}
return reconnectionPolicy;
}
private com.datastax.driver.core.policies.RetryPolicy getPolicy(RetryPolicy policy, Properties props)
{
com.datastax.driver.core.policies.RetryPolicy retryPolicy = null;
String isLoggingRetry = (String) props.get("isLoggingRetry");
switch (policy)
{
case DowngradingConsistencyRetryPolicy:
retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE;
break;
case FallthroughRetryPolicy:
retryPolicy = FallthroughRetryPolicy.INSTANCE;
break;
default:
break;
}
if (retryPolicy != null && Boolean.valueOf(isLoggingRetry))
{
retryPolicy = new LoggingRetryPolicy(retryPolicy);
}
return retryPolicy;
}
private SocketOptions getSocketOptions(Properties connectionProperties)
{
// SocketOptions
SocketOptions socketConfig = new SocketOptions();
String connectTimeoutMillis = connectionProperties.getProperty(CassandraConstants.SOCKET_TIMEOUT);
String readTimeoutMillis = connectionProperties.getProperty("readTimeoutMillis");
String keepAlive = connectionProperties.getProperty("keepAlive");
String reuseAddress = connectionProperties.getProperty("reuseAddress");
String soLinger = connectionProperties.getProperty("soLinger");
String tcpNoDelay = connectionProperties.getProperty("tcpNoDelay");
String receiveBufferSize = connectionProperties.getProperty("receiveBufferSize");
String sendBufferSize = connectionProperties.getProperty("sendBufferSize");
if (!StringUtils.isBlank(connectTimeoutMillis))
{
socketConfig.setConnectTimeoutMillis(new Integer(connectTimeoutMillis));
}
if (!StringUtils.isBlank(readTimeoutMillis))
{
socketConfig.setReadTimeoutMillis(new Integer(readTimeoutMillis));
}
if (!StringUtils.isBlank(keepAlive))
{
socketConfig.setKeepAlive(new Boolean(keepAlive));
}
if (!StringUtils.isBlank(reuseAddress))
{
socketConfig.setReuseAddress(new Boolean(reuseAddress));
}
if (!StringUtils.isBlank(soLinger))
{
socketConfig.setSoLinger(new Integer(soLinger));
}
if (!StringUtils.isBlank(tcpNoDelay))
{
socketConfig.setTcpNoDelay(new Boolean(tcpNoDelay));
}
if (!StringUtils.isBlank(receiveBufferSize))
{
socketConfig.setReceiveBufferSize(new Integer(receiveBufferSize));
}
if (!StringUtils.isBlank(sendBufferSize))
{
socketConfig.setSendBufferSize(new Integer(sendBufferSize));
}
return socketConfig;
}
private PoolingOptions getPoolingOptions(Properties connectionProperties)
{
// minSimultaneousRequests, maxSimultaneousRequests, coreConnections,
// maxConnections
PoolingOptions options = new PoolingOptions();
String hostDistance = connectionProperties.getProperty("hostDistance");
String minSimultaneousRequests = connectionProperties.getProperty("minSimultaneousRequests");
String maxSimultaneousRequests = connectionProperties.getProperty("maxSimultaneousRequests");
String coreConnections = connectionProperties.getProperty("coreConnections");
String maxConnections = connectionProperties.getProperty("maxConnections");
if (!StringUtils.isBlank(hostDistance))
{
HostDistance hostDist = HostDistance.valueOf(hostDistance.toUpperCase());
if (!StringUtils.isBlank(coreConnections))
{
options.setCoreConnectionsPerHost(HostDistance.LOCAL, new Integer(coreConnections));
}
if (!StringUtils.isBlank(maxConnections))
{
options.setMaxConnectionsPerHost(hostDist, new Integer(maxConnections));
}
if (!StringUtils.isBlank(minSimultaneousRequests))
{
options.setMinSimultaneousRequestsPerConnectionThreshold(hostDist, new Integer(minSimultaneousRequests));
}
if (!StringUtils.isBlank(maxSimultaneousRequests))
{
options.setMaxSimultaneousRequestsPerConnectionThreshold(hostDist, new Integer(maxSimultaneousRequests));
}
}
return options;
}
}