Package com.netflix.astyanax.entitystore

Source Code of com.netflix.astyanax.entitystore.DefaultEntityManager

package com.netflix.astyanax.entitystore;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.PersistenceException;

import org.apache.commons.lang.StringUtils;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.model.ColumnList;
import com.netflix.astyanax.model.ConsistencyLevel;
import com.netflix.astyanax.model.CqlResult;
import com.netflix.astyanax.model.Row;
import com.netflix.astyanax.model.Rows;
import com.netflix.astyanax.partitioner.BigInteger127Partitioner;
import com.netflix.astyanax.partitioner.Partitioner;
import com.netflix.astyanax.query.ColumnFamilyQuery;
import com.netflix.astyanax.recipes.reader.AllRowsReader;
import com.netflix.astyanax.retry.RetryPolicy;
import com.netflix.astyanax.serializers.StringSerializer;

/**
* Manager entities in a column famliy with any key type but columns that are
* encoded as strings.
*/
public class DefaultEntityManager<T, K> implements EntityManager<T, K> {

  //////////////////////////////////////////////////////////////////
  // Builder pattern

  public static class Builder<T, K> {

    private Class<T> clazz = null;
    private EntityMapper<T,K> entityMapper = null;
    private Keyspace keyspace = null;
    private ColumnFamily<K, String> columnFamily = null;
    private ConsistencyLevel readConsitency = null;
    private ConsistencyLevel writeConsistency = null;
    private Integer ttl = null;
    private RetryPolicy retryPolicy = null;
    private LifecycleEvents<T> lifecycleHandler = null;
    private String columnFamilyName = null;
    private boolean autoCommit = true;
    private Partitioner partitioner = DEFAULT_ENTITY_MANAGER_PARTITIONER;

    public Builder() {

    }

    /**
     * mandatory
     * @param clazz entity class type
     */
    public Builder<T, K> withEntityType(Class<T> clazz) {
      Preconditions.checkNotNull(clazz);
      this.clazz = clazz;
      return this;
    }

    /**
     * mandatory
     * @param keyspace
     */
    public Builder<T, K> withKeyspace(Keyspace keyspace) {
      Preconditions.checkNotNull(keyspace);
      this.keyspace = keyspace;
      return this;
    }

    /**
     * optional
     * @param columnFamily column name type is fixed to String/UTF8
     */
    public Builder<T, K> withColumnFamily(ColumnFamily<K, String> columnFamily) {
        Preconditions.checkState(this.columnFamilyName == null && this.columnFamily == null , "withColumnFamily called multiple times");
      Preconditions.checkNotNull(columnFamily);
      this.columnFamily = columnFamily;
      return this;
    }
   
    /**
     * optional
     * @param columnFamilyName Name of column family to use. 
     */
    public Builder<T, K> withColumnFamily(String columnFamilyName) {
            Preconditions.checkState(this.columnFamilyName == null && columnFamily == null , "withColumnFamily called multiple times");
            Preconditions.checkNotNull(columnFamilyName);
        this.columnFamilyName = columnFamilyName;
        return this;
    }

    /**
     * optional
     * @param level
     */
    public Builder<T, K> withReadConsistency(ConsistencyLevel level) {
      Preconditions.checkNotNull(level);
      this.readConsitency = level;
      return this;
    }

    /**
     * optional
     * @param level
     */
    public Builder<T, K> withWriteConsistency(ConsistencyLevel level) {
      Preconditions.checkNotNull(level);
      this.writeConsistency = level;
      return this;
    }

    /**
     * set both read and write consistency
     * optional
     * @param level
     */
    public Builder<T, K> withConsistency(ConsistencyLevel level) {
      Preconditions.checkNotNull(level);
      this.readConsitency = level;
      this.writeConsistency = level;
      return this;
    }

    /**
     * default TTL for all columns written to cassandra
     * optional
     * @return
     */
    public Builder<T, K> withTTL(Integer ttl) {
      this.ttl = ttl;
      return this;
    }

