/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.datastax.drivers.jdbc.pool.cassandra.connection; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.cassandra.thrift.Cassandra; import org.apache.cassandra.thrift.InvalidRequestException; import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.datastax.drivers.jdbc.pool.cassandra.service.ExceptionsTranslator; import com.datastax.drivers.jdbc.pool.cassandra.service.ExceptionsTranslatorImpl; import com.datastax.drivers.jdbc.pool.cassandra.service.FailoverPolicy; import com.datastax.drivers.jdbc.pool.cassandra.service.JmxMonitor; /** * A cluster instance the client side representation of a cassandra server cluster. * * The cluster is usually the main entry point for programs using hector. To start operating on * cassandra cluster you first get or create a cluster, then a keyspace operator for the keyspace * you're interested in and then create mutations of queries * <code> * //get a cluster: * Cluster cluster = getOrCreateCluster("MyCluster", new CassandraHostConfigurator("127.0.0.1:9170")); * //get a keyspace from this cluster: * Keyspace ko = createKeyspace("Keyspace1", cluster); * //Create a mutator: * Mutator m = createMutator(ko); * // Make a mutation: * MutationResult mr = m.insert("key", cf, createColumn("name", "value", serializer, serializer)); * </code> * * THREAD SAFETY: This class is thread safe. * * @author Ran Tavory * @author zznate */ public class ThriftCluster implements Cluster { private static final Map<String, String> EMPTY_CREDENTIALS = Collections.emptyMap(); private final Logger log = LoggerFactory.getLogger(ThriftCluster.class); /** * Linked to Cassandra StorageProxy. */ private static final int RING_DELAY = 30 * 1000; // delay after which we assume ring has stablized protected final HConnectionManager connectionManager; private final String name; private final CassandraHostConfigurator configurator; private final FailoverPolicy failoverPolicy; private final CassandraClientMonitor cassandraClientMonitor; private Set<String> knownClusterHosts; private Set<CassandraHost> knownPoolHosts; protected final ExceptionsTranslator xtrans; private final Map<String, String> credentials; public ThriftCluster(String clusterName, CassandraHostConfigurator cassandraHostConfigurator) { this(clusterName, cassandraHostConfigurator, EMPTY_CREDENTIALS); } public ThriftCluster(String clusterName, CassandraHostConfigurator cassandraHostConfigurator, Map<String, String> credentials) { connectionManager = new HConnectionManager(clusterName, cassandraHostConfigurator); name = clusterName; configurator = cassandraHostConfigurator; failoverPolicy = FailoverPolicy.ON_FAIL_TRY_ALL_AVAILABLE; cassandraClientMonitor = JmxMonitor.getInstance().getCassandraMonitor(connectionManager); xtrans = new ExceptionsTranslatorImpl(); this.credentials = Collections.unmodifiableMap(credentials); } @Override public HConnectionManager getConnectionManager() { return connectionManager; } /* (non-Javadoc) * @see me.prettyprint.cassandra.service.Cluster#getKnownPoolHosts(boolean) */ @Override public Set<CassandraHost> getKnownPoolHosts(boolean refresh) { if (refresh || knownPoolHosts == null) { knownPoolHosts = connectionManager.getHosts(); if ( log.isInfoEnabled() ) { log.info("found knownPoolHosts: {}", knownPoolHosts); } } return knownPoolHosts; } /* (non-Javadoc) * @see me.prettyprint.cassandra.service.Cluster#addHost(me.prettyprint.cassandra.service.CassandraHost, boolean) */ @Override public void addHost(CassandraHost cassandraHost, boolean skipApplyConfig) { if (!skipApplyConfig && configurator != null) { configurator.applyConfig(cassandraHost); } connectionManager.addCassandraHost(cassandraHost); } /* (non-Javadoc) * @see me.prettyprint.cassandra.service.Cluster#getName() */ @Override public String getName() { return name; } @Override public Map<String, String> getCredentials() { return credentials; } protected static void waitForSchemaAgreement(Cassandra.Client cassandra) throws InvalidRequestException, TException, InterruptedException { int waited = 0; int versions = 0; while (versions != 1) { ArrayList<String> liveschemas = new ArrayList<String>(); Map<String, List<String>> schema = cassandra.describe_schema_versions(); for (Map.Entry<String, List<String>> entry : schema.entrySet()) { if (!entry.getKey().equals("UNREACHABLE")) liveschemas.add(entry.getKey()); } versions = liveschemas.size(); Thread.sleep(1000); waited += 1000; if (waited > RING_DELAY) throw new RuntimeException("Could not reach schema agreement in " + RING_DELAY + "ms"); } } }