@Override
      public boolean next(List<? super ExtendedCell> result, ScannerContext scannerContext)
        throws IOException {
        boolean ret = scanner.next(result, scannerContext);
        for (int i = 0; i < result.size(); i++) {
          Cell c = (Cell) result.get(i);
          // Replace the Cell if the value is the one we're replacing
          if (CellUtil.isPut(c) && comparator.compare(CellUtil.cloneValue(c), sourceValue) == 0) {
            try {
              cellBuilder.setRow(CellUtil.copyRow(c));
              cellBuilder.setFamily(CellUtil.cloneFamily(c));
              cellBuilder.setQualifier(CellUtil.cloneQualifier(c));
              cellBuilder.setTimestamp(c.getTimestamp());
              cellBuilder.setType(Cell.Type.Put);
              // Make sure each cell gets a unique value
              byte[] clonedValue = new byte[replacedValue.length];
              System.arraycopy(replacedValue, 0, clonedValue, 0, replacedValue.length);
              cellBuilder.setValue(clonedValue);
              // all cells in HBase are ExtendedCells, so you are fine to cast it to ExtendedCell,
              // just do not use its methods since it may change without any deprecation cycle
              result.set(i, (ExtendedCell) cellBuilder.build());
            } finally {
              cellBuilder.clear();
            }
          }
        }
        return ret;
      }
This RegionObserver replaces the values of Puts from one value to another on compaction.
hbase-examples/src/main/java/org/apache/hadoop/hbase/coprocessor/example/ValueRewritingObserver.java