Package org.radargun.stages.cache.test

Source Code of org.radargun.stages.cache.test.QueryStage$SortElement

package org.radargun.stages.cache.test;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.radargun.DistStageAck;
import org.radargun.StageResult;
import org.radargun.config.Converter;
import org.radargun.config.DefinitionElement;
import org.radargun.config.Property;
import org.radargun.config.PropertyHelper;
import org.radargun.config.Stage;
import org.radargun.reporting.Report;
import org.radargun.stages.test.OperationLogic;
import org.radargun.stages.test.Stressor;
import org.radargun.stages.test.TestStage;
import org.radargun.state.SlaveState;
import org.radargun.stats.Statistics;
import org.radargun.traits.InjectTrait;
import org.radargun.traits.Queryable;
import org.radargun.utils.NumberConverter;
import org.radargun.utils.ObjectConverter;
import org.radargun.utils.Projections;
import org.radargun.utils.ReflexiveConverters;

/**
* Executes Queries using Infinispan-Query API against the cache.
*
* @author Anna Manukyan
*/
@Stage(doc = "Stage which executes a Query using Infinispan-query API against all keys in the cache.")
public class QueryStage extends TestStage {
   @Property(optional = false, doc = "Full class name of the object that should be queried. Mandatory.")
   private String queryObjectClass;

   @Property(optional = false, doc = "Conditions used in the query", complexConverter = ConditionConverter.class)
   private List<Condition> conditions;

   @Property(doc = "Use projection instead of returning full object. Default is without projection.")
   private String[] projection;

   @Property(doc = "Use sorting order, in form [attribute[:(ASC|DESC)]][,attribute[:(ASC|DESC)]]*. " +
         "Without specifying ASC or DESC the sort order defaults to ASC. Default is unordereded.",
         converter = SortConverter.class)
   private List<SortElement> orderBy;

   @Property(doc = "Offset in the results. Default is none.")
   private long offset = -1;

   @Property(doc = "Maximum number of the results. Default is none.")
   private long limit = -1;

   @Property(doc = "Check whether all slaves got the same result, and fail if not. Default is false.")
   private boolean checkSameResult = false;

   @InjectTrait
   private Queryable queryable;

   protected AtomicInteger expectedSize = new AtomicInteger(-1);

   @Override
   public OperationLogic getLogic() {
      return new Logic();
   }

   @Override
   protected QueryAck newStatisticsAck(SlaveState slaveState, List<List<Statistics>> iterations) {
      return new QueryAck(slaveState, iterations, expectedSize.get());
   }

   @Override
   public StageResult processAckOnMaster(List<DistStageAck> acks) {
      StageResult result = super.processAckOnMaster(acks);
      if (result.isError()) return result;

      int minSize = Integer.MAX_VALUE, maxSize = Integer.MIN_VALUE;
      Map<Integer, Report.SlaveResult> slaveResults = new HashMap<Integer, Report.SlaveResult>();
      for (QueryAck ack : Projections.instancesOf(acks, QueryAck.class)) {
         if (maxSize >= 0 && (minSize != ack.queryResultSize || maxSize != ack.queryResultSize)) {
            String message = String.format("The size got from %d -> %d is not the same as from other slaves -> %d .. %d ",
                  ack.getSlaveIndex(), ack.queryResultSize, minSize, maxSize);
            if (checkSameResult) {
               log.error(message);
               return errorResult();
            } else {
               log.info(message);
            }
         }
         minSize = Math.min(minSize, ack.queryResultSize);
         maxSize = Math.max(maxSize, ack.queryResultSize);
         slaveResults.put(ack.getSlaveIndex(), new Report.SlaveResult(String.valueOf(ack.queryResultSize), false));
      }
      Report.Test test = getTest();
      if (test != null) {
         String sizeString = minSize == maxSize ? String.valueOf(maxSize) : String.format("%d .. %d", minSize, maxSize);
         test.addResult(getTestIteration(), new Report.TestResult("Query result size", slaveResults, sizeString, false));
      } else {
         log.info("No test name - results are not recorded");
      }
      return result;
   }

   protected static class QueryAck extends StatisticsAck {
      public final int queryResultSize;

      public QueryAck(SlaveState slaveState, List<List<Statistics>> iterations, int queryResultSize) {
         super(slaveState, iterations);
         this.queryResultSize = queryResultSize;
      }
   }

