Package net.opentsdb.tsd

Source Code of net.opentsdb.tsd.TreeRpc

// This file is part of OpenTSDB.
// Copyright (C) 2013  The OpenTSDB Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 of the License, or (at your
// option) any later version.  This program is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.  You should have received a copy
// of the GNU Lesser General Public License along with this program.  If not,
// see <http://www.gnu.org/licenses/>.
package net.opentsdb.tsd;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.PatternSyntaxException;

import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;

import com.fasterxml.jackson.core.type.TypeReference;
import com.stumbleupon.async.DeferredGroupException;

import net.opentsdb.core.TSDB;
import net.opentsdb.meta.TSMeta;
import net.opentsdb.tree.Branch;
import net.opentsdb.tree.Tree;
import net.opentsdb.tree.TreeBuilder;
import net.opentsdb.tree.TreeRule;
import net.opentsdb.uid.NoSuchUniqueId;
import net.opentsdb.utils.JSON;

/**
* Handles API calls for trees such as fetching, editing or deleting trees,
* branches and rules.
* @since 2.0
*/
final class TreeRpc implements HttpRpc {
  /** Type reference for common string/string maps */
  private static TypeReference<HashMap<String, String>> TR_HASH_MAP =
    new TypeReference<HashMap<String, String>>() {};
   
