private <R, S, P extends Message, Q extends Message, T extends Message>
    Pair<NavigableMap<byte[], List<S>>, List<S>> getMedianArgs(final Table table,
      final ColumnInterpreter<R, S, P, Q, T> ci, final Scan scan) throws Throwable {
    final AggregateRequest requestArg = validateArgAndGetPB(scan, ci, false, false);
    final NavigableMap<byte[], List<S>> map = new TreeMap<>(Bytes.BYTES_COMPARATOR);
    class StdCallback implements Batch.Callback<List<S>> {
      S sumVal = null, sumWeights = null;

      public synchronized Pair<NavigableMap<byte[], List<S>>, List<S>> getMedianParams() {
        List<S> l = new ArrayList<>(2);
        l.add(sumVal);
        l.add(sumWeights);
        Pair<NavigableMap<byte[], List<S>>, List<S>> p = new Pair<>(map, l);
        return p;
      }

      @Override
      public synchronized void update(byte[] region, byte[] row, List<S> result) {
        map.put(row, result);
        sumVal = ci.add(sumVal, result.get(0));
        sumWeights = ci.add(sumWeights, result.get(1));
      }
    }
    StdCallback stdCallback = new StdCallback();
    table.coprocessorService(AggregateService.class, scan.getStartRow(), scan.getStopRow(),
      new Batch.Call<AggregateService, List<S>>() {
        @Override
        public List<S> call(AggregateService instance) throws IOException {
          RpcController controller = new AggregationClientRpcController();
          CoprocessorRpcUtils.BlockingRpcCallback<AggregateResponse> rpcCallback =
            new CoprocessorRpcUtils.BlockingRpcCallback<>();
          instance.getMedian(controller, requestArg, rpcCallback);
          AggregateResponse response = rpcCallback.get();
          if (controller.failed()) {
            throw new IOException(controller.errorText());
          }

          List<S> list = new ArrayList<>();
          for (int i = 0; i < response.getFirstPartCount(); i++) {
            ByteString b = response.getFirstPart(i);
            T t = getParsedGenericInstance(ci.getClass(), 4, b);
            S s = ci.getPromotedValueFromProto(t);
            list.add(s);
          }
          return list;
        }

      }, stdCallback);
    return stdCallback.getMedianParams();
  }
It helps locate the region with median for a given column whose weight is specified in an
optional column. From individual regions, it obtains sum of values and sum of weights.
@param table table to scan.
@param ci    the user's ColumnInterpreter implementation
@param scan  the HBase scan object to use to read data from HBase
@return pair whose first element is a map between start row of the region and (sum of values,
        sum of weights) for the region, the second element is (sum of values, sum of weights)
        for all the regions chosen
@throws Throwable The caller is supposed to handle the exception as they are thrown &amp;
                  propagated to it.
hbase-endpoint/src/main/java/org/apache/hadoop/hbase/client/coprocessor/AggregationClient.java