/******************************************************************************* * /*** * * * * Copyright 2013 Netflix, 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 com.netflix.paas.cassandra.provider.impl; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.configuration.AbstractConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.inject.Inject; import com.netflix.astyanax.AstyanaxContext; import com.netflix.astyanax.Keyspace; import com.netflix.astyanax.connectionpool.exceptions.ConnectionException; import com.netflix.astyanax.thrift.ThriftFamilyFactory; import com.netflix.paas.cassandra.keys.KeyspaceKey; import com.netflix.paas.cassandra.provider.AstyanaxConfigurationProvider; import com.netflix.paas.cassandra.provider.AstyanaxConnectionPoolConfigurationProvider; import com.netflix.paas.cassandra.provider.AstyanaxConnectionPoolMonitorProvider; import com.netflix.paas.cassandra.provider.HostSupplierProvider; import com.netflix.paas.cassandra.provider.KeyspaceClientProvider; public class DefaultKeyspaceClientProvider implements KeyspaceClientProvider { private static final Logger LOG = LoggerFactory.getLogger(DefaultKeyspaceClientProvider.class); private static final String DISCOVERY_TYPE_FORMAT = "com.netflix.paas.schema.%s.discovery"; private static final String CLUSTER_NAME_FORMAT = "com.netflix.paas.schema.%s.cluster"; private static final String KEYSPACE_NAME__FORMAT = "com.netflix.paas.schema.%s.keyspace"; ImmutableMap<String, Object> defaultKsOptions = ImmutableMap.<String, Object>builder() .put("strategy_options", ImmutableMap.<String, Object>builder() .put("replication_factor", "1") .build()) .put("strategy_class", "SimpleStrategy") .build(); public static class KeyspaceContextHolder { private AstyanaxContext<Keyspace> context; private AtomicLong refCount = new AtomicLong(0); public KeyspaceContextHolder(AstyanaxContext<Keyspace> context) { this.context = context; } public Keyspace getKeyspace() { return context.getClient(); } public void start() { context.start(); } public void shutdown() { context.shutdown(); } public long addRef() { return refCount.incrementAndGet(); } public long releaseRef() { return refCount.decrementAndGet(); } } private final Map<String, KeyspaceContextHolder> contextMap = Maps.newHashMap(); private final AstyanaxConfigurationProvider configurationProvider; private final AstyanaxConnectionPoolConfigurationProvider cpProvider; private final AstyanaxConnectionPoolMonitorProvider monitorProvider; private final Map<String, HostSupplierProvider> hostSupplierProviders; private final AbstractConfiguration configuration; @Inject public DefaultKeyspaceClientProvider( AbstractConfiguration configuration, Map<String, HostSupplierProvider> hostSupplierProviders, AstyanaxConfigurationProvider configurationProvider, AstyanaxConnectionPoolConfigurationProvider cpProvider, AstyanaxConnectionPoolMonitorProvider monitorProvider) { this.configurationProvider = configurationProvider; this.cpProvider = cpProvider; this.monitorProvider = monitorProvider; this.hostSupplierProviders = hostSupplierProviders; this.configuration = configuration; } @Override public synchronized Keyspace acquireKeyspace(String schemaName) { schemaName = schemaName.toLowerCase(); Preconditions.checkNotNull(schemaName, "Invalid schema name 'null'"); KeyspaceContextHolder holder = contextMap.get(schemaName); if (holder == null) { LOG.info("Creating schema for '{}'", new Object[]{schemaName}); String clusterName = configuration.getString(String.format(CLUSTER_NAME_FORMAT, schemaName)); String keyspaceName = configuration.getString(String.format(KEYSPACE_NAME__FORMAT, schemaName)); String discoveryType = configuration.getString(String.format(DISCOVERY_TYPE_FORMAT, schemaName)); if (clusterName==null || clusterName.equals("")) clusterName = configuration.getString(String.format(CLUSTER_NAME_FORMAT, "configuration")); if (keyspaceName == null || keyspaceName.equals("")) keyspaceName = schemaName; if (discoveryType==null || discoveryType.equals("")) discoveryType = configuration.getString(String.format(DISCOVERY_TYPE_FORMAT, "configuration")); Preconditions.checkNotNull(clusterName, "Missing cluster name for schema " + schemaName + " " + String.format(CLUSTER_NAME_FORMAT,schemaName)); Preconditions.checkNotNull(keyspaceName, "Missing cluster name for schema " + schemaName + " " + String.format(KEYSPACE_NAME__FORMAT,schemaName)); Preconditions.checkNotNull(discoveryType, "Missing cluster name for schema " + schemaName + " " + String.format(DISCOVERY_TYPE_FORMAT,schemaName)); HostSupplierProvider hostSupplierProvider = hostSupplierProviders.get(discoveryType); Preconditions.checkNotNull(hostSupplierProvider, String.format("Unknown host supplier provider '%s' for schema '%s'", discoveryType, schemaName)); AstyanaxContext<Keyspace> context = new AstyanaxContext.Builder() .forCluster(clusterName) .forKeyspace(keyspaceName) .withAstyanaxConfiguration(configurationProvider.get(schemaName)) .withConnectionPoolConfiguration(cpProvider.get(schemaName)) .withConnectionPoolMonitor(monitorProvider.get(schemaName)) .withHostSupplier(hostSupplierProvider.getSupplier(clusterName)) .buildKeyspace(ThriftFamilyFactory.getInstance()); context.start(); try { context.getClient().createKeyspace(defaultKsOptions); } catch (ConnectionException e) { // TODO Auto-generated catch block e.printStackTrace(); } holder = new KeyspaceContextHolder(context); contextMap.put(schemaName, holder); holder.start(); } holder.addRef(); return holder.getKeyspace(); } @Override public synchronized void releaseKeyspace(String schemaName) { KeyspaceContextHolder holder = contextMap.get(schemaName); if (holder.releaseRef() == 0) { contextMap.remove(schemaName); holder.shutdown(); } } @Override public Keyspace acquireKeyspace(KeyspaceKey key) { String schemaName = key.getSchemaName().toLowerCase(); Preconditions.checkNotNull(schemaName, "Invalid schema name 'null'"); KeyspaceContextHolder holder = contextMap.get(schemaName); if (holder == null) { Preconditions.checkNotNull(key.getClusterName(), "Missing cluster name for schema " + schemaName); Preconditions.checkNotNull(key.getKeyspaceName(), "Missing cluster name for schema " + schemaName); Preconditions.checkNotNull(key.getDiscoveryType(), "Missing cluster name for schema " + schemaName); HostSupplierProvider hostSupplierProvider = hostSupplierProviders.get(key.getDiscoveryType()); Preconditions.checkNotNull(hostSupplierProvider, "Unknown host supplier provider " + key.getDiscoveryType()); AstyanaxContext<Keyspace> context = new AstyanaxContext.Builder() .forCluster(key.getClusterName()) .forKeyspace(key.getKeyspaceName()) .withAstyanaxConfiguration(configurationProvider.get(schemaName)) .withConnectionPoolConfiguration(cpProvider.get(schemaName)) .withConnectionPoolMonitor(monitorProvider.get(schemaName)) .withHostSupplier(hostSupplierProvider.getSupplier(key.getClusterName())) .buildKeyspace(ThriftFamilyFactory.getInstance()); holder = new KeyspaceContextHolder(context); contextMap.put(schemaName, holder); holder.start(); } holder.addRef(); return holder.getKeyspace(); } }