   protected class Logic extends OperationLogic {
      protected Queryable.QueryBuilder builder;
      protected Queryable.QueryResult previousQueryResult = null;

      @Override
      public void init(Stressor stressor) {
         super.init(stressor);
         Class<?> clazz;
         try {
            clazz = slaveState.getClassLoader().loadClass(queryObjectClass);
         } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Cannot load class " + queryObjectClass, e);
         }
         builder = queryable.getBuilder(null, clazz);
         for (Condition condition : conditions) {
            condition.apply(builder);
         }
         if (orderBy != null) {
            for (SortElement se : orderBy) {
               builder.orderBy(se.attribute, se.asc ? Queryable.SortOrder.ASCENDING : Queryable.SortOrder.DESCENDING);
            }
         }
         if (projection != null) {
            builder.projection(projection);
         }
         if (offset >= 0) {
            builder.offset(offset);
         }
         if (limit >= 0) {
            builder.limit(limit);
         }
      }

      @Override
      public Object run() throws RequestException {
         Queryable.Query query = builder.build();
         Queryable.QueryResult queryResult = (Queryable.QueryResult) stressor.makeRequest(new Invocations.Query(query));

         if (previousQueryResult != null) {
            if (queryResult.size() != previousQueryResult.size()) {
               throw new IllegalStateException("The query result is different from the previous one. All results should be the same when executing the same query");
            }
         } else {
            log.info("First result has " + queryResult.size() + " entries");
            if (log.isTraceEnabled()) {
               for (Object entry : queryResult.values()) {
                  log.trace(String.valueOf(entry));
               }
            }
            if (!expectedSize.compareAndSet(-1, queryResult.size())) {
               if (expectedSize.get() != queryResult.size()) {
                  throw new IllegalStateException("Another thread reported " + expectedSize.get() + " results while we have " + queryResult.size());
               }
            }
         }
         previousQueryResult = queryResult;

         return queryResult;
      }
   }

   private static abstract class Condition {
      public abstract void apply(Queryable.QueryBuilder builder);

      public String toString() {
         DefinitionElement de = getClass().getAnnotation(DefinitionElement.class);
         StringBuilder sb = new StringBuilder();
         if (de == null) sb.append(getClass().getSimpleName());
         else sb.append(de.name());
         return sb.append(PropertyHelper.toString(this)).toString();
      }
   }

   private static abstract class PathCondition extends Condition {
      @Property(doc = "Target path (field on the queried object or path through embedded objects)", optional = false)
      public String path;
   }

   @DefinitionElement(name = "eq", doc = "Target is equal to value")
   private static class Eq extends PathCondition {
      @Property(doc = "Value used in the condition", optional = false, converter = ObjectConverter.class)
      public Object value;

      @Override
      public void apply(Queryable.QueryBuilder builder) {
         builder.eq(path, value);
      }
   }

   private static abstract class PathNumberCondition extends PathCondition {
      @Property(doc = "Value used in the condition", optional = false, converter = NumberConverter.class)
      public Number value;
   }


   @DefinitionElement(name = "lt", doc = "Target is < than value")
   private static class Lt extends PathNumberCondition {
      @Override
      public void apply(Queryable.QueryBuilder builder) {
         builder.lt(path, value);
      }
   }

   @DefinitionElement(name = "le", doc = "Target is <= than value")
   private static class Le extends PathNumberCondition {
      @Override
      public void apply(Queryable.QueryBuilder builder) {
         builder.le(path, value);
      }
   }

   @DefinitionElement(name = "gt", doc = "Target is > than value")
   private static class Gt extends PathNumberCondition {
      @Override
      public void apply(Queryable.QueryBuilder builder) {
         builder.gt(path, value);
      }
   }

   @DefinitionElement(name = "ge", doc = "Target is < than value")
   private static class Ge extends PathNumberCondition {
      @Override
      public void apply(Queryable.QueryBuilder builder) {
         builder.ge(path, value);
      }
   }

   @DefinitionElement(name = "like", doc = "Target string matches the value")
   private static class Like extends PathCondition {
      @Property(doc = "Value used in the condition", optional = false)
      public String value;

      @Override
      public void apply(Queryable.QueryBuilder builder) {
         builder.like(path, value);
      }
   }

   @DefinitionElement(name = "contains", doc = "Target is collection containing the value")
   private static class Contains extends PathCondition {
      @Property(doc = "Value used in the condition", optional = false, converter = ObjectConverter.class)
      public Object value;

      @Override
      public void apply(Queryable.QueryBuilder builder) {
         builder.contains(path, value);
      }
   }

   @DefinitionElement(name = "is-null", doc = "Target is not defined (null)")
   private static class IsNull extends PathCondition {
      @Override
      public void apply(Queryable.QueryBuilder builder) {
         builder.isNull(path);
      }
   }

   @DefinitionElement(name = "not", doc = "All inner conditions are false", resolveType = DefinitionElement.ResolveType.PASS_BY_DEFINITION)
   private static class Not extends Condition {
      @Property(name = "", doc = "Inner conditions", complexConverter = ConditionConverter.class)
      public final List<Condition> subs = new ArrayList<Condition>();

      @Override
      public void apply(Queryable.QueryBuilder builder) {
         Queryable.QueryBuilder subBuilder = builder.subquery();
         for (Condition sub : subs) {
            sub.apply(subBuilder);
         }
         builder.not(subBuilder);
      }
   }

   @DefinitionElement(name = "any", doc = "Any of inner conditions is true", resolveType = DefinitionElement.ResolveType.PASS_BY_DEFINITION)
   private static class Any extends Condition {
      @Property(name = "", doc = "Inner conditions", complexConverter = ConditionConverter.class)
      public final List<Condition> subs = new ArrayList<Condition>();

      @Override
      public void apply(Queryable.QueryBuilder builder) {
         Queryable.QueryBuilder subBuilders[] = new Queryable.QueryBuilder[subs.size()];
         int i = 0;
         for (Condition sub : subs) {
            Queryable.QueryBuilder subBuilder = builder.subquery();
            sub.apply(subBuilder);
            subBuilders[i++] = subBuilder;
         }
         builder.any(subBuilders);
      }
   }

   @DefinitionElement(name = "all", doc = "All inner conditions are false", resolveType = DefinitionElement.ResolveType.PASS_BY_DEFINITION)
   private static class All extends Condition {
      @Property(name = "", doc = "Inner conditions", complexConverter = ConditionConverter.class)
      public final List<Condition> subs = new ArrayList<Condition>();

      @Override
      public void apply(Queryable.QueryBuilder builder) {
         for (Condition sub : subs) {
            sub.apply(builder);
         }
      }
   }

   private static class ConditionConverter extends ReflexiveConverters.ListConverter {
      public ConditionConverter() {
         super(new Class[] {Eq.class, Lt.class, Le.class, Gt.class, Ge.class, Like.class, Contains.class, IsNull.class, Not.class, Any.class, All.class});
      }
   }

   private static class SortElement {
      public final String attribute;
      public final boolean asc;

      private SortElement(String attribute, boolean asc) {
         this.attribute = attribute;
         this.asc = asc;
      }
   }

   private static class SortConverter implements Converter<List<SortElement>> {
      @Override
      public List<SortElement> convert(String string, Type type) {
         String[] parts = string.split(",", 0);
         ArrayList<SortElement> result = new ArrayList<SortElement>(parts.length);
         for (String part : parts) {
            int colon = part.indexOf(':');
            if (colon < 0) {
               result.add(new SortElement(part.trim(), true));
            } else {
               String order = part.substring(colon + 1).trim();
               boolean asc;
               if (order.equalsIgnoreCase("ASC")) {
                  asc = true;
               } else if (order.equalsIgnoreCase("DESC")) {
                  asc = false;
               } else {
                  throw new IllegalArgumentException("Sort order: " + order);
               }
               result.add(new SortElement(part.substring(0, colon).trim(), asc));
            }
         }
         return result;
      }

      @Override
      public String convertToString(List<SortElement> value) {
         if (value == null) return "<unordered>";
         StringBuilder sb = new StringBuilder();
         for (SortElement e : value) {
            sb.append(e.attribute).append(':').append(e.asc ? "ASC" : "DESC").append(", ");
         }
         return sb.toString();
      }

      @Override
      public String allowedPattern(Type type) {
         return "[0-9a-zA-Z_]*(:ASC|:DESC)?(,\\s*[0-9a-zA-Z_]*(:ASC|:DESC)?)*";
      }
   }
}
TOP

Related Classes of org.radargun.stages.cache.test.QueryStage$SortElement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.