@Test
  public void testLocalStatListener() throws Exception {
    connect(createGemFireProperties());

    GemFireStatSampler statSampler = getGemFireStatSampler();
    assertThat(statSampler.waitForInitialization(5000))
        .as("initialized within 5 seconds")
        .isTrue();

    Method getLocalListeners = getGemFireStatSampler().getClass().getMethod("getLocalListeners");
    assertThat(getLocalListeners).isNotNull();

    Method addLocalStatListener = getGemFireStatSampler().getClass()
        .getMethod("addLocalStatListener", LocalStatListener.class, Statistics.class, String.class);
    assertThat(addLocalStatListener).isNotNull();

    Method removeLocalStatListener = getGemFireStatSampler().getClass()
        .getMethod("removeLocalStatListener", LocalStatListener.class);
    assertThat(removeLocalStatListener).isNotNull();

    assertThat(statSampler.getLocalListeners())
        .as("local listeners before adding first listener")
        .isEmpty();

    // add a listener for sampleCount stat in StatSampler statistics
    StatisticsType statSamplerType = getStatisticsManager().findType("StatSampler");
    Statistics[] statsArray = getStatisticsManager().findStatisticsByType(statSamplerType);
    assertThat(statsArray.length)
        .as("stats array length")
        .isEqualTo(1);

    final Statistics statSamplerStats = statsArray[0];
    final String statName = "sampleCount";
    final AtomicInteger sampleCountValue = new AtomicInteger(0);
    final AtomicInteger sampleCountChanged = new AtomicInteger(0);

    LocalStatListener listener = value -> {
      sampleCountValue.set((int) value);
      sampleCountChanged.incrementAndGet();
    };

    statSampler.addLocalStatListener(listener, statSamplerStats, statName);
    assertThat(statSampler.getLocalListeners())
        .as("local listeners after adding 1 listener")
        .hasSize(1);

    // there's a level of indirection here and some protected member fields
    LocalStatListenerImpl lsli = statSampler.getLocalListeners().iterator().next();
    assertThat(lsli.stat.getName())
        .as("listener's first stat's name")
        .isEqualTo("sampleCount");

    await("listener to update several times").untilAsserted(
        () -> assertThat(sampleCountChanged).hasValueGreaterThanOrEqualTo(4));

    // validate that the listener fired and updated the value
    assertThat(sampleCountValue.get())
        .as("sample count value after the listener has fired")
        .isGreaterThan(0);

    // remove the listener
    statSampler.removeLocalStatListener(listener);
    final int expectedSampleCountValue = sampleCountValue.get();
    final int expectedSampleCountChanged = sampleCountChanged.get();

    assertThat(statSampler.getLocalListeners())
        .as("local listeners after removing the listener")
        .isEmpty();

    // wait for 2 stat samples to occur
    waitForStatSample(statSamplerStats, expectedSampleCountValue, 5000, 10);

    // validate that the listener did not fire
    assertThat(sampleCountValue.get())
        .as("sample count value after the listener was removed")
        .isEqualTo(expectedSampleCountValue);
    assertThat(sampleCountChanged.get())
        .as("sample count changed after the listener was removed")
        .isEqualTo(expectedSampleCountChanged);
  }
Adds a LocalStatListener for an individual stat. Validates that it receives notifications.
Removes the listener and validates that it was in fact removed and no longer receives
notifications.
geode-core/src/integrationTest/java/org/apache/geode/internal/statistics/GemFireStatSamplerIntegrationTest.java