    /**
     * optional
     * @param level
     */
    public Builder<T, K> withRetryPolicy(RetryPolicy policy) {
      Preconditions.checkNotNull(policy);
      this.retryPolicy = policy;
      return this;
    }
   
    public Builder<T, K> withAutoCommit(boolean autoCommit) {
      this.autoCommit = autoCommit;
      return this;
    }

    /**
     * Partitioner used to determine token ranges and how to break token
     * ranges into sub parts. The default is BigInteger127Partitioner in
     * pre-cassandra 1.2.
     *
     * @param partitioner
     * @return
     */
    public Builder<T, K> withPartitioner(Partitioner partitioner) {
      this.partitioner = partitioner;
      return this;
    }

    @SuppressWarnings("unchecked")
    public DefaultEntityManager<T, K> build() {
      // check mandatory fields
      Preconditions.checkNotNull(clazz, "withEntityType(...) is not set");
      Preconditions
          .checkNotNull(keyspace, "withKeyspace(...) is not set");

      // TODO: check @Id type compatibility
      // TODO: do we need to require @Entity annotation
      this.entityMapper = new EntityMapper<T, K>(clazz, ttl);
      this.lifecycleHandler = new LifecycleEvents<T>(clazz);

      if (columnFamily == null) {
        if (columnFamilyName == null)
          columnFamilyName = entityMapper.getEntityName();
        columnFamily = new ColumnFamily<K, String>(columnFamilyName,
            (com.netflix.astyanax.Serializer<K>) MappingUtils
                .getSerializerForField(this.entityMapper
                    .getId()), StringSerializer.get());
      }
      // build object
      return new DefaultEntityManager<T, K>(this);
    }
  }
 
  public static <T,K> Builder<T,K> builder() {
      return new Builder<T,K>();
  }

  //////////////////////////////////////////////////////////////////
  // private members

  private final EntityMapper<T, K> entityMapper;
  private final Keyspace keyspace;
  private final ColumnFamily<K, String> columnFamily;
  private final ConsistencyLevel readConsitency;
  private final ConsistencyLevel writeConsistency;
  private final RetryPolicy retryPolicy;
  private final LifecycleEvents<T> lifecycleHandler;
  private final boolean autoCommit;
  private final ThreadLocal<MutationBatch> tlMutation = new ThreadLocal<MutationBatch>();
  private static final Partitioner DEFAULT_ENTITY_MANAGER_PARTITIONER = BigInteger127Partitioner
      .get();
  private final Partitioner partitioner;

  private DefaultEntityManager(Builder<T, K> builder) {
    entityMapper      = builder.entityMapper;
    keyspace          = builder.keyspace;
    columnFamily      = builder.columnFamily;
    readConsitency    = builder.readConsitency;
    writeConsistency  = builder.writeConsistency;
    retryPolicy       = builder.retryPolicy;
    lifecycleHandler  = builder.lifecycleHandler;
    autoCommit        = builder.autoCommit;
    partitioner       = builder.partitioner;
  }

  //////////////////////////////////////////////////////////////////
  // public APIs

  /**
   * @inheritDoc
   */
  public void put(T entity) throws PersistenceException {
    try {
        lifecycleHandler.onPrePersist(entity);
            MutationBatch mb = newMutationBatch();
      entityMapper.fillMutationBatch(mb, columnFamily, entity);     
            if (autoCommit)
                mb.execute();
            lifecycleHandler.onPostPersist(entity);
    } catch(Exception e) {
      throw new PersistenceException("failed to put entity ", e);
    }
  }

  /**
   * @inheritDoc
   */
  public T get(K id) throws PersistenceException {
    try {
      ColumnFamilyQuery<K, String> cfq = newQuery();           
      ColumnList<String> cl = cfq.getKey(id).execute().getResult();
      // when a row is deleted in cassandra,
      // the row key remains (without any columns) until the next compaction.
      // simply return null (as non exist)
      if(cl.isEmpty())
        return null;
      T entity = entityMapper.constructEntity(id, cl);
      lifecycleHandler.onPostLoad(entity);
      return entity;
    } catch(Exception e) {
      throw new PersistenceException("failed to get entity " + id, e);
    }
  }

