package com.appmetr.hercules.manager;
import com.appmetr.hercules.FieldFilter;
import com.appmetr.hercules.Hercules;
import com.appmetr.hercules.HerculesMonitoringGroup;
import com.appmetr.hercules.annotations.Id;
import com.appmetr.hercules.driver.DataDriver;
import com.appmetr.hercules.driver.HerculesMultiQueryResult;
import com.appmetr.hercules.driver.HerculesQueryResult;
import com.appmetr.hercules.driver.serializer.ByteArrayRowSerializer;
import com.appmetr.hercules.driver.serializer.ColumnRowSerializer;
import com.appmetr.hercules.driver.serializer.RowSerializer;
import com.appmetr.hercules.keys.ForeignKey;
import com.appmetr.hercules.metadata.CollectionIndexMetadata;
import com.appmetr.hercules.metadata.EntityMetadata;
import com.appmetr.hercules.metadata.ForeignKeyMetadata;
import com.appmetr.hercules.profile.DataOperationsProfile;
import com.appmetr.hercules.serializers.AbstractHerculesSerializer;
import com.appmetr.hercules.serializers.SerializerProvider;
import com.appmetr.hercules.utils.Tuple2;
import com.appmetr.hercules.wide.SliceDataSpecificator;
import com.appmetr.monblank.Monitoring;
import com.appmetr.monblank.StopWatch;
import com.google.inject.Inject;
import me.prettyprint.cassandra.serializers.BytesArraySerializer;
import me.prettyprint.cassandra.serializers.StringSerializer;
import me.prettyprint.hector.api.Serializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.*;
public class EntityManager {
private Logger logger = LoggerFactory.getLogger(EntityManager.class);
public static final String PRIMARY_KEY_CF_NAME = "PrimaryKeyIndices";
public static final String SERIALIZED_ENTITY_TOP_KEY = "entityData";
public static final String METADATA_COLUMN_NAME = "*";
@Inject private Hercules hercules;
@Inject private DataDriver dataDriver;
@Inject private IndexManager indexManager;
@Inject private SerializerProvider serializerProvider;
@Inject private Monitoring monitoring;
@Inject private EntityListenerInvocationHelper listenerInvocationHelper;
//public level
public <E, K> K getPK(E entity) {
return (K) getPrimaryKey(entity, getMetadata(entity.getClass()));
}
public <E, K> Serializer<K> getPKSerializer(Class<E> clazz) {
return getPrimaryKeySerializer(getMetadata(clazz));
}
public <E, K> E get(Class<E> clazz, K primaryKey, DataOperationsProfile dataOperationsProfile) {
if (primaryKey == null) return null;
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get by PK " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
HerculesQueryResult<String> queryResult = dataDriver.getRow(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile, getRowSerializerForEntity(metadata), primaryKey);
if (queryResult.hasResult()) {
countEntities(dataOperationsProfile, queryResult.getEntries());
return convertToEntity(clazz, primaryKey, queryResult.getEntries());
} else {
return null;
}
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: getting entity");
logger.error("Entity get by row key exception", e);
throw e;
} finally {
monitor.stop();
}
}
public <E, K> List<E> get(Class<E> clazz, Iterable<K> primaryKeys, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get list by PK " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
HerculesMultiQueryResult<K, String> queryResult = dataDriver.getRows(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile,
this.<K>getRowSerializerForEntity(metadata), primaryKeys);
if (queryResult.hasResult()) {
countEntities(dataOperationsProfile, queryResult.getEntries());
return convertToEntityList(clazz, queryResult.getEntries());
} else {
return new ArrayList<E>();
}
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: getting entities list");
logger.error("Entity get by row keys exception", e);
throw e;
} finally {
monitor.stop();
}
}
public <E, K> List<K> getKeyRange(Class<E> clazz, K from, K to, Integer count, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get key range " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
return dataDriver.getKeyRange(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile, this.<K>getRowSerializerForEntity(metadata), from, to, count);
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: getting entities key range");
logger.error("Get key range exception", e);
throw e;
} finally {
monitor.stop();
}
}
public <E, K> Tuple2<List<E>, K> getRange(Class<E> clazz, K from, K to, Integer count, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get range " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
HerculesMultiQueryResult<K, String> queryResult = dataDriver.getRangeSlice(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile,
this.<K>getRowSerializerForEntity(metadata), from, to, count, new SliceDataSpecificator<String>(null, null, false, null));
if (queryResult.hasResult()) {
countEntities(dataOperationsProfile, queryResult.getEntries());
return new Tuple2<List<E>, K>(convertToEntityList(clazz, queryResult.getEntries()), queryResult.getLastKey());
} else {
return new Tuple2<List<E>, K>(new ArrayList<E>(), queryResult.getLastKey());
}
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: getting entities range");
logger.error("Get entity range exception", e);
throw e;
} finally {
monitor.stop();
}
}
public <E> List<E> getAll(Class<E> clazz, DataOperationsProfile dataOperationsProfile) {
EntityMetadata metadata = getMetadata(clazz);
List<E> entities;
if (metadata.isCreatePrimaryKeyIndex()) {
entities = getAllByIndex(clazz, dataOperationsProfile);
} else {
entities = getAllPlain(clazz, dataOperationsProfile);
}
return entities;
}
public <E> List<E> getByFK(Class<E> clazz, ForeignKey foreignKey, DataOperationsProfile dataOperationsProfile) {
return getByFK(clazz, foreignKey, null, dataOperationsProfile);
}
public <E> E getSingleByFK(Class<E> clazz, ForeignKey foreignKey, DataOperationsProfile dataOperationsProfile) {
List<E> entites = getByFK(clazz, foreignKey, 1, dataOperationsProfile);
return entites.size() > 0 ? entites.get(0) : null;
}
public <E, U> List<E> getByCollectionIndex(Class<E> clazz, String indexedFieldame, U indexValue, DataOperationsProfile dataOperationsProfile) {
return getByCollectionIndex(clazz, indexedFieldame, indexValue, null, dataOperationsProfile);
}
public <E, U> E getSingleByCollectionIndex(Class<E> clazz, String indexedFieldName, U indexValue, DataOperationsProfile dataOperationsProfile) {
List<E> entites = getByCollectionIndex(clazz, indexedFieldName, indexValue, 1, dataOperationsProfile);
return entites.size() > 0 ? entites.get(0) : null;
}
public <E> int getCountByFK(Class<E> clazz, ForeignKey foreignKey, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get count by FK " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
ForeignKeyMetadata foreignKeyMetadata = metadata.getIndexMetadata(foreignKey.getClass());
return dataDriver.getTopCount(hercules.getKeyspace(), foreignKeyMetadata.getColumnFamily(), dataOperationsProfile,
new ByteArrayRowSerializer<Object, Object>(
getForeignKeySerializer(foreignKeyMetadata),
getPrimaryKeySerializer(metadata)),
foreignKey, null, null, null
);
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: getting count by FK");
logger.error("Get count by FK exception", e);
throw e;
} finally {
monitor.stop();
}
}
public <E> void save(E entity, int ttl, DataOperationsProfile dataOperationsProfile) {
EntityMetadata metadata = getMetadata(entity.getClass());
Object primaryKey = getPrimaryKey(entity, metadata);
save(primaryKey, entity, null, ttl, dataOperationsProfile);
}
public <E> void save(E entity, FieldFilter fieldFilter, DataOperationsProfile dataOperationsProfile) {
EntityMetadata metadata = getMetadata(entity.getClass());
Object primaryKey = getPrimaryKey(entity, metadata);
save(primaryKey, entity, fieldFilter, null, dataOperationsProfile);
}
public <E, K> void save(K primaryKey, E entity, int ttl, DataOperationsProfile dataOperationsProfile) {
save(primaryKey, entity, null, ttl, dataOperationsProfile);
}
public <E, K> void save(K primaryKey, E entity, FieldFilter fieldFilter, DataOperationsProfile dataOperationsProfile) {
save(primaryKey, entity, fieldFilter, null, dataOperationsProfile);
}
private <E, K> void save(K primaryKey, E entity, FieldFilter fieldFilter, Integer ttl, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Save " + entity.getClass().getSimpleName());
try {
EntityMetadata metadata = getMetadata(entity.getClass());
E prePersistResult = listenerInvocationHelper.invokePrePersistListener(metadata.getListenerMetadata(), entity);
if (prePersistResult != null) {
entity = prePersistResult;
}
if (primaryKey == null && metadata.isPrimaryKeyGenerated()) {
try {
Field primaryKeyField = metadata.getPrimaryKeyMetadata().getField();
UUID uid = UUID.randomUUID();
if (metadata.getPrimaryKeyMetadata().getKeyClass().isAssignableFrom(UUID.class)) {
primaryKey = (K) uid;
} else if (metadata.getPrimaryKeyMetadata().getKeyClass().isAssignableFrom(String.class)) {
primaryKey = (K) uid.toString();
} else {
String primaryKeyFieldClassName = primaryKeyField == null ? null : primaryKeyField.getClass().getName();
throw new RuntimeException(MessageFormat.format("Currently we support only java.util.UUID and String id generation, but entity of type {0} have id with class {1}", entity.getClass().getName(), primaryKeyFieldClassName));
}
if (primaryKeyField != null) {
metadata.getPrimaryKeyMetadata().getField().set(entity, primaryKey);
}
} catch (IllegalAccessException ex) {
String descr = "Unable to set generated id for entity: " + entity.getClass();
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: " + descr);
throw new RuntimeException(descr, ex);
}
}
E oldEntity = null;
if (metadata.getIndexes().size() > 0 || metadata.getCollectionIndexes().size()>0) {
HerculesQueryResult<String> queryResult = dataDriver.getRow(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile, getRowSerializerForEntity(metadata), primaryKey);
if (queryResult.hasResult()) {
countEntities(dataOperationsProfile, queryResult.getEntries());
oldEntity = (E) this.<E, K>convertToEntity(metadata.getEntityClass(), primaryKey, queryResult.getEntries());
}
}
saveEntity(primaryKey, entity, fieldFilter, ttl, dataOperationsProfile);
indexManager.updateIndexOnSave(entity, oldEntity, dataOperationsProfile);
listenerInvocationHelper.invokePostPersistListener(metadata.getListenerMetadata(), entity);
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: saving entity");
logger.error("Entity save exception" + (entity != null ? ". Entity class:" + entity.getClass().getSimpleName() : ""), e);
throw e;
} finally {
monitor.stop();
}
}
public <E> void delete(E entity, DataOperationsProfile dataOperationsProfile) {
if (entity == null) return;
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Delete " + entity.getClass().getSimpleName());
try {
EntityMetadata metadata = getMetadata(entity.getClass());
E preDeleteResult = listenerInvocationHelper.invokePreDeleteListener(metadata.getListenerMetadata(), entity);
if (preDeleteResult != null) {
entity = preDeleteResult;
}
Object primaryKey = getPrimaryKey(entity, metadata);
delete(primaryKey, getMetadata(metadata.getEntityClass()), dataOperationsProfile);
listenerInvocationHelper.invokePostDeleteListener(metadata.getListenerMetadata(), entity);
indexManager.updateIndexOnDelete(entity, dataOperationsProfile);
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: deleting exception");
logger.error("Entity delete exception", e);
throw e;
} finally {
monitor.stop();
}
}
public <K> void delete(Class clazz, K primaryKey, DataOperationsProfile dataOperationsProfile) {
//Can't delete entity by PK directly, cause we need to delete indexes
delete(get(clazz, primaryKey, dataOperationsProfile), dataOperationsProfile);
}
//package level
EntityMetadata getMetadata(Class entityClass) {
return hercules.getMetadata(entityClass);
}
<E, T> T getPrimaryKey(E entity, EntityMetadata metadata) {
final Field primaryKeyField = metadata.getPrimaryKeyMetadata().getField();
if (primaryKeyField == null) {
throw new RuntimeException(Id.class.getSimpleName() + " field isn't declared for entity " + entity.getClass() + " in range query");
}
try {
return (T) primaryKeyField.get(entity);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
<K> Serializer<K> getPrimaryKeySerializer(EntityMetadata metadata) {
Class keyClass = metadata.getPrimaryKeyMetadata().getKeyClass();
Class keySerializer = metadata.getPrimaryKeyMetadata().getSerializer();
return keySerializer == null ?
dataDriver.<K>getSerializerForClass(keyClass) :
serializerProvider.<K>getSerializer(keySerializer, keyClass);
}
<K> Serializer<K> getForeignKeySerializer(ForeignKeyMetadata metadata) {
return serializerProvider.<K>getSerializer(metadata.getSerializer(), metadata.getKeyClass());
}
<E, F> F getForeignKeyFromEntity(E entity, EntityMetadata metadata, Class<? extends ForeignKey> foreignKeyClass) {
ForeignKeyMetadata keyMetadata = metadata.getIndexMetadata(foreignKeyClass);
try {
F foreignKey = (F) foreignKeyClass.newInstance();
boolean notNullValueExist = false;
for (Field keyField : keyMetadata.getKeyFields()) {
try {
//try to find field in entity
Field entityField = metadata.getEntityClass().getDeclaredField(keyField.getName());
entityField.setAccessible(true);
Object fieldValue = entityField.get(entity);
keyField.set(foreignKey, fieldValue);
if (fieldValue != null) {
notNullValueExist = true;
}
} catch (NoSuchFieldException noField) {
//try to find field in entity key
Field primaryKeyField = metadata.getPrimaryKeyMetadata().getKeyClass().getDeclaredField(keyField.getName());
primaryKeyField.setAccessible(true);
Object fieldValue = primaryKeyField.get(metadata.getPrimaryKeyMetadata().getField().get(entity));
keyField.set(foreignKey, fieldValue);
if (fieldValue != null) {
notNullValueExist = true;
}
}
}
return notNullValueExist ? foreignKey : null;
} catch (NoSuchFieldException e) {
throw new RuntimeException("Failed to serialize foreign key of class " + foreignKeyClass.getName() + " and entity class " + entity.getClass().getName(), e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to serialize foreign key of class " + foreignKeyClass.getName() + " and entity class " + entity.getClass().getName(), e);
} catch (InstantiationException e) {
throw new RuntimeException("Failed to serialize foreign key of class " + foreignKeyClass.getName() + " and entity class " + entity.getClass().getName(), e);
}
}
//private level
private <E, K> List<E> getAllPlain(Class<E> clazz, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get all plain " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
HerculesMultiQueryResult<K, String> result = dataDriver.getAllRows(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile, this.<K>getRowSerializerForEntity(metadata));
List<E> entities = new ArrayList<E>();
if (result.hasResult()) {
countEntities(dataOperationsProfile, result.getEntries());
for (K primaryKey : result.getEntries().keySet()) {
entities.add(convertToEntity(clazz, primaryKey, result.getEntries().get(primaryKey)));
}
}
return entities;
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: get all plain exception");
logger.error("Entity get all plain exception", e);
throw e;
} finally {
monitor.stop();
}
}
private <E, K> List<E> getAllByIndex(Class<E> clazz, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get all by index " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
HerculesQueryResult<K> queryResult = dataDriver.getRow(
hercules.getKeyspace(),
PRIMARY_KEY_CF_NAME,
dataOperationsProfile,
new ByteArrayRowSerializer<String, K>(StringSerializer.get(), this.<K>getPrimaryKeySerializer(metadata)),
metadata.getColumnFamily());
List<E> entities = new ArrayList<E>();
if (queryResult.hasResult()) {
countEntities(dataOperationsProfile, queryResult.getEntries());
Set<K> indexes = queryResult.getEntries().keySet();
if (indexes.size() > 0) {
HerculesMultiQueryResult<K, String> entitiesQueryResult = dataDriver.getRows(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile,
this.<K>getRowSerializerForEntity(metadata), indexes);
countEntities(dataOperationsProfile, entitiesQueryResult.getEntries());
for (Map.Entry<K, LinkedHashMap<String, Object>> entry : entitiesQueryResult.getEntries().entrySet()) {
entities.add(convertToEntity(clazz, entry.getKey(), entry.getValue()));
}
}
}
return entities;
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: get all by index exception");
logger.error("Entity get all by index exception", e);
throw e;
} finally {
monitor.stop();
}
}
private <E, K> List<E> getByFK(Class<E> clazz, ForeignKey foreignKey, Integer count, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get list by FK " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
ForeignKeyMetadata foreignKeyMetadata = metadata.getIndexMetadata(foreignKey.getClass());
HerculesQueryResult<K> queryResult = dataDriver.getSlice(hercules.getKeyspace(), foreignKeyMetadata.getColumnFamily(), dataOperationsProfile,
new ByteArrayRowSerializer<Object, K>(getForeignKeySerializer(metadata.getIndexMetadata(foreignKey.getClass())), this.<K>getPrimaryKeySerializer(metadata)),
foreignKey, new SliceDataSpecificator<K>(null, null, false, count));
if (queryResult.hasResult()) {
countEntities(dataOperationsProfile, queryResult.getEntries());
//filtering using real entity fk
List<E> candidates = get(clazz, queryResult.getEntries().keySet(), dataOperationsProfile);
List<E> entities = new ArrayList<E>(candidates.size());
for (E candidate : candidates) {
ForeignKey candidateKey = getForeignKeyFromEntity(candidate, metadata, foreignKey.getClass());
if (!foreignKey.equals(candidateKey)) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: invalid FK");
logger.error(String.format("Invalid FK: FK class %1$s, FK %2$s, entity class %3$s, PK %4$s",
foreignKey.getClass().getSimpleName(), foreignKey, clazz.getSimpleName(), getPrimaryKey(candidate, metadata)));
continue;
}
entities.add(candidate);
}
return entities;
} else {
return new ArrayList<E>();
}
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: get list by FK exception");
logger.error("Entity get list by FK exception", e);
throw e;
} finally {
monitor.stop();
}
}
private <E, K, U> List<E> getByCollectionIndex(Class<E> clazz, String indexedFieldName, U indexValue, Integer count, DataOperationsProfile dataOperationsProfile) {
StopWatch monitor = monitoring.start(HerculesMonitoringGroup.HERCULES_EM, "Get list by CollectionIndex " + clazz.getSimpleName());
try {
EntityMetadata metadata = getMetadata(clazz);
CollectionIndexMetadata indexMetadata = metadata.getCollectionIndexes().get(indexedFieldName);
HerculesQueryResult<K> queryResult = dataDriver.getSlice(hercules.getKeyspace(), indexMetadata.getIndexColumnFamily(), dataOperationsProfile,
new ByteArrayRowSerializer<Object, K>(indexMetadata.getKeyExtractor().getKeySerializer(), this.<K>getPrimaryKeySerializer(metadata)),
indexValue, new SliceDataSpecificator<K>(null, null, false, count));
if (queryResult.hasResult()) {
countEntities(dataOperationsProfile, queryResult.getEntries());
//filtering using real entity fk
List<E> candidates = get(clazz, queryResult.getEntries().keySet(), dataOperationsProfile);
List<E> entities = new ArrayList<E>(candidates.size());
//TODO probably it could be removed. there is no errors in monitoring
for (E candidate : candidates) {
boolean found = false;
for (Object key : indexMetadata.getKeyExtractor().extractKeys(candidate)) {
if (indexValue.equals(key)) {
found = true;
break;
}
}
if (!found) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: invalid CollectionIndex");
logger.error(String.format("Invalid CollectionIndex: index field %1$s, value %2$s, entity class %3$s, PK %4$s",
indexedFieldName, indexValue, clazz.getSimpleName(), getPrimaryKey(candidate, metadata)));
continue;
}
entities.add(candidate);
}
return entities;
} else {
return new ArrayList<E>();
}
} catch (RuntimeException e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error: get list by CollectionIndex exception");
logger.error("Entity get list by CollectionIndex exception", e);
throw e;
} finally {
monitor.stop();
}
}
private <K> void delete(K primaryKey, EntityMetadata metadata, DataOperationsProfile dataOperationsProfile) {
dataDriver.delete(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile,
new ByteArrayRowSerializer<K, String>(this.<K>getPrimaryKeySerializer(metadata), dataDriver.<String>getSerializerForClass(String.class)),
primaryKey);
}
private <E, K> E convertToEntity(Class<E> clazz, K primaryKey, Map<String, Object> values) {
try {
EntityMetadata metadata = getMetadata(clazz);
if (!metadata.getEntityClass().equals(clazz)) {
throw new RuntimeException(MessageFormat.format("Specified entity class differ from metadata class. entity: {0}, metadata: {1}", clazz, metadata.getEntityClass()));
}
if (metadata.getEntitySerializer() != null) {
if (!values.containsKey(SERIALIZED_ENTITY_TOP_KEY)) {
throw new RuntimeException("Serialized entity data column doesn't exist for entity class " +
metadata.getEntityClass().getSimpleName());
}
E entity = (E) values.get(SERIALIZED_ENTITY_TOP_KEY);
//Set PK from parameter
metadata.getPrimaryKeyMetadata().getField().set(entity, primaryKey);
return entity;
} else {
Constructor<E> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
E entity = constructor.newInstance();
E preLoadResult = listenerInvocationHelper.invokePreLoadListener(metadata.getListenerMetadata(), entity);
if (preLoadResult != null) entity = preLoadResult;
try {
HashSet<String> processedColumns = new HashSet<String>();
metadata.getPrimaryKeyMetadata().getField().set(entity, primaryKey);
for (Map.Entry<Field, String> entry : metadata.getFieldToColumn().entrySet()) {
String column = entry.getValue();
if (processedColumns.contains(column)) {
continue;
}
if (!values.containsKey(column) && metadata.getColumnClass(column).isPrimitive()) {
// we can not write null to primitive column type, so leave it with
// initial value
processedColumns.add(column);
continue;
}
Object columnValue = values.get(column);
Object entityDefaultValue = entry.getKey().get(entity);
if (columnValue == null && metadata.isNotNullColumn(column)) {
if (entityDefaultValue == null) {
throw new RuntimeException("Default value empty for entity class " +
metadata.getEntityClass().getSimpleName() + " field " + column);
}
columnValue = entityDefaultValue;
}
entry.getKey().set(entity, columnValue);
processedColumns.add(column);
}
} catch (IllegalAccessException e) {
throwConvertError(clazz, values, e);
}
E postLoadResult = listenerInvocationHelper.invokePostLoadListener(metadata.getListenerMetadata(), entity);
return postLoadResult == null ? entity : postLoadResult;
}
} catch (InstantiationException e) {
return throwConvertError(clazz, values, e);
} catch (IllegalAccessException e) {
return throwConvertError(clazz, values, e);
} catch (NoSuchMethodException e) {
return throwConvertError(clazz, values, e);
} catch (InvocationTargetException e) {
return throwConvertError(clazz, values, e);
}
}
private <E, K> List<E> convertToEntityList(Class<E> clazz, Map<K, LinkedHashMap<String, Object>> values) {
List<E> entities = new ArrayList<E>();
for (Map.Entry<K, LinkedHashMap<String, Object>> entry : values.entrySet()) {
E entity = convertToEntity(clazz, entry.getKey(), entry.getValue());
if (entity != null)
entities.add(entity);
}
return entities;
}
private <E> E throwConvertError(Class<E> clazz, Map<String, Object> values, Exception e) {
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, "Error converting map to " + clazz.getName());
String descr = "Failed to convert map to " + clazz.getName() + " Values Map: " + values.toString();
throw new RuntimeException(descr, e);
}
private <E, K> void saveEntity(K primaryKey, E entity, FieldFilter fieldFilter, Integer ttl, DataOperationsProfile dataOperationsProfile) {
EntityMetadata metadata = getMetadata(entity.getClass());
if (!entity.getClass().equals(metadata.getEntityClass())) {
throw new RuntimeException(MessageFormat.format("Specified entity class differ from metadata class. entity: {0}, metadata: {1}", entity.getClass(), metadata.getEntityClass()));
}
try {
Map<String, Object> values = new HashMap<String, Object>();
int timeToLive = ttl == null ? metadata.getEntityTTL() : ttl;
Class<? extends AbstractHerculesSerializer> entitySerializerClass = metadata.getEntitySerializer();
if (fieldFilter != null) {
if (entitySerializerClass != null) {
throw new RuntimeException(MessageFormat.format("Selective save doesn't support for entity {0} with serializers", entity.getClass().getSimpleName()));
}
}
if (entitySerializerClass != null) {
values.put(SERIALIZED_ENTITY_TOP_KEY, entity);
} else {
boolean notNullValueExisted = false;
for (Map.Entry<Field, String> entry : metadata.getFieldToColumn().entrySet()) {
Object value = entry.getKey().get(entity);
if (fieldFilter == null || fieldFilter.accept(entry.getKey())) {
if (value != null) {
notNullValueExisted = true;
}
values.put(entry.getValue(), value);
}
}
if (!notNullValueExisted) {
values.put(METADATA_COLUMN_NAME, new byte[0]);
} else {
values.put(METADATA_COLUMN_NAME, null);
}
}
dataDriver.insert(hercules.getKeyspace(), metadata.getColumnFamily(), dataOperationsProfile,
getRowSerializerForEntity(metadata), primaryKey, values, timeToLive);
} catch (IllegalAccessException e) {
String msg = "Error: build value map for " + entity.getClass().getName();
monitoring.inc(HerculesMonitoringGroup.HERCULES_EM, msg);
throw new RuntimeException(msg, e);
}
}
private Serializer getFieldSerializer(String fieldName, EntityMetadata metadata) {
Class<? extends AbstractHerculesSerializer> serializerClass = metadata.getColumnSerializer(fieldName);
if (serializerClass != null) {
return serializerProvider.getSerializer(serializerClass, metadata.getColumnClass(fieldName));
}
return dataDriver.getSerializerForClass(metadata.getColumnClass(fieldName));
}
private <K> RowSerializer<K, String> getRowSerializerForEntity(EntityMetadata metadata) {
Map<String, Serializer> columnSerializers = new HashMap<String, Serializer>();
if (metadata.getEntitySerializer() != null) {
AbstractHerculesSerializer entitySerializer = serializerProvider.getSerializer(metadata.getEntitySerializer(), metadata.getEntityClass());
columnSerializers.put(SERIALIZED_ENTITY_TOP_KEY, entitySerializer);
} else {
for (Map.Entry<String, Class> entry : metadata.getColumnClasses().entrySet()) {
Serializer fieldSerializer = getFieldSerializer(entry.getKey(), metadata);
columnSerializers.put(entry.getKey(), fieldSerializer);
}
}
columnSerializers.put(METADATA_COLUMN_NAME, BytesArraySerializer.get());
return new ColumnRowSerializer<K, String>(this.<K>getPrimaryKeySerializer(metadata), StringSerializer.get(), columnSerializers);
}
private void countEntities(DataOperationsProfile dataOperationsProfile, LinkedHashMap entries) {
if (dataOperationsProfile != null) {
dataOperationsProfile.count += entries.size();
}
}
}