Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Mapping: Per-column and per-class TTL specification added.

Existing implementations of AnnotationSet not affected because new
functionality added in extension AnnotationSet2.
  • Loading branch information...
commit 2c1da7340fe0d750b12fec55afeed5e6f628089b 1 parent 48a764f
@georgkoester authored
View
13 src/main/java/com/netflix/astyanax/mapping/AnnotationSet2.java
@@ -0,0 +1,13 @@
+package com.netflix.astyanax.mapping;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+/**
+ * Allows for any annotations to be used to mark columns in a bean
+ */
+public interface AnnotationSet2<ID extends Annotation, COLUMN extends Annotation>
+ extends AnnotationSet<ID, COLUMN> {
+
+ public int getColumnTtl(Field field, COLUMN annotation);
+}
View
27 src/main/java/com/netflix/astyanax/mapping/Coercions.java
@@ -49,37 +49,42 @@
static <T> void setColumnMutationFromField(T instance, Field field,
String columnName, ColumnListMutation<String> mutation) {
+ setColumnMutationFromField(instance, field, columnName, mutation, null);
+ }
+
+ static <T> void setColumnMutationFromField(T instance, Field field,
+ String columnName, ColumnListMutation<String> mutation, Integer ttl) {
try {
Object objValue = field.get(instance);
if (objValue != null) {
if ((objValue.getClass() == Byte.class)
|| (objValue.getClass() == Byte.TYPE)) {
- mutation.putColumn(columnName, (Byte) objValue & 0xff, null);
+ mutation.putColumn(columnName, (Byte) objValue & 0xff, ttl);
} else if ((objValue.getClass() == Boolean.class)
|| (objValue.getClass() == Boolean.TYPE)) {
- mutation.putColumn(columnName, (Boolean) objValue, null);
+ mutation.putColumn(columnName, (Boolean) objValue, ttl);
} else if ((objValue.getClass() == Short.class)
|| (objValue.getClass() == Short.TYPE)) {
- mutation.putColumn(columnName, (Short) objValue, null);
+ mutation.putColumn(columnName, (Short) objValue, ttl);
} else if ((objValue.getClass() == Integer.class)
|| (objValue.getClass() == Integer.TYPE)) {
- mutation.putColumn(columnName, (Integer) objValue, null);
+ mutation.putColumn(columnName, (Integer) objValue, ttl);
} else if ((objValue.getClass() == Long.class)
|| (objValue.getClass() == Long.TYPE)) {
- mutation.putColumn(columnName, (Long) objValue, null);
+ mutation.putColumn(columnName, (Long) objValue, ttl);
} else if ((objValue.getClass() == Float.class)
|| (objValue.getClass() == Float.TYPE)) {
- mutation.putColumn(columnName, (Float) objValue, null);
+ mutation.putColumn(columnName, (Float) objValue, ttl);
} else if ((objValue.getClass() == Double.class)
|| (objValue.getClass() == Double.TYPE)) {
- mutation.putColumn(columnName, (Double) objValue, null);
+ mutation.putColumn(columnName, (Double) objValue, ttl);
} else if (objValue.getClass() == Date.class) {
- mutation.putColumn(columnName, (Date) objValue, null);
+ mutation.putColumn(columnName, (Date) objValue, ttl);
} else if (objValue.getClass() == String.class) {
- mutation.putColumn(columnName, (String) objValue, null);
- } else if(objValue.getClass() == byte[].class) {
- mutation.putColumn(columnName, (byte[]) objValue, null);
+ mutation.putColumn(columnName, (String) objValue, ttl);
+ } else if (objValue.getClass() == byte[].class) {
+ mutation.putColumn(columnName, (byte[]) objValue, ttl);
} else {
throw new UnsupportedOperationException();
}
View
16 src/main/java/com/netflix/astyanax/mapping/Column.java
@@ -1,6 +1,10 @@
package com.netflix.astyanax.mapping;
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
@Documented
@Target({ ElementType.FIELD })
@@ -13,4 +17,14 @@
* @return column name
*/
String value() default "";
+
+ /**
+ * The time-to-live in seconds with which this particular field should be
+ * persisted as. By default no expiration deadline (ttl) is set. Setting a
+ * value of <code>0</code> disables expiration for this column, overriding a
+ * class-wide setting on {@link DefaultAnnotationSet}.
+ *
+ * @return
+ */
+ int ttl() default -1;
}
View
29 src/main/java/com/netflix/astyanax/mapping/DefaultAnnotationSet.java
@@ -5,7 +5,24 @@
/**
* The default annotation set. Supports {@link Id} and {@link Column}
*/
-public class DefaultAnnotationSet implements AnnotationSet<Id, Column> {
+public class DefaultAnnotationSet implements AnnotationSet2<Id, Column> {
+
+ private Integer defaultTtl = null;
+
+ public DefaultAnnotationSet() {
+ this(null);
+ }
+
+ /**
+ * Specify a default time-to-live for columns with no own ttl specification.
+ *
+ * @param defaultColumnTtl
+ * a negative value or 0 disables default ttl setting
+ */
+ public DefaultAnnotationSet(Integer defaultColumnTtl) {
+ this.defaultTtl = defaultColumnTtl;
+ }
+
@Override
public Class<Id> getIdAnnotation() {
return Id.class;
@@ -27,4 +44,14 @@ public String getColumnName(Field field, Column annotation) {
String name = annotation.value();
return (name.length() > 0) ? name : field.getName();
}
+
+ @Override
+ public int getColumnTtl(Field field, Column annotation) {
+ if (annotation.ttl() > -1) {
+ return annotation.ttl();
+ } else if (defaultTtl != null && defaultTtl.intValue() > 0) {
+ return defaultTtl.intValue();
+ }
+ return -1;
+ }
}
View
30 src/main/java/com/netflix/astyanax/mapping/FieldMapping.java
@@ -0,0 +1,30 @@
+package com.netflix.astyanax.mapping;
+
+import java.lang.reflect.Field;
+
+public class FieldMapping {
+
+ private Field field;
+
+ private Integer ttl = null;
+
+ public FieldMapping() {
+
+ }
+
+ public Field getField() {
+ return field;
+ }
+
+ public void setField(Field field) {
+ this.field = field;
+ }
+
+ public Integer getTtl() {
+ return ttl;
+ }
+
+ public void setTtl(Integer ttl) {
+ this.ttl = ttl;
+ }
+}
View
48 src/main/java/com/netflix/astyanax/mapping/Mapping.java
@@ -30,7 +30,7 @@
*/
@SuppressWarnings({ "SuspiciousMethodCalls" })
public class Mapping<T> {
- private final ImmutableMap<String, Field> fields;
+ private final ImmutableMap<String, FieldMapping> fields;
private final String idFieldName;
private final Class<T> clazz;
@@ -83,7 +83,8 @@ public Mapping(Class<T> clazz, AnnotationSet<?, ?> annotationSet) {
this.clazz = clazz;
String localKeyFieldName = null;
- ImmutableMap.Builder<String, Field> builder = ImmutableMap.builder();
+ ImmutableMap.Builder<String, FieldMapping> builder = ImmutableMap
+ .builder();
AtomicBoolean isKey = new AtomicBoolean();
Set<String> usedNames = Sets.newHashSet();
@@ -131,13 +132,13 @@ public Mapping(Class<T> clazz, AnnotationSet<?, ?> annotationSet) {
*/
public <V> V getColumnValue(T instance, String columnName,
Class<V> valueClass) {
- Field field = fields.get(columnName);
- if (field == null) {
+ FieldMapping fieldMapping = fields.get(columnName);
+ if (fieldMapping == null) {
throw new IllegalArgumentException("Column not found: "
+ columnName);
}
try {
- return valueClass.cast(field.get(instance));
+ return valueClass.cast(fieldMapping.getField().get(instance));
} catch (IllegalAccessException e) {
throw new RuntimeException(e); // should never get here
}
@@ -169,13 +170,13 @@ public Mapping(Class<T> clazz, AnnotationSet<?, ?> annotationSet) {
* class)
*/
public <V> void setColumnValue(T instance, String columnName, V value) {
- Field field = fields.get(columnName);
- if (field == null) {
+ FieldMapping fieldMapping = fields.get(columnName);
+ if (fieldMapping == null) {
throw new IllegalArgumentException("Column not found: "
+ columnName);
}
try {
- field.set(instance, value);
+ fieldMapping.getField().set(instance, value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e); // should never get here
}
@@ -192,8 +193,10 @@ public Mapping(Class<T> clazz, AnnotationSet<?, ?> annotationSet) {
*/
public void fillMutation(T instance, ColumnListMutation<String> mutation) {
for (String fieldName : getNames()) {
+ FieldMapping fieldMapping = fields.get(fieldName);
Coercions.setColumnMutationFromField(instance,
- fields.get(fieldName), fieldName, mutation);
+ fieldMapping.getField(), fieldName, mutation,
+ fieldMapping.getTtl());
}
}
@@ -225,10 +228,11 @@ public T newInstance(ColumnList<String> columns)
*/
public T initInstance(T instance, ColumnList<String> columns) {
for (com.netflix.astyanax.model.Column<String> column : columns) {
- Field field = fields.get(column.getName());
- if (field != null) { // otherwise it may be a column that was
- // removed, etc.
- Coercions.setFieldFromColumn(instance, field, column);
+ FieldMapping fieldMapping = fields.get(column.getName());
+ if (fieldMapping != null) { // otherwise it may be a column that was
+ // removed, etc.
+ Coercions.setFieldFromColumn(instance, fieldMapping.getField(),
+ column);
}
}
return instance;
@@ -267,13 +271,13 @@ public T initInstance(T instance, ColumnList<String> columns) {
}
Class<?> getIdFieldClass() {
- return fields.get(idFieldName).getType();
+ return fields.get(idFieldName).getField().getType();
}
private <ID extends Annotation, COLUMN extends Annotation> String mapField(
Field field, AnnotationSet<ID, COLUMN> annotationSet,
- ImmutableMap.Builder<String, Field> builder, Set<String> usedNames,
- AtomicBoolean isKey) {
+ ImmutableMap.Builder<String, FieldMapping> builder,
+ Set<String> usedNames, AtomicBoolean isKey) {
String mappingName = null;
ID idAnnotation = field.getAnnotation(annotationSet.getIdAnnotation());
@@ -293,8 +297,13 @@ public T initInstance(T instance, ColumnList<String> columns) {
isKey.set(false);
}
+ int ttl = -1;
if ((columnAnnotation != null)) {
mappingName = annotationSet.getColumnName(field, columnAnnotation);
+ if (annotationSet instanceof AnnotationSet2) {
+ ttl = ((AnnotationSet2<ID, COLUMN>) annotationSet)
+ .getColumnTtl(field, columnAnnotation);
+ }
}
if (mappingName != null) {
@@ -304,7 +313,12 @@ public T initInstance(T instance, ColumnList<String> columns) {
usedNames.add(mappingName.toLowerCase());
field.setAccessible(true);
- builder.put(mappingName, field);
+ FieldMapping fieldMapping = new FieldMapping();
+ fieldMapping.setField(field);
+ if (ttl > -1) {
+ fieldMapping.setTtl(ttl);
+ }
+ builder.put(mappingName, fieldMapping);
}
return mappingName;
View
237 src/test/java/com/netflix/astyanax/mapping/TestMapping.java
@@ -1,9 +1,22 @@
package com.netflix.astyanax.mapping;
-import junit.framework.Assert;
import org.junit.Test;
+import com.netflix.astyanax.ColumnListMutation;
+import com.netflix.astyanax.Serializer;
+import com.netflix.astyanax.model.ColumnPath;
+
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import junit.framework.Assert;
+
+@SuppressWarnings("deprecation")
public class TestMapping {
+
@Test
public void testKeyspaceAnnotations() {
FakeKeyspaceBean override = new FakeKeyspaceBean();
@@ -77,4 +90,226 @@ public void testCache() {
Assert.assertSame(keyspaceBeanMapping1, keyspaceBeanMapping2);
}
+
+ @Test
+ public void testTtlMapping() {
+ TtlTestBean testBean = new TtlTestBean();
+ testBean.setId("1");
+ testBean.setDefaultTtl("defaultval");
+ testBean.setZeroTtl("zeroval");
+ testBean.setSixtyTtl("sixtyval");
+
+ Mapping<TtlTestBean> mapping = Mapping.make(TtlTestBean.class);
+
+ ColumnListMutationImplementation mutation = new ColumnListMutationImplementation();
+ mapping.fillMutation(testBean, mutation);
+ Assert.assertEquals(null, mutation.getTtls().get("defaultval"));
+ Assert.assertEquals(new Integer(0), mutation.getTtls().get("zeroval"));
+ Assert.assertEquals(new Integer(60), mutation.getTtls().get("sixtyval"));
+ }
+
+ @Test
+ public void testTtlMappingWithDefaultExpiry() {
+ TtlTestBean testBean = new TtlTestBean();
+ testBean.setId("1");
+ testBean.setDefaultTtl("defaultval");
+ testBean.setZeroTtl("zeroval");
+ testBean.setSixtyTtl("sixtyval");
+
+ Mapping<TtlTestBean> mapping = Mapping.make(TtlTestBean.class,
+ new DefaultAnnotationSet(new Integer(33)));
+
+ ColumnListMutationImplementation mutation = new ColumnListMutationImplementation();
+ mapping.fillMutation(testBean, mutation);
+ Assert.assertEquals(new Integer(33),
+ mutation.getTtls().get("defaultval"));
+ Assert.assertEquals(new Integer(0), mutation.getTtls().get("zeroval"));
+ Assert.assertEquals(new Integer(60), mutation.getTtls().get("sixtyval"));
+ }
+
+ private final class ColumnListMutationImplementation implements
+ ColumnListMutation<String> {
+ final Map<String, Integer> ttls = new HashMap<String, Integer>();
+
+ public Map<String, Integer> getTtls() {
+ return ttls;
+ }
+
+ @Override
+ public <V> ColumnListMutation<String> putColumn(String columnName,
+ V value, Serializer<V> valueSerializer, Integer ttl) {
+ rememberTtl(value, ttl);
+ return this;
+ }
+
+ private <V> void rememberTtl(V value, Integer ttl) {
+ ttls.put(value.toString(), ttl);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public <SC> ColumnListMutation<SC> withSuperColumn(
+ ColumnPath<SC> superColumnPath) {
+ return null;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ String value, Integer ttl) {
+ rememberTtl(value, ttl);
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ String value) {
+ rememberTtl(value, null);
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ byte[] value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ byte[] value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ int value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName, int value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ long value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ long value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ boolean value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ boolean value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ ByteBuffer value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ ByteBuffer value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ Date value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ Date value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ float value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ float value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ double value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ double value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ UUID value, Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putColumn(String columnName,
+ UUID value) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putEmptyColumn(String columnName,
+ Integer ttl) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> putEmptyColumn(String columnName) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> incrementCounterColumn(
+ String columnName, long amount) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> deleteColumn(String columnName) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> setTimestamp(long timestamp) {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> delete() {
+ return this;
+ }
+
+ @Override
+ public ColumnListMutation<String> setDefaultTtl(Integer ttl) {
+ return this;
+ }
+ }
+
}
View
92 src/test/java/com/netflix/astyanax/mapping/TtlTestBean.java
@@ -0,0 +1,92 @@
+package com.netflix.astyanax.mapping;
+
+public class TtlTestBean implements Comparable<TtlTestBean> {
+ @Id("PK")
+ private String id;
+
+ @Column("DEFAULT_TTL")
+ private String defaultTtl;
+
+ @Column(value = "ZERO_TTL", ttl = 0)
+ private String zeroTtl;
+
+ @Column(value = "SIXTY_TTL", ttl = 60)
+ private String sixtyTtl;
+
+ /**
+ * Public empty constructor needed
+ */
+ public TtlTestBean() {
+ }
+
+ /**
+ * Unique identifying id
+ *
+ * @return value
+ */
+ public String getId() {
+ return id;
+ }
+
+ public String getDefaultTtl() {
+ return defaultTtl;
+ }
+
+ public void setDefaultTtl(String defaultTtl) {
+ this.defaultTtl = defaultTtl;
+ }
+
+ public String getZeroTtl() {
+ return zeroTtl;
+ }
+
+ public void setZeroTtl(String zeroTtl) {
+ this.zeroTtl = zeroTtl;
+ }
+
+ public String getSixtyTtl() {
+ return sixtyTtl;
+ }
+
+ public void setSixtyTtl(String sixtyTtl) {
+ this.sixtyTtl = sixtyTtl;
+ }
+
+ /**
+ * Set unique override id.
+ *
+ * @param id
+ * value
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof TtlTestBean) {
+ return ((TtlTestBean) o).getId().equals(getId());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int compareTo(TtlTestBean o) {
+ if (o == null) {
+ return -1;
+ } else {
+ return getId().compareTo(o.getId());
+ }
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.