  /**
   * @inheritDoc
   */
  @Override
  public void delete(K id) throws PersistenceException {
    try {
      MutationBatch mb = getMutationBatch();
      mb.withRow(columnFamily, id).delete();
            if (autoCommit)
                mb.execute();
    } catch(Exception e) {
      throw new PersistenceException("failed to delete entity " + id, e);
    }
  }
 
    @Override
    public void remove(T entity) throws PersistenceException {
        K id = null;
        try {
            lifecycleHandler.onPreRemove(entity);
            id = entityMapper.getEntityId(entity);
            MutationBatch mb = newMutationBatch();
            mb.withRow(columnFamily, id).delete();
            if (autoCommit)
                mb.execute();
            lifecycleHandler.onPostRemove(entity);
        } catch(Exception e) {
            throw new PersistenceException("failed to delete entity " + id, e);
        }
    }

    /**
     * @inheritDoc
     */
    @Override
    public List<T> getAll() throws PersistenceException {
        final List<T> entities = Lists.newArrayList();
        visitAll(new Function<T, Boolean>() {
            @Override
            public synchronized Boolean apply(T entity) {
                entities.add(entity);
                return true;
            }
        });
        return entities;
    }

    /**
     * @inheritDoc
     */
    @Override
    public List<T> get(Collection<K> ids) throws PersistenceException {
        try {
            ColumnFamilyQuery<K, String> cfq = newQuery();           
            Rows<K, String> rows = cfq.getRowSlice(ids).execute().getResult();

            List<T> entities = Lists.newArrayListWithExpectedSize(rows.size());
            for (Row<K, String> row : rows) {
                if (!row.getColumns().isEmpty()) {
                    T entity = entityMapper.constructEntity(row.getKey(), row.getColumns());
                    lifecycleHandler.onPostLoad(entity);
                    entities.add(entity);
                }
            }
            return entities;
        } catch(Exception e) {
            throw new PersistenceException("failed to get entities " + ids, e);
        }
    }

    /**
     * @inheritDoc
     */
    @Override
    public void delete(Collection<K> ids) throws PersistenceException {
        MutationBatch mb = getMutationBatch();       
        try {
            for (K id : ids) {
                mb.withRow(columnFamily, id).delete();
            }
            if (autoCommit)
                mb.execute();
        } catch(Exception e) {
            throw new PersistenceException("failed to delete entities " + ids, e);
        }
    }

    @Override
    public void remove(Collection<T> entities) throws PersistenceException {
        MutationBatch mb = getMutationBatch();       
        try {
            for (T entity : entities) {
                lifecycleHandler.onPreRemove(entity);
                K id = entityMapper.getEntityId(entity);
                mb.withRow(columnFamily, id).delete();
            }
            mb.execute();
            for (T entity : entities) {
                lifecycleHandler.onPostRemove(entity);
            }
        } catch(Exception e) {
            throw new PersistenceException("failed to delete entities ", e);
        }
    }

    /**
     * @inheritDoc
     */
    @Override
    public void put(Collection<T> entities) throws PersistenceException {
        MutationBatch mb = getMutationBatch();       
        try {
            for (T entity : entities) {
                lifecycleHandler.onPrePersist(entity);
                entityMapper.fillMutationBatch(mb, columnFamily, entity);          
            }
            if (autoCommit)
                mb.execute();
           
            for (T entity : entities) {
                lifecycleHandler.onPostPersist(entity);
            }

        } catch(Exception e) {
            throw new PersistenceException("failed to put entities ", e);
        }
    }
   
