docs: public static Deferred<List<Tree>> fetchAllTrees(final TSDB tsdb) {
      
      final Deferred<List<Tree>> result = new Deferred<List<Tree>>();
      
      /**
       * Scanner callback that recursively calls itself to load the next set of
       * rows from storage. When the scanner returns a null, the callback will
       * return with the list of trees discovered.
       */
      final class AllTreeScanner implements Callback<Object, 
        ArrayList<ArrayList<KeyValue>>> {
    
        private final List<Tree> trees = new ArrayList<Tree>();
        private final Scanner scanner;
        
        public AllTreeScanner() {
          scanner = setupAllTreeScanner(tsdb);
        }
        
        /**
         * Fetches the next set of results from the scanner and adds this class
         * as a callback.
         * @return A list of trees if the scanner has reached the end
         */
        public Object fetchTrees() {
          return scanner.nextRows().addCallback(this);
        }
        
        @Override
        public Object call(ArrayList<ArrayList<KeyValue>> rows)
            throws Exception {
          if (rows == null) {
            result.callback(trees);
            return null;
          }
          
          for (ArrayList<KeyValue> row : rows) {
            final Tree tree = new Tree();
            for (KeyValue column : row) {
              if (column.qualifier().length >= TREE_QUALIFIER.length && 
                  Bytes.memcmp(TREE_QUALIFIER, column.qualifier()) == 0) {
                // it's *this* tree. We deserialize to a new object and copy
                // since the columns could be in any order and we may get a rule 
                // before the tree object
                final Tree local_tree = JSON.parseToObject(column.value(), 
                    Tree.class);
                tree.created = local_tree.created;
                tree.description = local_tree.description;
                tree.name = local_tree.name;
                tree.notes = local_tree.notes;
                tree.strict_match = local_tree.strict_match;
                tree.enabled = local_tree.enabled;
                tree.store_failures = local_tree.store_failures;
                
                // WARNING: Since the JSON data in storage doesn't contain the tree
                // ID, we need to parse it from the row key
                tree.setTreeId(bytesToId(row.get(0).key()));
                
              // tree rule
              } else if (column.qualifier().length > TreeRule.RULE_PREFIX().length &&
                  Bytes.memcmp(TreeRule.RULE_PREFIX(), column.qualifier(), 
                  0, TreeRule.RULE_PREFIX().length) == 0) {
                final TreeRule rule = TreeRule.parseFromStorage(column);
                tree.addRule(rule);
              }
            }
            
            // only add the tree if we parsed a valid ID
            if (tree.tree_id > 0) {
              trees.add(tree);
            }
          }
          
          // recurse to get the next set of rows from the scanner
          return fetchTrees();
        }
        
      }
      
      // start the scanning process
      new AllTreeScanner().fetchTrees();
      return result;
    }