All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.netflix.astyanax.entitystore.EntityMapper Maven / Gradle / Ivy
package com.netflix.astyanax.entitystore;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.PersistenceException;
import org.apache.commons.lang.StringUtils;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.netflix.astyanax.ColumnListMutation;
import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.model.ColumnList;
/**
* utility class to map btw root Entity and cassandra data model
* @param entity type
* @param rowKey type
*/
public class EntityMapper {
private final Class clazz;
private final Integer ttl;
private final Method ttlMethod;
private final Field idField;
private final Map columnList;
private final ColumnMapper uniqueColumn;
private final String entityName;
/**
*
* @param clazz
* @throws IllegalArgumentException
* if clazz is NOT annotated with @Entity
* if column name contains illegal char (like dot)
*/
public EntityMapper(Class clazz, Integer ttl) {
this.clazz = clazz;
// clazz should be annotated with @Entity
Entity entityAnnotation = clazz.getAnnotation(Entity.class);
if(entityAnnotation == null)
throw new IllegalArgumentException("class is NOT annotated with @javax.persistence.Entity: " + clazz.getName());
entityName = MappingUtils.getEntityName(entityAnnotation, clazz);
// TTL value from constructor or class-level annotation
Integer tmpTtlValue = ttl;
if(tmpTtlValue == null) {
// constructor value has higher priority
// try @TTL annotation at entity/class level.
// it doesn't make sense to support @TTL annotation at individual column level.
TTL ttlAnnotation = clazz.getAnnotation(TTL.class);
if(ttlAnnotation != null) {
int ttlAnnotationValue = ttlAnnotation.value();
Preconditions.checkState(ttlAnnotationValue > 0, "cannot define non-positive value for TTL annotation at class level: " + ttlAnnotationValue);
tmpTtlValue = ttlAnnotationValue;
}
}
this.ttl = tmpTtlValue;
// TTL method
Method tmpTtlMethod = null;
for (Method method : this.clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(TTL.class)) {
Preconditions.checkState(tmpTtlMethod == null, "Duplicate TTL method annotation on " + method.getName());
tmpTtlMethod = method;
tmpTtlMethod.setAccessible(true);
}
}
this.ttlMethod = tmpTtlMethod;
Field[] declaredFields = clazz.getDeclaredFields();
columnList = Maps.newHashMapWithExpectedSize(declaredFields.length);
Set usedColumnNames = Sets.newHashSet();
Field tmpIdField = null;
ColumnMapper tempUniqueMapper = null;
for (Field field : declaredFields) {
Id idAnnotation = field.getAnnotation(Id.class);
if(idAnnotation != null) {
Preconditions.checkArgument(tmpIdField == null, "there are multiple fields with @Id annotation");
field.setAccessible(true);
tmpIdField = field;
}
Column columnAnnotation = field.getAnnotation(Column.class);
if ((columnAnnotation != null)) {
field.setAccessible(true);
ColumnMapper columnMapper = null;
Entity compositeAnnotation = field.getType().getAnnotation(Entity.class);
if (Map.class.isAssignableFrom(field.getType())) {
columnMapper = new MapColumnMapper(field);
} else if (Set.class.isAssignableFrom(field.getType())) {
columnMapper = new SetColumnMapper(field);
} else if(compositeAnnotation == null) {
if (columnAnnotation.unique()) {
Preconditions.checkArgument(tempUniqueMapper == null, "can't have multiple unique columns '" + field.getName() + "'");
tempUniqueMapper = new LeafColumnMapper(field);
}
else {
columnMapper = new LeafColumnMapper(field);
}
} else {
columnMapper = new CompositeColumnMapper(field);
}
Preconditions.checkArgument(!usedColumnNames.contains(columnMapper.getColumnName()),
String.format("duplicate case-insensitive column name: %s", columnMapper.getColumnName().toLowerCase()));
columnList.put(columnMapper.getColumnName(), columnMapper);
usedColumnNames.add(columnMapper.getColumnName().toLowerCase());
}
}
Preconditions.checkNotNull(tmpIdField, "there are no field with @Id annotation");
//Preconditions.checkArgument(tmpIdField.getClass().equals(K.getClass()), String.format("@Id field type (%s) doesn't match generic type K (%s)", tmpIdField.getClass(), K.getClass()));
idField = tmpIdField;
uniqueColumn = tempUniqueMapper;
}
public void fillMutationBatch(MutationBatch mb, ColumnFamily columnFamily, T entity) {
try {
@SuppressWarnings("unchecked")
K rowKey = (K) idField.get(entity);
ColumnListMutation clm = mb.withRow(columnFamily, rowKey);
clm.setDefaultTtl(getTtl(entity));
for (ColumnMapper mapper : columnList.values()) {
mapper.fillMutationBatch(entity, clm, "");
}
} catch(Exception e) {
throw new PersistenceException("failed to fill mutation batch", e);
}
}
private Integer getTtl(T entity) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Integer retTtl = this.ttl;
// TTL method has higher priority
if(ttlMethod != null) {
Object retobj = ttlMethod.invoke(entity);
retTtl = (Integer) retobj;
}
return retTtl;
}
public T constructEntity(K id, ColumnList cl) {
try {
T entity = clazz.newInstance();
idField.set(entity, id);
for (com.netflix.astyanax.model.Column column : cl) {
List name = Lists.newArrayList(StringUtils.split(column.getName(), "."));
setField(entity, name.iterator(), column);
}
for (ColumnMapper column : columnList.values()) {
column.validate(entity);
}
return entity;
} catch(Exception e) {
throw new PersistenceException("failed to construct entity", e);
}
}
void setField(T entity, Iterator name, com.netflix.astyanax.model.Column column) throws Exception {
String fieldName = name.next();
ColumnMapper mapper = this.columnList.get(fieldName);
if (mapper != null)
mapper.setField(entity, name, column);
}
@SuppressWarnings("unchecked")
public K getEntityId(T entity) throws Exception {
return (K)idField.get(entity);
}
@VisibleForTesting
Field getId() {
return idField;
}
@VisibleForTesting
Collection getColumnList() {
return columnList.values();
}
public String getEntityName() {
return entityName;
}
@Override
public String toString() {
return String.format("EntityMapper(%s)", clazz);
}
}