    /**
     * @inheritDoc
     */
    @Override
    public void visitAll(final Function<T, Boolean> callback) throws PersistenceException {
        try {
            new AllRowsReader.Builder<K, String>(keyspace, columnFamily)
                    .withIncludeEmptyRows(false)
                    .withPartitioner(partitioner)
                    .forEachRow(new Function<Row<K,String>, Boolean>() {
                        @Override
                        public Boolean apply(Row<K, String> row) {
                            if (row.getColumns().isEmpty())
                                return true;
                            T entity = entityMapper.constructEntity(row.getKey(), row.getColumns());
                            try {
                                lifecycleHandler.onPostLoad(entity);
                            } catch (Exception e) {
                                // TODO:
                            }
                            return callback.apply(entity);
                        }
                    })
                    .build()
                    .call();
        } catch (Exception e) {
            throw new PersistenceException("Failed to fetch all entites", e);
        }
    }
   
    @Override
    public List<T> find(String cql) throws PersistenceException {
        Preconditions.checkArgument(StringUtils.left(cql, 6).equalsIgnoreCase("SELECT"), "CQL must be SELECT statement");
       
        try {
            CqlResult<K, String> results = newQuery().withCql(cql).execute().getResult();
            List<T> entities = Lists.newArrayListWithExpectedSize(results.getRows().size());
            for (Row<K, String> row : results.getRows()) {
                if (!row.getColumns().isEmpty()) {
                    T entity = entityMapper.constructEntity(row.getKey(), row.getColumns());
                    lifecycleHandler.onPostLoad(entity);
                    entities.add(entity);
                }
            }
            return entities;
        } catch (Exception e) {
            throw new PersistenceException("Failed to execute cql query", e);
        }
    }
   
    private MutationBatch newMutationBatch() {
        MutationBatch mb = keyspace.prepareMutationBatch();
        if(writeConsistency != null)
            mb.withConsistencyLevel(writeConsistency);
        if(retryPolicy != null)
            mb.withRetryPolicy(retryPolicy);
        return mb;
    }
   
    private MutationBatch getMutationBatch() {
        if (autoCommit) {
            return newMutationBatch();
        }
        else {
            MutationBatch mb = tlMutation.get();
            if (mb == null) {
                mb = newMutationBatch();
                tlMutation.set(mb);
            }
            return mb;
        }
    }
   
    private ColumnFamilyQuery<K, String> newQuery() {
        ColumnFamilyQuery<K, String> cfq = keyspace.prepareQuery(columnFamily);
        if(readConsitency != null)
            cfq.setConsistencyLevel(readConsitency);
        if(retryPolicy != null)
            cfq.withRetryPolicy(retryPolicy);
        return cfq;
    }

    @Override
    public void createStorage(Map<String, Object> options) throws PersistenceException {
        try {
            keyspace.createColumnFamily(this.columnFamily, options);
        } catch (ConnectionException e) {
            if (e.getMessage().contains("already exist"))
                return;
            throw new PersistenceException("Unable to create column family " + this.columnFamily.getName(), e);
        }
    }

    @Override
    public void deleteStorage() throws PersistenceException {
        try {
            keyspace.dropColumnFamily(this.columnFamily);
        } catch (ConnectionException e) {
            throw new PersistenceException("Unable to drop column family " + this.columnFamily.getName(), e);
        }
    }

    @Override
    public void truncate() throws PersistenceException {
        try {
            keyspace.truncateColumnFamily(this.columnFamily);
        } catch (ConnectionException e) {
            throw new PersistenceException("Unable to drop column family " + this.columnFamily.getName(), e);
        }
    }

    @Override
    public void commit() throws PersistenceException {
        MutationBatch mb = tlMutation.get();
        if (mb != null) {
            try {
                mb.execute();
            } catch (ConnectionException e) {
                throw new PersistenceException("Failed to commit mutation batch", e);
            }
        }
    }

    @Override
    public NativeQuery<T, K> createNativeQuery() {
        throw new UnsupportedOperationException("Not implemented yet");
    }

}
TOP

Related Classes of com.netflix.astyanax.entitystore.DefaultEntityManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.