  /**
   * Routes the request to the proper handler
   * @param tsdb The TSDB to which we belong
   * @param query The HTTP query to use for parsing and responding
   */
  @Override
  public void execute(TSDB tsdb, HttpQuery query) throws IOException {
    // the uri will be /api/vX/tree/? or /api/tree/?
    final String[] uri = query.explodeAPIPath();
    final String endpoint = uri.length > 1 ? uri[1] : "";
   
    try {
      if (endpoint.isEmpty()) {
        handleTree(tsdb, query);
      } else if (endpoint.toLowerCase().equals("branch")) {
        handleBranch(tsdb, query);
      } else if (endpoint.toLowerCase().equals("rule")) {
        handleRule(tsdb, query);
      } else if (endpoint.toLowerCase().equals("rules")) {
        handleRules(tsdb, query);
      } else if (endpoint.toLowerCase().equals("test")) {
        handleTest(tsdb, query);
      } else if (endpoint.toLowerCase().equals("collisions")) {
        handleCollisionNotMatched(tsdb, query, true);
      } else if (endpoint.toLowerCase().equals("notmatched")) {
        handleCollisionNotMatched(tsdb, query, false);
      } else {
        throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
            "This endpoint is not supported");
      }
    } catch (BadRequestException e) {
      throw e;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Handles the plain /tree endpoint CRUD. If a POST or PUT is requested and
   * no tree ID is provided, we'll assume the user wanted to create a new tree.
   * @param tsdb The TSDB to which we belong
   * @param query The HTTP query to work with
   * @throws BadRequestException if the request was invalid.
   */
  private void handleTree(TSDB tsdb, HttpQuery query) {
    final Tree tree;
    if (query.hasContent()) {
      tree = query.serializer().parseTreeV1();
    } else {
      tree = parseTree(query);
    }
   
    try {
      // if get, then we're just returning one or more trees
      if (query.getAPIMethod() == HttpMethod.GET) {
 
        if (tree.getTreeId() == 0) {
          query.sendReply(query.serializer().formatTreesV1(
              Tree.fetchAllTrees(tsdb).joinUninterruptibly()));
        } else {
          final Tree single_tree = Tree.fetchTree(tsdb, tree.getTreeId())
            .joinUninterruptibly();
          if (single_tree == null) {
            throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
              "Unable to locate tree: " + tree.getTreeId());
          }
          query.sendReply(query.serializer().formatTreeV1(single_tree));
        }
 
      } else if (query.getAPIMethod() == HttpMethod.POST || query.getAPIMethod() == HttpMethod.PUT) {
        // For post or put, we're either editing a tree or creating a new one.
        // If the tree ID is missing, we need to create a new one, otherwise we
        // edit an existing tree.
       
        // if the tree ID is set, fetch, copy, save
        if (tree.getTreeId() > 0) {
          if (Tree.fetchTree(tsdb, tree.getTreeId())
              .joinUninterruptibly() == null) {
            throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
                "Unable to locate tree: " + tree.getTreeId());
          } else {
            if (tree.storeTree(tsdb, (query.getAPIMethod() == HttpMethod.PUT))
                .joinUninterruptibly() != null) {
              final Tree stored_tree = Tree.fetchTree(tsdb, tree.getTreeId())
                .joinUninterruptibly();
              query.sendReply(query.serializer().formatTreeV1(stored_tree));
            } else {
              throw new BadRequestException(
                  HttpResponseStatus.INTERNAL_SERVER_ERROR,
                  "Unable to save changes to tre tree: " + tree.getTreeId(),
                  "Plesae try again at a later time");
            }
          }
        } else {
          // create a new tree
          final int tree_id = tree.createNewTree(tsdb).joinUninterruptibly();
          if (tree_id > 0) {
            final Tree stored_tree = Tree.fetchTree(tsdb, tree_id)
              .joinUninterruptibly();
            query.sendReply(query.serializer().formatTreeV1(stored_tree));
          } else {
            throw new BadRequestException(
                HttpResponseStatus.INTERNAL_SERVER_ERROR,
                "Unable to save changes to tree: " + tree.getTreeId(),
                "Plesae try again at a later time");
          }
        }
       
      // handle DELETE requests
      } else if (query.getAPIMethod() == HttpMethod.DELETE) {
        boolean delete_definition = false;
       
        if (query.hasContent()) {
          // since we don't want to complicate the Tree class with a "delete
          // description" flag, we can just double parse the hash map in delete
          // calls
          final String json = query.getContent();
          final HashMap<String, String> properties =
            JSON.parseToObject(json, TR_HASH_MAP);
          final String delete_all = properties.get("definition");
          if (delete_all != null && delete_all.toLowerCase().equals("true")) {
              delete_definition = true;
          }
        } else {
          final String delete_all = query.getQueryStringParam("definition");
          if (delete_all != null && delete_all.toLowerCase().equals("true")) {
            delete_definition = true;
          }
        }
       
        if (Tree.fetchTree(tsdb, tree.getTreeId()).joinUninterruptibly() ==
          null) {
          throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
              "Unable to locate tree: " + tree.getTreeId());
        }
        Tree.deleteTree(tsdb, tree.getTreeId(), delete_definition)
          .joinUninterruptibly();
        query.sendStatusOnly(HttpResponseStatus.NO_CONTENT);
       
      } else {
        throw new BadRequestException(HttpResponseStatus.BAD_REQUEST,
          "Unsupported HTTP request method");
      }
     
    } catch (BadRequestException e) {
      throw e;
    } catch (IllegalStateException e) {
      query.sendStatusOnly(HttpResponseStatus.NOT_MODIFIED);
    } catch (IllegalArgumentException e) {
      throw new BadRequestException(e);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
 
  /**
   * Attempts to retrieve a single branch and return it to the user. If the
   * requested branch doesn't exist, it returns a 404.
   * @param tsdb The TSDB to which we belong
   * @param query The HTTP query to work with
   * @throws BadRequestException if the request was invalid.
   */
  private void handleBranch(TSDB tsdb, HttpQuery query) {
    if (query.getAPIMethod() != HttpMethod.GET) {
      throw new BadRequestException(HttpResponseStatus.BAD_REQUEST,
        "Unsupported HTTP request method");
    }
   
    try {
      final int tree_id = parseTreeId(query, false);
      final String branch_hex =
          query.getQueryStringParam("branch");
     
      // compile the branch ID. If the user did NOT supply a branch address,
      // that would include the tree ID, then we fall back to the tree ID and
      // the root for that tree
      final byte[] branch_id;
      if (branch_hex == null || branch_hex.isEmpty()) {
        if (tree_id < 1) {
          throw new BadRequestException(
              "Missing or invalid branch and tree IDs");
        }
        branch_id = Tree.idToBytes(tree_id);
      } else {
        branch_id = Branch.stringToId(branch_hex);
      }
     
      // fetch it
      final Branch branch = Branch.fetchBranch(tsdb, branch_id, true)
        .joinUninterruptibly();
      if (branch == null) {
        throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
            "Unable to locate branch '" + Branch.idToString(branch_id) +
            "' for tree '" + Tree.bytesToId(branch_id) + "'");
      }
      query.sendReply(query.serializer().formatBranchV1(branch));
     
    } catch (BadRequestException e) {
      throw e;
    } catch (IllegalArgumentException e) {
      throw new BadRequestException(e);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
 
  /**
   * Handles the CRUD calls for a single rule, enabling adding, editing or
   * deleting the rule
   * @param tsdb The TSDB to which we belong
   * @param query The HTTP query to work with
   * @throws BadRequestException if the request was invalid.
   */
  private void handleRule(TSDB tsdb, HttpQuery query) {
    final TreeRule rule;
    if (query.hasContent()) {
      rule = query.serializer().parseTreeRuleV1();
    } else {
      rule = parseRule(query);
    }
   
    try {
     
      // no matter what, we'll need a tree to work with, so make sure it exists
      Tree tree = null;
        tree = Tree.fetchTree(tsdb, rule.getTreeId())
          .joinUninterruptibly();
 
      if (tree == null) {
        throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
            "Unable to locate tree: " + rule.getTreeId());
      }
     
      // if get, then we're just returning a rule from a tree
      if (query.getAPIMethod() == HttpMethod.GET) {
       
        final TreeRule tree_rule = tree.getRule(rule.getLevel(),
            rule.getOrder());
        if (tree_rule == null) {
          throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
              "Unable to locate rule: " + rule);
        }
        query.sendReply(query.serializer().formatTreeRuleV1(tree_rule));
       
      } else if (query.getAPIMethod() == HttpMethod.POST || query.getAPIMethod() == HttpMethod.PUT) {
 
        if (rule.syncToStorage(tsdb, (query.getAPIMethod() == HttpMethod.PUT))
            .joinUninterruptibly()) {
          final TreeRule stored_rule = TreeRule.fetchRule(tsdb,
              rule.getTreeId(), rule.getLevel(), rule.getOrder())
              .joinUninterruptibly();
          query.sendReply(query.serializer().formatTreeRuleV1(stored_rule));
        } else {
          throw new RuntimeException("Unable to save rule " + rule +
              " to storage");
        }
 
      } else if (query.getAPIMethod() == HttpMethod.DELETE) {
 
        if (tree.getRule(rule.getLevel(), rule.getOrder()) == null) {
          throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
              "Unable to locate rule: " + rule);
        }
        TreeRule.deleteRule(tsdb, tree.getTreeId(), rule.getLevel(),
            rule.getOrder()).joinUninterruptibly();
        query.sendStatusOnly(HttpResponseStatus.NO_CONTENT);
 
      } else {
        throw new BadRequestException(HttpResponseStatus.BAD_REQUEST,
          "Unsupported HTTP request method");
      }
   
    } catch (BadRequestException e) {
      throw e;
    } catch (IllegalStateException e) {
      query.sendStatusOnly(HttpResponseStatus.NOT_MODIFIED);
    } catch (IllegalArgumentException e) {
      throw new BadRequestException(e);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
 
  /**
   * Handles requests to replace or delete all of the rules in the given tree.
   * It's an efficiency helper for cases where folks don't want to make a single
   * call per rule when updating many rules at once.
   * @param tsdb The TSDB to which we belong
   * @param query The HTTP query to work with
   * @throws BadRequestException if the request was invalid.
   */
  private void handleRules(TSDB tsdb, HttpQuery query) {
    int tree_id = 0;
    List<TreeRule> rules = null;
    if (query.hasContent()) {
      rules = query.serializer().parseTreeRulesV1();
      if (rules == null || rules.isEmpty()) {
        throw new BadRequestException("Missing tree rules");
      }
     
      // validate that they all belong to the same tree
      tree_id = rules.get(0).getTreeId();
      for (TreeRule rule : rules) {
        if (rule.getTreeId() != tree_id) {
          throw new BadRequestException(
              "All rules must belong to the same tree");
        }
      }
    } else {
      tree_id = parseTreeId(query, false);
    }
   
    // make sure the tree exists
    try {
      if (Tree.fetchTree(tsdb, tree_id).joinUninterruptibly() == null) {
        throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
            "Unable to locate tree: " + tree_id);
      }
   
      if (query.getAPIMethod() == HttpMethod.POST || query.getAPIMethod() == HttpMethod.PUT) {
        if (rules == null || rules.isEmpty()) {
          if (rules == null || rules.isEmpty()) {
            throw new BadRequestException("Missing tree rules");
          }
        }
       
        // purge the existing tree rules if we're told to PUT
        if (query.getAPIMethod() == HttpMethod.PUT) {
          TreeRule.deleteAllRules(tsdb, tree_id).joinUninterruptibly();
        }
        for (TreeRule rule : rules) {
          rule.syncToStorage(tsdb, query.getAPIMethod() == HttpMethod.PUT)
            .joinUninterruptibly();
        }
        query.sendStatusOnly(HttpResponseStatus.NO_CONTENT);
 
      } else if (query.getAPIMethod() == HttpMethod.DELETE) {
 
        TreeRule.deleteAllRules(tsdb, tree_id).joinUninterruptibly();
        query.sendStatusOnly(HttpResponseStatus.NO_CONTENT);
 
      } else {
        throw new BadRequestException(HttpResponseStatus.BAD_REQUEST,
          "Unsupported HTTP request method");
      }
   
    } catch (BadRequestException e) {
      throw e;
    } catch (IllegalArgumentException e) {
      throw new BadRequestException(e);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
 
  /**
   * Runs the specified TSMeta object through a tree's rule set to determine
   * what the results would be or debug a meta that wasn't added to a tree
   * successfully
   * @param tsdb The TSDB to which we belong
   * @param query The HTTP query to work with
   * @throws BadRequestException if the request was invalid.
   */
  private void handleTest(TSDB tsdb, HttpQuery query) {
    final Map<String, Object> map;
    if (query.hasContent()) {
      map = query.serializer().parseTreeTSUIDsListV1();
    } else {
      map = parseTSUIDsList(query);
    }
   
    final Integer tree_id = (Integer) map.get("treeId");
    if (tree_id == null) {
      throw new BadRequestException("Missing or invalid Tree ID");
    }
   
    // make sure the tree exists
    Tree tree = null;
    try {
     
      tree = Tree.fetchTree(tsdb, tree_id).joinUninterruptibly();
      if (tree == null) {
        throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
            "Unable to locate tree: " + tree_id);
      }
     
      // ugly, but keeps from having to create a dedicated class just to
      // convert one field.
      @SuppressWarnings("unchecked")
      final List<String> tsuids = (List<String>)map.get("tsuids");
      if (tsuids == null || tsuids.isEmpty()) {
        throw new BadRequestException("Missing or empty TSUID list");
      }
     
      if (query.getAPIMethod() == HttpMethod.GET || query.getAPIMethod() == HttpMethod.POST ||
          query.getAPIMethod() == HttpMethod.PUT) {
       
        final HashMap<String, HashMap<String, Object>> results =
          new HashMap<String, HashMap<String, Object>>(tsuids.size());
        final TreeBuilder builder = new TreeBuilder(tsdb, tree);
        for (String tsuid : tsuids) {
          final HashMap<String, Object> tsuid_results =
            new HashMap<String, Object>();
         
          try {
            final TSMeta meta = TSMeta.getTSMeta(tsdb, tsuid)
              .joinUninterruptibly();
            // if the meta doesn't exist, we can't process, so just log a
            // message to the results and move on to the next TSUID
            if (meta == null) {
              tsuid_results.put("branch", null);
              tsuid_results.put("meta", null);
              final ArrayList<String> messages = new ArrayList<String>(1);
              messages.add("Unable to locate TSUID meta data");
              tsuid_results.put("messages", messages);
              results.put(tsuid, tsuid_results);
              continue;
            }
           
            builder.processTimeseriesMeta(meta, true).joinUninterruptibly();
            tsuid_results.put("branch", builder.getRootBranch());
            tsuid_results.put("meta", meta);
            tsuid_results.put("messages", builder.getTestMessage());
           
            results.put(tsuid, tsuid_results);
          } catch (DeferredGroupException e) {
            // we want to catch NSU errors and handle them gracefully for
            // TSUIDs where they may have been deleted
            Throwable ex = e;
            while (ex.getClass().equals(DeferredGroupException.class)) {
              ex = ex.getCause();
            }
           
            if (ex.getClass().equals(NoSuchUniqueId.class)) {
              tsuid_results.put("branch", null);
              tsuid_results.put("meta", null);
              final ArrayList<String> messages = new ArrayList<String>(1);
              messages.add("TSUID was missing a UID name: " + ex.getMessage());
              tsuid_results.put("messages", messages);
              results.put(tsuid, tsuid_results);
            }
          }
        }
       
        query.sendReply(query.serializer().formatTreeTestV1(results));
 
      } else {
        throw new BadRequestException(HttpResponseStatus.BAD_REQUEST,
          "Unsupported HTTP request method");
      }
   
    } catch (BadRequestException e) {
      throw e;
    } catch (IllegalArgumentException e) {
      throw new BadRequestException(e);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
 
  /**
   * Handles requests to fetch collisions or not-matched entries for the given
   * tree. To cut down on code, this method uses a flag to determine if we want
   * collisions or not-matched entries, since they both have the same data types.
   * @param tsdb The TSDB to which we belong
   * @param query The HTTP query to work with
   * @param for_collisions
   */
  private void handleCollisionNotMatched(TSDB tsdb, HttpQuery query, final boolean for_collisions) {
    final Map<String, Object> map;
    if (query.hasContent()) {
      map = query.serializer().parseTreeTSUIDsListV1();
    } else {
      map = parseTSUIDsList(query);
    }
   
    final Integer tree_id = (Integer) map.get("treeId");
    if (tree_id == null) {
      throw new BadRequestException("Missing or invalid Tree ID");
    }
   
    // make sure the tree exists
    try {
     
      if (Tree.fetchTree(tsdb, tree_id).joinUninterruptibly() == null) {
        throw new BadRequestException(HttpResponseStatus.NOT_FOUND,
            "Unable to locate tree: " + tree_id);
      }
 
      if (query.getAPIMethod() == HttpMethod.GET || query.getAPIMethod() == HttpMethod.POST ||
          query.getAPIMethod() == HttpMethod.PUT) {
 
        // ugly, but keeps from having to create a dedicated class just to
        // convert one field.
        @SuppressWarnings("unchecked")
        final List<String> tsuids = (List<String>)map.get("tsuids");
        final Map<String, String> results = for_collisions ?
            Tree.fetchCollisions(tsdb, tree_id, tsuids).joinUninterruptibly() :
              Tree.fetchNotMatched(tsdb, tree_id, tsuids).joinUninterruptibly();
        query.sendReply(query.serializer().formatTreeCollisionNotMatchedV1(
            results, for_collisions));
 
      } else {
        throw new BadRequestException(HttpResponseStatus.BAD_REQUEST,
        "Unsupported HTTP request method");
      }
   
    } catch (ClassCastException e) {
      throw new BadRequestException(
          "Unable to convert the given data to a list", e);
    } catch (BadRequestException e) {
      throw e;
    } catch (IllegalArgumentException e) {
      throw new BadRequestException(e);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
 
  /**
   * Parses query string parameters into a blank tree object. Used for updating
   * tree meta data.
   * @param query The HTTP query to work with
   * @return A tree object filled in with changes
   * @throws BadRequestException if some of the data was invalid
   */
  private Tree parseTree(HttpQuery query) {
    final Tree tree = new Tree(parseTreeId(query, false));
    if (query.hasQueryStringParam("name")) {
      tree.setName(query.getQueryStringParam("name"));
    }
    if (query.hasQueryStringParam("description")) {
      tree.setDescription(query.getQueryStringParam("description"));
    }
    if (query.hasQueryStringParam("notes")) {
      tree.setNotes(query.getQueryStringParam("notes"));
    }
    if (query.hasQueryStringParam("strict_match")) {
      if (query.getQueryStringParam("strict_match").toLowerCase()
          .equals("true")) {
        tree.setStrictMatch(true);
      } else {
        tree.setStrictMatch(false);
      }
    }
    if (query.hasQueryStringParam("enabled")) {
      final String enabled = query.getQueryStringParam("enabled");
      if (enabled.toLowerCase().equals("true")) {
        tree.setEnabled(true);
      } else {
        tree.setEnabled(false);
      }
    }
    if (query.hasQueryStringParam("store_failures")) {
      if (query.getQueryStringParam("store_failures").toLowerCase()
          .equals("true")) {
        tree.setStoreFailures(true);
      } else {
        tree.setStoreFailures(false);
      }
    }
    return tree;
  }

  /**
   * Parses query string parameters into a blank tree rule object. Used for
   * updating individual rules
   * @param query The HTTP query to work with
   * @return A rule object filled in with changes
   * @throws BadRequestException if some of the data was invalid
   */
  private TreeRule parseRule(HttpQuery query) {
    final TreeRule rule = new TreeRule(parseTreeId(query, true));
   
    if (query.hasQueryStringParam("type")) {
      try {
        rule.setType(TreeRule.stringToType(query.getQueryStringParam("type")));
      } catch (IllegalArgumentException e) {
        throw new BadRequestException("Unable to parse the 'type' parameter", e);
      }
    }
    if (query.hasQueryStringParam("field")) {
      rule.setField(query.getQueryStringParam("field"));
    }
    if (query.hasQueryStringParam("custom_field")) {
      rule.setCustomField(query.getQueryStringParam("custom_field"));
    }
    if (query.hasQueryStringParam("regex")) {
      try {
        rule.setRegex(query.getQueryStringParam("regex"));
      } catch (PatternSyntaxException e) {
        throw new BadRequestException(
            "Unable to parse the 'regex' parameter", e);
      }
    }
    if (query.hasQueryStringParam("separator")) {
      rule.setSeparator(query.getQueryStringParam("separator"));
    }
    if (query.hasQueryStringParam("description")) {
      rule.setDescription(query.getQueryStringParam("description"));
    }
    if (query.hasQueryStringParam("notes")) {
      rule.setNotes(query.getQueryStringParam("notes"));
    }
    if (query.hasQueryStringParam("regex_group_idx")) {
      try {
        rule.setRegexGroupIdx(Integer.parseInt(
            query.getQueryStringParam("regex_group_idx")));
      } catch (NumberFormatException e) {
        throw new BadRequestException(
            "Unable to parse the 'regex_group_idx' parameter", e);
      }
    }
    if (query.hasQueryStringParam("display_format")) {
      rule.setDisplayFormat(query.getQueryStringParam("display_format"));
    }
    //if (query.hasQueryStringParam("level")) {
      try {
        rule.setLevel(Integer.parseInt(
            query.getRequiredQueryStringParam("level")));
      } catch (NumberFormatException e) {
        throw new BadRequestException(
            "Unable to parse the 'level' parameter", e);
      }
    //}
    //if (query.hasQueryStringParam("order")) {
      try {
        rule.setOrder(Integer.parseInt(
            query.getRequiredQueryStringParam("order")));
      } catch (NumberFormatException e) {
        throw new BadRequestException(
            "Unable to parse the 'order' parameter", e);
      }
    //}
    return rule;
  }
 
  /**
   * Parses the tree ID from a query
   * Used often so it's been broken into it's own method
   * @param query The HTTP query to work with
   * @param required Whether or not the ID is required for the given call
   * @return The tree ID or 0 if not provided
   */
  private int parseTreeId(HttpQuery query, final boolean required) {
    try{
      if (required) {
        return Integer.parseInt(query.getRequiredQueryStringParam("treeid"));
      } else {
        if (query.hasQueryStringParam("treeid")) {
          return Integer.parseInt(query.getQueryStringParam("treeid"));
        } else {
          return 0;
        }
      }
    } catch (NumberFormatException nfe) {
      throw new BadRequestException("Unable to parse 'tree' value", nfe);
    }
  }

  /**
   * Used to parse a list of TSUIDs from the query string for collision or not
   * matched requests. TSUIDs must be comma separated.
   * @param query The HTTP query to work with
   * @return A map with a list of tsuids. If found, the tsuids array will be
   * under the "tsuid" key. The map is necessary for compatability with POJO
   * parsing.
   */
  private Map<String, Object> parseTSUIDsList(HttpQuery query) {
    final HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("treeId", parseTreeId(query, true));
   
    final String tsquery = query.getQueryStringParam("tsuids");
    if (tsquery != null) {
      final String[] tsuids = tsquery.split(",");
      map.put("tsuids", Arrays.asList(tsuids));
    }
   
    return map;
  }
}
TOP

Related Classes of net.opentsdb.tsd.TreeRpc

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.