/******************************************************************************* * /*** * * * * 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.dao.astyanax; import java.util.Collection; import java.util.IdentityHashMap; import java.util.Map; import java.util.Properties; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.commons.configuration.AbstractConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.inject.Inject; import com.netflix.astyanax.Keyspace; import com.netflix.astyanax.connectionpool.exceptions.BadRequestException; import com.netflix.astyanax.connectionpool.exceptions.ConnectionException; import com.netflix.paas.cassandra.provider.KeyspaceClientProvider; import com.netflix.paas.dao.Dao; import com.netflix.paas.dao.DaoSchemaProvider; import com.netflix.paas.dao.DaoSchema; import com.netflix.paas.exceptions.NotFoundException; /** * Astyanax based Dao factory for persisting PAAS state * * @author elandau * */ public class AstyanaxDaoSchemaProvider implements DaoSchemaProvider { private final Logger LOG = LoggerFactory.getLogger(AstyanaxDaoSchemaProvider.class); private final static String CONFIG_PREFIX_FORMAT = "com.netflix.paas.schema.%s"; private final KeyspaceClientProvider keyspaceProvider; private final Map<String, DaoSchema> schemas = Maps.newHashMap(); private final AbstractConfiguration configuration; public class AstyanaxDaoSchema implements DaoSchema { private final IdentityHashMap<Class<?>, Dao<?>> daos = Maps.newIdentityHashMap(); private final Keyspace keyspace; private final String schemaName; public AstyanaxDaoSchema(String schemaName, Keyspace keyspace) { this.keyspace = keyspace; this.schemaName = schemaName; Configuration config = configuration.subset(String.format(CONFIG_PREFIX_FORMAT, schemaName.toLowerCase())); if (config.getBoolean("autocreate", false)) { try { createSchema(); } catch (Exception e) { LOG.error("Error creating column keyspace", e); } } } @Override public synchronized void createSchema() { final Properties props = ConfigurationConverter.getProperties(configuration.subset(String.format(CONFIG_PREFIX_FORMAT, schemaName.toLowerCase()))); try { props.setProperty("name", props.getProperty("keyspace")); LOG.info("Creating schema: " + schemaName + " " + props); this.keyspace.createKeyspace(props); } catch (ConnectionException e) { LOG.error("Failed to create schema '{}' with properties '{}'", new Object[]{schemaName, props.toString(), e}); throw new RuntimeException("Failed to create keyspace " + keyspace.getKeyspaceName(), e); } } @Override public synchronized void dropSchema() { try { this.keyspace.dropKeyspace(); } catch (ConnectionException e) { throw new RuntimeException("Failed to drop keyspace " + keyspace.getKeyspaceName(), e); } } @Override public synchronized Collection<Dao<?>> listDaos() { return Lists.newArrayList(daos.values()); } @Override public boolean isExists() { try { this.keyspace.describeKeyspace(); return true; } catch (BadRequestException e) { return false; } catch (Exception e) { throw new RuntimeException("Failed to determine if keyspace " + keyspace.getKeyspaceName() + " exists", e); } } @Override public synchronized <T> Dao<T> getDao(Class<T> type) { Dao<?> dao = daos.get(type); if (dao == null) { dao = new AstyanaxDao<T>(keyspace, type); daos.put(type, dao); } return (Dao<T>) dao; } } @Inject public AstyanaxDaoSchemaProvider(KeyspaceClientProvider keyspaceProvider, AbstractConfiguration configuration) { this.keyspaceProvider = keyspaceProvider; this.configuration = configuration; } @PostConstruct public void start() { } @PreDestroy public void stop() { } @Override public synchronized Collection<DaoSchema> listSchemas() { return Lists.newArrayList(schemas.values()); } @Override public synchronized DaoSchema getSchema(String schemaName) throws NotFoundException { AstyanaxDaoSchema schema = (AstyanaxDaoSchema)schemas.get(schemaName); if (schema == null) { LOG.info("Creating schema '{}'", new Object[]{schemaName}); Keyspace keyspace = keyspaceProvider.acquireKeyspace(schemaName); schema = new AstyanaxDaoSchema(schemaName, keyspace); schemas.put(schemaName, schema); } return schema; } }