/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.client.constraint.impl; import java.net.URI; import java.util.List; import java.util.ArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.netflix.astyanax.connectionpool.exceptions.ConnectionException; import com.netflix.astyanax.model.Column; import com.netflix.astyanax.model.ColumnList; import com.netflix.astyanax.query.RowQuery; import com.netflix.astyanax.serializers.CompositeRangeBuilder; import com.netflix.astyanax.serializers.AnnotatedCompositeSerializer; import com.emc.storageos.db.client.constraint.ConstraintDescriptor; import com.emc.storageos.db.client.constraint.Constraint; import com.emc.storageos.db.client.impl.*; import com.emc.storageos.db.exceptions.DatabaseException; /** * Abstract base for all containment queries */ public abstract class ConstraintImpl <T extends CompositeIndexColumnName> implements Constraint { private static final Logger log = LoggerFactory.getLogger(ConstraintImpl.class); private static final int DEFAULT_PAGE_SIZE = 100; private ConstraintDescriptor constraintDescriptor; protected String startId; protected int pageCount = DEFAULT_PAGE_SIZE; protected boolean returnOnePage; protected AnnotatedCompositeSerializer<T> indexSerializer; public ConstraintImpl(Object... arguments) { ColumnField field = null; int cfPosition = 0; int i = 0; List args = new ArrayList(); for (Object argument : arguments) { i++; if (argument instanceof ColumnField) { field = (ColumnField) argument; cfPosition = i; continue; } args.add(argument); } // TODO: remove this once TimeConstraintImpl has been reworked to work over geo-queries if (this instanceof AlternateIdConstraintImpl || this instanceof TimeConstraintImpl || this instanceof ClassNameTimeSeriesConstraintImpl) { return; } if (field == null) { throw new IllegalArgumentException("ColumnField should be in the constructor arguments"); } String dataObjClassName = field.getDataObjectType().getName(); String fieldName = field.getName(); constraintDescriptor = new ConstraintDescriptor(); constraintDescriptor.setConstraintClassName(this.getClass().getName()); constraintDescriptor.setDataObjectClassName(dataObjClassName); constraintDescriptor.setColumnFieldName(fieldName); constraintDescriptor.setColumnFieldPosition(cfPosition); constraintDescriptor.setArguments(args); } @Override public ConstraintDescriptor toConstraintDescriptor() { return constraintDescriptor; } public abstract boolean isValid(); public void setStartId(URI startId) { if (startId != null) { this.startId = startId.toString(); } returnOnePage = true; } public void setPageCount(int pageCount) { if (pageCount > 0) { this.pageCount = pageCount; } } public int getPageCount() { return pageCount; } @Override public <T> void execute(final Constraint.QueryResult<T> result) { try { if (returnOnePage) { queryOnePage(result); return; } } catch (ConnectionException e) { log.info("Query failed e=", e); throw DatabaseException.retryables.connectionFailed(e); } queryWithAutoPaginate(genQuery(), result); } protected abstract <T1> void queryOnePage(final QueryResult<T1> result) throws ConnectionException; protected abstract RowQuery<String, T> genQuery(); protected <T1> void queryWithAutoPaginate(RowQuery<String, T> query, final QueryResult<T1> result) { query.autoPaginate(true); QueryHitIterator<T1, T> it = getQueryHitIterator(query, result); it.prime(); result.setResult(it); } protected <T3> QueryHitIterator<T3, T> getQueryHitIterator(RowQuery<String, T> query, final QueryResult<T3> result) { final ConstraintImpl constraint = this; QueryHitIterator<T3, T> it = new QueryHitIterator<T3, T>(query) { @Override protected T3 createQueryHit(Column<T> column) { return (T3)constraint.createQueryHit(result, column); } }; return it; } protected abstract URI getURI(Column<T> col); protected abstract <T1> T1 createQueryHit(final QueryResult<T1> result, Column<T> col); protected <T1> void queryOnePageWithoutAutoPaginate(RowQuery<String, T> query, String prefix, final QueryResult<T1> result) throws ConnectionException { CompositeRangeBuilder builder = indexSerializer.buildRange() .greaterThanEquals(prefix) .lessThanEquals(prefix) .reverse() // last column comes first .limit(1); query.withColumnRange(builder); ColumnList<T> columns = query.execute().getResult(); List<T1> ids = new ArrayList(); if (columns.isEmpty()) { result.setResult(ids.iterator()); return; // not found } Column<T> lastColumn = columns.getColumnByIndex(0); String endId = lastColumn.getName().getTwo(); builder = indexSerializer.buildRange(); if (startId == null) { // query first page builder.greaterThanEquals(prefix) .lessThanEquals(prefix) .limit(pageCount); } else { builder.withPrefix(prefix) .greaterThan(startId) .lessThanEquals(endId) .limit(pageCount); } query = query.withColumnRange(builder); columns = query.execute().getResult(); for (Column<T> col : columns) { T1 obj = createQueryHit(result, col); if (obj != null && !ids.contains(obj)) { ids.add(createQueryHit(result, col)); } } result.setResult(ids.iterator()); } protected <T1> void queryOnePageWithAutoPaginate(RowQuery<String, T> query, String prefix, final QueryResult<T1> result) throws ConnectionException { CompositeRangeBuilder range = indexSerializer.buildRange() .greaterThanEquals(prefix) .lessThanEquals(prefix) .limit(pageCount); query.withColumnRange(range); queryOnePageWithAutoPaginate(query, result); } protected <T1> void queryOnePageWithAutoPaginate(RowQuery<String, T> query, final QueryResult<T1> result) throws ConnectionException { boolean start = false; List<T1> ids = new ArrayList(); int count = 0; query.autoPaginate(true); ColumnList<T> columns; while ((count < pageCount)) { columns = query.execute().getResult(); if (columns.isEmpty()) { break; // reach the end } for (Column<T> col : columns) { if (startId == null) { start = true; } else if (startId.equals(getURI(col).toString())) { start = true; continue; } if (start) { T1 obj = createQueryHit(result, col); if (obj != null && !ids.contains(obj)) { ids.add(obj); } count++; } } } result.setResult(ids.iterator()); } }