@Override
  public void getMedian(RpcController controller, AggregateRequest request,
    RpcCallback<AggregateResponse> done) {
    AggregateResponse response = null;
    InternalScanner scanner = null;
    PartialResultContext partialResultContext = new PartialResultContext();
    try {
      ColumnInterpreter<T, S, P, Q, R> ci = constructColumnInterpreterFromRequest(request);
      S sumVal = null, sumWeights = null, tempVal = null, tempWeight = null;
      Scan scan = ProtobufUtil.toScan(request.getScan());
      scanner = env.getRegion().getScanner(scan);
      byte[] colFamily = scan.getFamilies()[0];
      NavigableSet<byte[]> qualifiers = scan.getFamilyMap().get(colFamily);
      byte[] valQualifier = null, weightQualifier = null;
      if (qualifiers != null && !qualifiers.isEmpty()) {
        valQualifier = qualifiers.pollFirst();
        // if weighted median is requested, get qualifier for the weight column
        weightQualifier = qualifiers.pollLast();
      }
      List<Cell> results = new ArrayList<>();

      boolean hasMoreRows = true;
      do {
        if (shouldBreakForThrottling(request, scan, partialResultContext)) {
          break;
        }
        tempVal = null;
        tempWeight = null;
        hasMoreRows = scanner.next(results);
        int listSize = results.size();
        for (int i = 0; i < listSize; i++) {
          Cell kv = results.get(i);
          tempVal = ci.add(tempVal, ci.castToReturnType(ci.getValue(colFamily, valQualifier, kv)));
          if (weightQualifier != null) {
            tempWeight =
              ci.add(tempWeight, ci.castToReturnType(ci.getValue(colFamily, weightQualifier, kv)));
          }
        }
        postScanPartialResultUpdate(results, partialResultContext);
        results.clear();
        sumVal = ci.add(sumVal, tempVal);
        sumWeights = ci.add(sumWeights, tempWeight);
      } while (hasMoreRows);
      if (sumVal != null && !request.getClientSupportsPartialResult()) {
        AggregateResponse.Builder pair = AggregateResponse.newBuilder();
        ByteString first_sumVal = ci.getProtoForPromotedType(sumVal).toByteString();
        S s = sumWeights == null ? ci.castToReturnType(ci.getMinValue()) : sumWeights;
        ByteString first_sumWeights = ci.getProtoForPromotedType(s).toByteString();
        pair.addFirstPart(first_sumVal);
        pair.addFirstPart(first_sumWeights);
        response = pair.build();
      } else if (request.getClientSupportsPartialResult()) {
        AggregateResponse.Builder pair =
          responseBuilder(request, hasMoreRows, partialResultContext);
        if (sumVal != null) {
          ByteString first_sumVal = ci.getProtoForPromotedType(sumVal).toByteString();
          S s = sumWeights == null ? ci.castToReturnType(ci.getMinValue()) : sumWeights;
          ByteString first_sumWeights = ci.getProtoForPromotedType(s).toByteString();
          pair.addFirstPart(first_sumVal);
          pair.addFirstPart(first_sumWeights);
        }
        response = pair.build();
      }
    } catch (IOException e) {
      CoprocessorRpcUtils.setControllerException(controller, e);
    } finally {
      if (scanner != null) {
        IOUtils.closeQuietly(scanner);
      }
      closeQuota(partialResultContext);
    }
    done.run(response);
  }
Gives a List containing sum of values and sum of weights. It is computed for the combination of
column family and column qualifier(s) in the given row range as defined in the Scan object. In
its current implementation, it takes one column family and two column qualifiers. The first
qualifier is for values column and the second qualifier (optional) is for weight column.
hbase-endpoint/src/main/java/org/apache/hadoop/hbase/coprocessor/AggregateImplementation.java