/*******************************************************************************
* /***
* *
* * 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.resources.admin;
import java.util.Collection;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.persistence.PersistenceException;
import javax.ws.rs.PathParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.EventBus;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.name.Named;
import com.netflix.astyanax.Cluster;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.paas.cassandra.admin.CassandraClusterAdminResource;
import com.netflix.paas.cassandra.discovery.ClusterDiscoveryService;
import com.netflix.paas.cassandra.entity.CassandraClusterEntity;
import com.netflix.paas.cassandra.entity.ColumnFamilyEntity;
import com.netflix.paas.cassandra.entity.KeyspaceEntity;
import com.netflix.paas.cassandra.events.ColumnFamilyDeleteEvent;
import com.netflix.paas.cassandra.events.ColumnFamilyUpdateEvent;
import com.netflix.paas.cassandra.events.KeyspaceUpdateEvent;
import com.netflix.paas.cassandra.keys.ClusterKey;
import com.netflix.paas.cassandra.keys.ColumnFamilyKey;
import com.netflix.paas.cassandra.keys.KeyspaceKey;
import com.netflix.paas.cassandra.provider.ClusterClientProvider;
import com.netflix.paas.dao.Dao;
import com.netflix.paas.dao.DaoProvider;
import com.netflix.paas.dao.astyanax.DaoKeys;
import com.netflix.paas.exceptions.NotFoundException;
import com.netflix.paas.exceptions.PaasException;
/**
* Implementation of a Cassandra Cluster Admin interface using Astyanax and Thrift.
* Since this is the admin interface only one connection is actually needed to the cluster
*
* @author elandau
*/
public class AstyanaxThriftClusterAdminResource implements CassandraClusterAdminResource {
private static final Logger LOG = LoggerFactory.getLogger(AstyanaxThriftClusterAdminResource.class);
private final ClusterKey clusterKey;
private final Cluster cluster;
private final DaoProvider daoProvider;
private final EventBus eventBus;
@Inject
public AstyanaxThriftClusterAdminResource(
EventBus eventBus,
DaoProvider daoProvider,
@Named("tasks") ScheduledExecutorService taskExecutor,
ClusterDiscoveryService discoveryService,
ClusterClientProvider clusterProvider,
@Assisted ClusterKey clusterKey) {
this.clusterKey = clusterKey;
this.cluster = clusterProvider.acquireCluster(clusterKey);
this.daoProvider = daoProvider;
this.eventBus = eventBus;
}
@PostConstruct
public void initialize() {
}
@PreDestroy
public void shutdown() {
}
@Override
public CassandraClusterEntity getClusterDetails() throws PersistenceException {
return daoProvider.getDao(DaoKeys.DAO_CASSANDRA_CLUSTER_ENTITY).read(clusterKey.getCanonicalName());
}
@Override
public KeyspaceEntity getKeyspace(String keyspaceName) throws PersistenceException {
Dao<KeyspaceEntity> dao = daoProvider.getDao(DaoKeys.DAO_KEYSPACE_ENTITY);
return dao.read(keyspaceName);
}
@Override
public void createKeyspace(KeyspaceEntity keyspace) throws PaasException {
LOG.info("Creating keyspace '{}'", new Object[] {keyspace.getName()});
Preconditions.checkNotNull(keyspace, "Missing keyspace entity definition");
Properties props = new Properties();
props.putAll(getDefaultKeyspaceProperties());
if (keyspace.getOptions() != null) {
props.putAll(keyspace.getOptions());
}
props.setProperty("name", keyspace.getName());
keyspace.setClusterName(clusterKey.getClusterName());
try {
cluster.createKeyspace(props);
eventBus.post(new KeyspaceUpdateEvent(new KeyspaceKey(clusterKey, keyspace.getName())));
} catch (ConnectionException e) {
throw new PaasException(String.format("Error creating keyspace '%s' from cluster '%s'", keyspace.getName(), clusterKey.getClusterName()), e);
}
}
@Override
public void updateKeyspace(@PathParam("keyspace") String keyspaceName, KeyspaceEntity keyspace) throws PaasException {
try {
if (keyspace.getOptions() == null) {
return; // Nothing to do
}
// Add them as existing values to the properties object
Properties props = new Properties();
props.putAll(cluster.getKeyspaceProperties(keyspaceName));
props.putAll(keyspace.getOptions());
props.setProperty("name", keyspace.getName());
keyspace.setClusterName(clusterKey.getClusterName());
cluster.updateKeyspace(props);
eventBus.post(new KeyspaceUpdateEvent(new KeyspaceKey(clusterKey, keyspace.getName())));
} catch (ConnectionException e) {
throw new PaasException(String.format("Error creating keyspace '%s' from cluster '%s'", keyspace.getName(), clusterKey.getClusterName()), e);
}
}
@Override
public void deleteKeyspace(String keyspaceName) throws PaasException {
LOG.info("Dropping keyspace");
try {
cluster.dropKeyspace(keyspaceName);
} catch (ConnectionException e) {
throw new PaasException(String.format("Error deleting keyspace '%s' from cluster '%s'", keyspaceName, clusterKey.getClusterName()), e);
}
}
@Override
public ColumnFamilyEntity getColumnFamily(String keyspaceName, String columnFamilyName) throws NotFoundException {
Dao<ColumnFamilyEntity> dao = daoProvider.getDao(DaoKeys.DAO_COLUMN_FAMILY_ENTITY);
return dao.read(new ColumnFamilyKey(clusterKey, keyspaceName, columnFamilyName).getCanonicalName());
}
@Override
public void deleteColumnFamily(String keyspaceName, String columnFamilyName) throws PaasException {
LOG.info("Deleting column family: '{}.{}.{}'", new Object[] {clusterKey.getClusterName(), keyspaceName, columnFamilyName});
try {
cluster.dropColumnFamily(keyspaceName, columnFamilyName);
} catch (ConnectionException e) {
throw new PaasException(String.format("Error creating column family '%s.%s' on cluster '%s'",
keyspaceName, columnFamilyName, clusterKey.getClusterName()), e);
}
eventBus.post(new ColumnFamilyDeleteEvent(new ColumnFamilyKey(clusterKey, keyspaceName, columnFamilyName)));
}
@Override
public void createColumnFamily(@PathParam("keyspace") String keyspaceName, ColumnFamilyEntity columnFamily) throws PaasException {
LOG.info("Creating column family: '{}.{}.{}'", new Object[] {clusterKey.getClusterName(), keyspaceName, columnFamily.getName()});
columnFamily.setKeyspaceName(keyspaceName);
columnFamily.setClusterName(clusterKey.getClusterName());
Properties props = new Properties();
props.putAll(getDefaultColumnFamilyProperties());
if (columnFamily.getOptions() != null) {
props.putAll(columnFamily.getOptions());
}
props.setProperty("name", columnFamily.getName());
props.setProperty("keyspace", columnFamily.getKeyspaceName());
try {
cluster.createColumnFamily(props);
eventBus.post(new ColumnFamilyUpdateEvent(new ColumnFamilyKey(new KeyspaceKey(clusterKey, keyspaceName), columnFamily.getName())));
} catch (ConnectionException e) {
throw new PaasException(String.format("Error creating column family '%s.%s' on cluster '%s'",
keyspaceName, columnFamily.getName(), clusterKey.getClusterName()), e);
}
}
@Override
public void updateColumnFamily(@PathParam("keyspace") String keyspaceName, String columnFamilyName, ColumnFamilyEntity columnFamily) throws PaasException {
LOG.info("Updating column family: '{}.{}.{}'", new Object[] {clusterKey.getClusterName(), keyspaceName, columnFamily.getName()});
columnFamily.setKeyspaceName(keyspaceName);
columnFamily.setClusterName(clusterKey.getClusterName());
try {
Properties props = new Properties();
props.putAll(cluster.getColumnFamilyProperties(keyspaceName, columnFamilyName));
if (columnFamily.getOptions() != null) {
props.putAll(columnFamily.getOptions());
}
props.setProperty("name", columnFamily.getName());
props.setProperty("keyspace", columnFamily.getKeyspaceName());
cluster.createColumnFamily(props);
eventBus.post(new ColumnFamilyUpdateEvent(new ColumnFamilyKey(new KeyspaceKey(clusterKey, keyspaceName), columnFamily.getName())));
} catch (ConnectionException e) {
throw new PaasException(String.format("Error creating column family '%s.%s' on cluster '%s'",
keyspaceName, columnFamily.getName(), clusterKey.getClusterName()), e);
}
}
@Override
public Collection<ColumnFamilyEntity> listColumnFamilies() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<String> listKeyspaceNames() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<String> listColumnFamilyNames() {
// TODO Auto-generated method stub
return null;
}
private Properties getDefaultKeyspaceProperties() {
// TODO: Read from configuration
return new Properties();
}
private Properties getDefaultColumnFamilyProperties() {
return new Properties();
}
@Override
public Collection<KeyspaceEntity> listKeyspaces() {
// TODO Auto-generated method stub
return null;
}
}