/*
* Copyright (c) 2008-2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.client.constraint.impl;
import java.net.URI;
import java.util.Date;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.model.Column;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.query.RowQuery;
import com.netflix.astyanax.util.RangeBuilder;
import com.netflix.astyanax.util.TimeUUIDUtils;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.emc.storageos.db.client.constraint.DecommissionedConstraint;
import com.emc.storageos.db.client.impl.CompositeColumnNameSerializer;
import com.emc.storageos.db.client.impl.IndexColumnName;
import com.emc.storageos.db.client.impl.IndexColumnNameSerializer;
import com.emc.storageos.db.client.model.DataObject;
/**
* Constraint to query indexed columns on a start/end time. This uses the same
* index as decommissionedConstraintImpl but is designed to work on other column
* families. This constraint takes a Start and End time and returns records
* between this time period.
*/
public class TimeConstraintImpl extends ConstraintImpl<IndexColumnName> implements DecommissionedConstraint {
private static final long MILLIS_TO_MICROS = 1000L;
private static final int DEFAULT_PAGE_SIZE = 100;
private Keyspace keyspace;
private final ColumnFamily<String, IndexColumnName> cf;
private final String rowKey;
private final long startTimeMicros;
private final long endTimeMicros;
private Boolean value = null;
private Class<? extends DataObject> entityType;
/**
* Constructs the time constraint.
*
* @param clazz DataObject class
* @param cf Column Family
* @param startTimeMillis Start time in milliseconds or -1 for no filtering on start time.
* @param endTimeMillis End time in milliseconds or -1 for no filtering on end time.
*/
public TimeConstraintImpl(Class<? extends DataObject> clazz, ColumnFamily<String, IndexColumnName> cf,
Boolean value, long startTimeMillis, long endTimeMillis) {
this.cf = cf;
indexSerializer = IndexColumnNameSerializer.get();
rowKey = clazz.getSimpleName();
this.startTimeMicros = startTimeMillis * MILLIS_TO_MICROS;
this.endTimeMicros = endTimeMillis * MILLIS_TO_MICROS;
this.value = value;
this.entityType = clazz;
}
/**
* Constructs the time constraint.
*
* @param clazz DataObject class
* @param cf Column Family
* @param startTime Start time Date or null for no filtering on start time
* @param endTime End time Date or null for no filtering on end time
*/
public TimeConstraintImpl(Class<? extends DataObject> clazz, Boolean value,
ColumnFamily<String, IndexColumnName> cf, Date startTime, Date endTime) {
this(clazz, cf, value,
startTime == null ? -1 : startTime.getTime(),
endTime == null ? -1 : endTime.getTime());
}
@Override
public void setKeyspace(Keyspace keyspace) {
this.keyspace = keyspace;
}
@Override
public <T> void execute(final QueryResult<T> result) {
RowQuery<String, IndexColumnName> query;
if (value == null) {
query = keyspace.prepareQuery(cf).getKey(rowKey)
.autoPaginate(true)
.withColumnRange(new RangeBuilder().setLimit(DEFAULT_PAGE_SIZE).build());
}
else {
query = keyspace.prepareQuery(cf).getKey(rowKey)
.autoPaginate(true)
.withColumnRange(
CompositeColumnNameSerializer.get().buildRange()
.greaterThanEquals(value.toString())
.lessThanEquals(value.toString())
.limit(DEFAULT_PAGE_SIZE));
}
FilteredQueryHitIterator<T, IndexColumnName> it = new FilteredQueryHitIterator<T, IndexColumnName>(query) {
@Override
protected T createQueryHit(Column<IndexColumnName> column) {
return result.createQueryHit(URI.create(column.getName().getTwo()));
}
@Override
public boolean filter(Column<IndexColumnName> column) {
long timeMarked = TimeUUIDUtils.getMicrosTimeFromUUID(column.getName().getTimeUUID());
// Filtering on startTime, startTime = -1 for no filtering
if (startTimeMicros > 0 && timeMarked < startTimeMicros) {
return false;
}
// Filtering on endTime, endTime = -1 for no filtering
if (endTimeMicros > 0 && timeMarked > endTimeMicros) {
return false;
}
return true;
}
};
it.prime();
result.setResult(it);
}
@Override
protected <T> void queryOnePage(final QueryResult<T> result) throws ConnectionException {
queryOnePageWithoutAutoPaginate(genQuery(), Boolean.toString(value), result);
}
@Override
protected URI getURI(Column<IndexColumnName> col) {
return URI.create(col.getName().getTwo());
}
@Override
protected <T> T createQueryHit(final QueryResult<T> result, Column<IndexColumnName> column) {
return result.createQueryHit(URI.create(column.getName().getTwo()));
}
@Override
public RowQuery<String, IndexColumnName> genQuery() {
RowQuery<String, IndexColumnName> query;
if (value == null) {
query = keyspace.prepareQuery(cf).getKey(rowKey)
.autoPaginate(true)
.withColumnRange(new RangeBuilder().build());
} else {
query = keyspace.prepareQuery(cf).getKey(rowKey)
.autoPaginate(true)
.withColumnRange(
IndexColumnNameSerializer.get().buildRange()
.greaterThanEquals(value.toString())
.lessThanEquals(value.toString())
.limit(pageCount).build());
}
return query;
}
@Override
public Class<? extends DataObject> getDataObjectType() {
return entityType;
}
@Override
public boolean isValid() {
return rowKey!=null && !rowKey.isEmpty();
}
}