This is an automated email from the ASF dual-hosted git repository. jbarrett pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push: new 23e554bbe6 GEODE-10249: Adds BufferPoolMXBean stats. (#7607) 23e554bbe6 is described below commit 23e554bbe633c7fca189c788d34c85778924bafb Author: Jacob Barrett <jbarr...@pivotal.io> AuthorDate: Mon Apr 25 12:54:01 2022 -0700 GEODE-10249: Adds BufferPoolMXBean stats. (#7607) * Adds new class for BufferPoolMXBean. * Adds tests for BufferPoolStats. * Cleanup VMStats50. --- .../stats50/BufferPoolStatsIntegrationTest.java | 95 +++++++++++ .../geode/internal/stats50/BufferPoolStats.java | 86 ++++++++++ .../apache/geode/internal/stats50/VMStats50.java | 68 ++++---- .../internal/stats50/BufferPoolStatsTest.java | 174 +++++++++++++++++++++ 4 files changed, 382 insertions(+), 41 deletions(-) diff --git a/geode-core/src/integrationTest/java/org/apache/geode/internal/stats50/BufferPoolStatsIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/internal/stats50/BufferPoolStatsIntegrationTest.java new file mode 100644 index 0000000000..9383d938be --- /dev/null +++ b/geode-core/src/integrationTest/java/org/apache/geode/internal/stats50/BufferPoolStatsIntegrationTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.geode.internal.stats50; + +import static java.lang.management.ManagementFactory.getPlatformMXBeans; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.lang.management.BufferPoolMXBean; +import java.nio.ByteBuffer; + +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import org.apache.geode.Statistics; +import org.apache.geode.StatisticsFactory; +import org.apache.geode.StatisticsType; +import org.apache.geode.StatisticsTypeFactory; + +class BufferPoolStatsIntegrationTest { + + @Test + void refreshAfterDirectBufferAllocationChangesStatistics() { + assumeThat(getPlatformMXBeans(BufferPoolMXBean.class)) + .anySatisfy(p -> assertThat(p.getName()).contains("direct")); + + final StatisticsTypeFactory statisticsTypeFactory = mock(StatisticsTypeFactory.class); + final StatisticsType statisticsType = mock(StatisticsType.class); + when(statisticsTypeFactory.createType(anyString(), anyString(), any())) + .thenReturn(statisticsType); + when(statisticsType.nameToId(eq("count"))).thenReturn(0); + when(statisticsType.nameToId(eq("totalCapacity"))).thenReturn(1); + when(statisticsType.nameToId(eq("memoryUsed"))).thenReturn(2); + final StatisticsFactory statisticsFactory = mock(StatisticsFactory.class); + final Statistics statistics = mock(Statistics.class); + when(statisticsFactory.createStatistics(any(), contains("direct"), anyLong())) + .thenReturn(statistics); + + final BufferPoolStats bufferPoolStats = new BufferPoolStats(statisticsTypeFactory); + bufferPoolStats.init(statisticsFactory, 42); + + bufferPoolStats.refresh(); + + final ArgumentCaptor<Long> count = ArgumentCaptor.forClass(Long.class); + verify(statistics).setLong(eq(0), count.capture()); + final ArgumentCaptor<Long> totalCapacity = ArgumentCaptor.forClass(Long.class); + verify(statistics).setLong(eq(1), totalCapacity.capture()); + final ArgumentCaptor<Long> memoryUsed = ArgumentCaptor.forClass(Long.class); + verify(statistics).setLong(eq(2), memoryUsed.capture()); + + clearInvocations(statistics); + + final ByteBuffer directBuffer = ByteBuffer.allocateDirect(1000); + + bufferPoolStats.refresh(); + + final ArgumentCaptor<Long> countAfterAllocate = ArgumentCaptor.forClass(Long.class); + verify(statistics).setLong(eq(0), countAfterAllocate.capture()); + final ArgumentCaptor<Long> totalCapacityAfterAllocate = ArgumentCaptor.forClass(Long.class); + verify(statistics).setLong(eq(1), totalCapacityAfterAllocate.capture()); + final ArgumentCaptor<Long> memoryUsedAfterAllocate = ArgumentCaptor.forClass(Long.class); + verify(statistics).setLong(eq(2), memoryUsedAfterAllocate.capture()); + + assertThat(countAfterAllocate.getValue()).isGreaterThan(count.getValue()); + assertThat(totalCapacityAfterAllocate.getValue()).isGreaterThan(totalCapacity.getValue()); + assertThat(memoryUsedAfterAllocate.getValue()).isGreaterThan(memoryUsed.getValue()); + + // Used only to prevent GC of directBuffer during test + assertThat(directBuffer).isNotNull(); + } + + +} diff --git a/geode-core/src/main/java/org/apache/geode/internal/stats50/BufferPoolStats.java b/geode-core/src/main/java/org/apache/geode/internal/stats50/BufferPoolStats.java new file mode 100644 index 0000000000..1de05fb293 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/stats50/BufferPoolStats.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.geode.internal.stats50; + +import static java.lang.management.ManagementFactory.getPlatformMXBeans; + +import java.lang.management.BufferPoolMXBean; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; + +import org.apache.geode.StatisticDescriptor; +import org.apache.geode.Statistics; +import org.apache.geode.StatisticsFactory; +import org.apache.geode.StatisticsType; +import org.apache.geode.StatisticsTypeFactory; + +/** + * Polls Java platform buffer pool statistics from {@link BufferPoolMXBean}. + */ +public class BufferPoolStats { + + private final StatisticsType bufferPoolType; + private final int bufferPoolCountId; + private final int bufferPoolTotalCapacityId; + private final int bufferPoolMemoryUsedId; + + private final Map<BufferPoolMXBean, Statistics> bufferPoolStatistics = + new IdentityHashMap<>(); + + BufferPoolStats(final @NotNull StatisticsTypeFactory typeFactory) { + bufferPoolType = + typeFactory.createType("PlatformBufferPoolStats", "Java platform buffer pools.", + new StatisticDescriptor[] { + typeFactory.createLongGauge("count", + "An estimate of the number of buffers in this pool.", "buffers"), + typeFactory.createLongGauge("totalCapacity", + "An estimate of the total capacity of the buffers in this pool in bytes.", + "bytes"), + typeFactory.createLongGauge("memoryUsed", + "An estimate of the memory that the Java virtual machine is using for this buffer pool in bytes, or -1L if an estimate of the memory usage is not available.", + "bytes")}); + bufferPoolCountId = bufferPoolType.nameToId("count"); + bufferPoolTotalCapacityId = bufferPoolType.nameToId("totalCapacity"); + bufferPoolMemoryUsedId = bufferPoolType.nameToId("memoryUsed"); + } + + void init(final @NotNull StatisticsFactory statisticsFactory, final long id) { + init(statisticsFactory, id, getPlatformMXBeans(BufferPoolMXBean.class)); + } + + void init(final StatisticsFactory statisticsFactory, final long id, + final List<BufferPoolMXBean> platformMXBeans) { + platformMXBeans.forEach( + pool -> bufferPoolStatistics.computeIfAbsent(pool, + k -> statisticsFactory.createStatistics(bufferPoolType, k.getName() + " buffer pool", + id))); + } + + void refresh() { + bufferPoolStatistics.forEach((bufferPool, statistics) -> { + statistics.setLong(bufferPoolCountId, bufferPool.getCount()); + statistics.setLong(bufferPoolTotalCapacityId, bufferPool.getTotalCapacity()); + statistics.setLong(bufferPoolMemoryUsedId, bufferPool.getMemoryUsed()); + }); + } + + public void close() { + bufferPoolStatistics.values().forEach(Statistics::close); + } +} diff --git a/geode-core/src/main/java/org/apache/geode/internal/stats50/VMStats50.java b/geode-core/src/main/java/org/apache/geode/internal/stats50/VMStats50.java index a2d25dadeb..10e7c36509 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/stats50/VMStats50.java +++ b/geode-core/src/main/java/org/apache/geode/internal/stats50/VMStats50.java @@ -113,10 +113,7 @@ public class VMStats50 implements VMStatsContract { private static final int mp_l_maxMemoryId; private static final int mp_l_usedMemoryId; private static final int mp_l_committedMemoryId; - // private static final int mp_gc_initMemoryId; - // private static final int mp_gc_maxMemoryId; private static final int mp_gc_usedMemoryId; - // private static final int mp_gc_committedMemoryId; private static final int mp_usageThresholdId; private static final int mp_collectionUsageThresholdId; private static final int mp_usageExceededId; @@ -128,7 +125,6 @@ public class VMStats50 implements VMStatsContract { private static final int unix_fdLimitId; private static final int unix_fdsOpenId; private static final int processCpuTimeId; - private long threadStartCount = 0; private long[] allThreadIds = null; private static final boolean THREAD_STATS_ENABLED = @@ -147,6 +143,9 @@ public class VMStats50 implements VMStatsContract { private static final int thread_cpuTimeId; private static final int thread_userTimeId; + @Immutable + private static final BufferPoolStats bufferPoolStats; + static { clBean = ManagementFactory.getClassLoadingMXBean(); memBean = ManagementFactory.getMemoryMXBean(); @@ -157,20 +156,17 @@ public class VMStats50 implements VMStatsContract { Method m3 = null; Object bean = null; try { - Class c = + Class<?> c = ClassPathLoader.getLatest().forName("com.sun.management.UnixOperatingSystemMXBean"); if (c.isInstance(osBean)) { m1 = c.getMethod("getMaxFileDescriptorCount"); m2 = c.getMethod("getOpenFileDescriptorCount"); bean = osBean; - } else { - // leave them null } + // Always set ProcessCpuTime m3 = osBean.getClass().getMethod("getProcessCpuTime"); - if (m3 != null) { - m3.setAccessible(true); - } + m3.setAccessible(true); } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned @@ -215,17 +211,17 @@ public class VMStats50 implements VMStatsContract { } StatisticsTypeFactory f = StatisticsTypeFactoryImpl.singleton(); List<StatisticDescriptor> sds = new ArrayList<>(); - sds.add(f.createIntGauge("pendingFinalization", + sds.add(f.createLongGauge("pendingFinalization", "Number of objects that are pending finalization in the java VM.", "objects")); - sds.add(f.createIntGauge("daemonThreads", "Current number of live daemon threads in this VM.", + sds.add(f.createLongGauge("daemonThreads", "Current number of live daemon threads in this VM.", "threads")); - sds.add(f.createIntGauge("threads", + sds.add(f.createLongGauge("threads", "Current number of live threads (both daemon and non-daemon) in this VM.", "threads")); sds.add( - f.createIntGauge("peakThreads", "High water mark of live threads in this VM.", "threads")); + f.createLongGauge("peakThreads", "High water mark of live threads in this VM.", "threads")); sds.add(f.createLongCounter("threadStarts", "Total number of times a thread has been started since this vm started.", "threads")); - sds.add(f.createIntGauge("cpus", "Number of cpus available to the java VM on its machine.", + sds.add(f.createLongGauge("cpus", "Number of cpus available to the java VM on its machine.", "cpus", true)); sds.add(f.createLongCounter("loadedClasses", "Total number of classes loaded since vm started.", "classes")); @@ -350,8 +346,8 @@ public class VMStats50 implements VMStatsContract { "milliseconds"), f.createLongGauge("lockOwner", "The thread id that owns the lock that this thread is blocking on.", "threadId"), - f.createIntGauge("inNative", "1 if the thread is in native code.", "boolean"), - f.createIntGauge("suspended", "1 if this thread is suspended", "boolean"), + f.createLongGauge("inNative", "1 if the thread is in native code.", "boolean"), + f.createLongGauge("suspended", "1 if this thread is suspended", "boolean"), f.createLongCounter("waited", "Total number of times this thread waited for notification.", "operations"), f.createLongCounter("waitedTime", @@ -384,6 +380,8 @@ public class VMStats50 implements VMStatsContract { thread_cpuTimeId = -1; thread_userTimeId = -1; } + + bufferPoolStats = new BufferPoolStats(f); } private final Statistics vmStats; @@ -399,7 +397,8 @@ public class VMStats50 implements VMStatsContract { vmStats = f.createStatistics(vmType, "vmStats", id); heapMemStats = f.createStatistics(memoryUsageType, "vmHeapMemoryStats", id); nonHeapMemStats = f.createStatistics(memoryUsageType, "vmNonHeapMemoryStats", id); - initMemoryPools(); // Fix for #40424 + initMemoryPools(); + bufferPoolStats.init(f, id); initGC(); } @@ -443,8 +442,8 @@ public class VMStats50 implements VMStatsContract { s.setLong(thread_blockedId, ti.getBlockedCount()); s.setLong(thread_lockOwnerId, ti.getLockOwnerId()); s.setLong(thread_waitedId, ti.getWaitedCount()); - s.setInt(thread_inNativeId, ti.isInNative() ? 1 : 0); - s.setInt(thread_suspendedId, ti.isSuspended() ? 1 : 0); + s.setLong(thread_inNativeId, ti.isInNative() ? 1 : 0); + s.setLong(thread_suspendedId, ti.isSuspended() ? 1 : 0); if (threadBean.isThreadContentionMonitoringSupported() && threadBean.isThreadContentionMonitoringEnabled()) { s.setLong(thread_blockedTimeId, ti.getBlockedTime()); @@ -499,7 +498,7 @@ public class VMStats50 implements VMStatsContract { it.remove(); reInitPools = true; } else { - MemoryUsage mu = null; + MemoryUsage mu; try { mu = mp.getUsage(); } catch (IllegalArgumentException ex) { @@ -584,11 +583,11 @@ public class VMStats50 implements VMStatsContract { @Override public void refresh() { Runtime rt = Runtime.getRuntime(); - vmStats.setInt(pendingFinalizationCountId, memBean.getObjectPendingFinalizationCount()); - vmStats.setInt(cpusId, osBean.getAvailableProcessors()); - vmStats.setInt(threadsId, threadBean.getThreadCount()); - vmStats.setInt(daemonThreadsId, threadBean.getDaemonThreadCount()); - vmStats.setInt(peakThreadsId, threadBean.getPeakThreadCount()); + vmStats.setLong(pendingFinalizationCountId, memBean.getObjectPendingFinalizationCount()); + vmStats.setLong(cpusId, osBean.getAvailableProcessors()); + vmStats.setLong(threadsId, threadBean.getThreadCount()); + vmStats.setLong(daemonThreadsId, threadBean.getDaemonThreadCount()); + vmStats.setLong(peakThreadsId, threadBean.getPeakThreadCount()); vmStats.setLong(threadStartsId, threadBean.getTotalStartedThreadCount()); vmStats.setLong(loadedClassesId, clBean.getTotalLoadedClassCount()); vmStats.setLong(unloadedClassesId, clBean.getUnloadedClassCount()); @@ -641,6 +640,7 @@ public class VMStats50 implements VMStatsContract { refresh(nonHeapMemStats, getNonHeapMemoryUsage(memBean)); refreshGC(); refreshMemoryPools(); + bufferPoolStats.refresh(); refreshThreads(); } @@ -693,6 +693,7 @@ public class VMStats50 implements VMStatsContract { vmStats.close(); closeStatsMap(mpMap); closeStatsMap(gcMap); + bufferPoolStats.close(); } private void closeStatsMap(Map<?, Statistics> map) { @@ -723,23 +724,8 @@ public class VMStats50 implements VMStatsContract { return heapMemStats; } - public Statistics getVMNonHeapStats() { - return nonHeapMemStats; - } - public static StatisticsType getGCType() { return gcType; } - public static StatisticsType getMemoryPoolType() { - return mpType; - } - - public static StatisticsType getThreadType() { - return threadType; - } - - public static StatisticsType getMemoryUsageType() { - return memoryUsageType; - } } diff --git a/geode-core/src/test/java/org/apache/geode/internal/stats50/BufferPoolStatsTest.java b/geode-core/src/test/java/org/apache/geode/internal/stats50/BufferPoolStatsTest.java new file mode 100644 index 0000000000..9f8fd45d91 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/internal/stats50/BufferPoolStatsTest.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.geode.internal.stats50; + +import static java.util.Arrays.asList; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.lang.management.BufferPoolMXBean; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import org.apache.geode.Statistics; +import org.apache.geode.StatisticsFactory; +import org.apache.geode.StatisticsType; +import org.apache.geode.StatisticsTypeFactory; + +class BufferPoolStatsTest { + + @Test + void constructorCreatesStatisticsType() { + final StatisticsTypeFactory statisticsTypeFactory = mock(StatisticsTypeFactory.class); + when(statisticsTypeFactory.createType(anyString(), anyString(), any())) + .thenReturn(mock(StatisticsType.class)); + + new BufferPoolStats(statisticsTypeFactory); + + verify(statisticsTypeFactory).createType(eq("PlatformBufferPoolStats"), anyString(), any()); + verify(statisticsTypeFactory).createLongGauge(eq("count"), anyString(), eq("buffers")); + verify(statisticsTypeFactory).createLongGauge(eq("totalCapacity"), anyString(), eq("bytes")); + verify(statisticsTypeFactory).createLongGauge(eq("memoryUsed"), anyString(), eq("bytes")); + verifyNoMoreInteractions(statisticsTypeFactory); + } + + @Test + void initCreatesStatistics() { + final StatisticsTypeFactory statisticsTypeFactory = mock(StatisticsTypeFactory.class); + final StatisticsType statisticsType = mock(StatisticsType.class); + when(statisticsTypeFactory.createType(anyString(), anyString(), any())) + .thenReturn(statisticsType); + final StatisticsFactory statisticsFactory = mock(StatisticsFactory.class); + when(statisticsFactory.createStatistics(any(), anyString(), anyLong())) + .thenReturn(mock(Statistics.class), mock(Statistics.class)); + final BufferPoolMXBean bufferPoolMXBean1 = mock(BufferPoolMXBean.class); + when(bufferPoolMXBean1.getName()).thenReturn("mocked1"); + final BufferPoolMXBean bufferPoolMXBean2 = mock(BufferPoolMXBean.class); + when(bufferPoolMXBean2.getName()).thenReturn("mocked2"); + final List<BufferPoolMXBean> platformMXBeans = asList(bufferPoolMXBean1, bufferPoolMXBean2); + + final BufferPoolStats bufferPoolStats = new BufferPoolStats(statisticsTypeFactory); + bufferPoolStats.init(statisticsFactory, 42, platformMXBeans); + + verify(bufferPoolMXBean1).getName(); + verify(bufferPoolMXBean2).getName(); + verify(statisticsFactory).createStatistics(same(statisticsType), contains("mocked1"), eq(42L)); + verify(statisticsFactory).createStatistics(same(statisticsType), contains("mocked2"), eq(42L)); + verifyNoMoreInteractions(bufferPoolMXBean1, bufferPoolMXBean2, statisticsFactory); + } + + @Test + void initCreatesStatisticsOnceIfCalledTwice() { + final StatisticsTypeFactory statisticsTypeFactory = mock(StatisticsTypeFactory.class); + final StatisticsType statisticsType = mock(StatisticsType.class); + when(statisticsTypeFactory.createType(anyString(), anyString(), any())) + .thenReturn(statisticsType); + final StatisticsFactory statisticsFactory = mock(StatisticsFactory.class); + when(statisticsFactory.createStatistics(any(), anyString(), anyLong())) + .thenReturn(mock(Statistics.class), mock(Statistics.class)); + final BufferPoolMXBean bufferPoolMXBean1 = mock(BufferPoolMXBean.class); + when(bufferPoolMXBean1.getName()).thenReturn("mocked1"); + final BufferPoolMXBean bufferPoolMXBean2 = mock(BufferPoolMXBean.class); + when(bufferPoolMXBean2.getName()).thenReturn("mocked2"); + final List<BufferPoolMXBean> platformMXBeans = asList(bufferPoolMXBean1, bufferPoolMXBean2); + + final BufferPoolStats bufferPoolStats = new BufferPoolStats(statisticsTypeFactory); + bufferPoolStats.init(statisticsFactory, 42, platformMXBeans); + bufferPoolStats.init(statisticsFactory, 42, platformMXBeans); + + verify(bufferPoolMXBean1).getName(); + verify(bufferPoolMXBean2).getName(); + verify(statisticsFactory).createStatistics(same(statisticsType), contains("mocked1"), eq(42L)); + verify(statisticsFactory).createStatistics(same(statisticsType), contains("mocked2"), eq(42L)); + verifyNoMoreInteractions(bufferPoolMXBean1, bufferPoolMXBean2, statisticsFactory); + } + + @Test + void refreshUpdatesStatistics() { + final StatisticsTypeFactory statisticsTypeFactory = mock(StatisticsTypeFactory.class); + when(statisticsTypeFactory.createType(anyString(), anyString(), any())) + .thenReturn(mock(StatisticsType.class)); + final StatisticsFactory statisticsFactory = mock(StatisticsFactory.class); + final Statistics statistics1 = mock(Statistics.class); + final Statistics statistics2 = mock(Statistics.class); + when(statisticsFactory.createStatistics(any(), anyString(), anyLong())) + .thenReturn(statistics1, statistics2); + final BufferPoolMXBean bufferPoolMXBean1 = mock(BufferPoolMXBean.class); + when(bufferPoolMXBean1.getName()).thenReturn("mocked1"); + when(bufferPoolMXBean1.getCount()).thenReturn(1200L); + when(bufferPoolMXBean1.getTotalCapacity()).thenReturn(2400L); + when(bufferPoolMXBean1.getMemoryUsed()).thenReturn(9600L); + final BufferPoolMXBean bufferPoolMXBean2 = mock(BufferPoolMXBean.class); + when(bufferPoolMXBean2.getName()).thenReturn("mocked2"); + when(bufferPoolMXBean2.getCount()).thenReturn(1L); + when(bufferPoolMXBean2.getTotalCapacity()).thenReturn(2L); + when(bufferPoolMXBean2.getMemoryUsed()).thenReturn(3L); + final List<BufferPoolMXBean> platformMXBeans = asList(bufferPoolMXBean1, bufferPoolMXBean2); + + final BufferPoolStats bufferPoolStats = new BufferPoolStats(statisticsTypeFactory); + bufferPoolStats.init(statisticsFactory, 42, platformMXBeans); + bufferPoolStats.refresh(); + + verify(bufferPoolMXBean1).getName(); + verify(bufferPoolMXBean2).getName(); + verify(bufferPoolMXBean1).getCount(); + verify(bufferPoolMXBean1).getTotalCapacity(); + verify(bufferPoolMXBean1).getMemoryUsed(); + verify(bufferPoolMXBean2).getCount(); + verify(bufferPoolMXBean2).getTotalCapacity(); + verify(bufferPoolMXBean2).getMemoryUsed(); + verify(statistics1).setLong(anyInt(), eq(1200L)); + verify(statistics1).setLong(anyInt(), eq(2400L)); + verify(statistics1).setLong(anyInt(), eq(9600L)); + verify(statistics2).setLong(anyInt(), eq(1L)); + verify(statistics2).setLong(anyInt(), eq(2L)); + verify(statistics2).setLong(anyInt(), eq(3L)); + verifyNoMoreInteractions(bufferPoolMXBean1, bufferPoolMXBean2, statistics1, statistics2); + } + + @Test + void closeClosesAllStatistics() { + final StatisticsTypeFactory statisticsTypeFactory = mock(StatisticsTypeFactory.class); + when(statisticsTypeFactory.createType(anyString(), anyString(), any())) + .thenReturn(mock(StatisticsType.class)); + final StatisticsFactory statisticsFactory = mock(StatisticsFactory.class); + final Statistics statistics1 = mock(Statistics.class); + final Statistics statistics2 = mock(Statistics.class); + when(statisticsFactory.createStatistics(any(), anyString(), anyLong())) + .thenReturn(statistics1, statistics2); + final List<BufferPoolMXBean> platformMXBeans = + asList(mock(BufferPoolMXBean.class), mock(BufferPoolMXBean.class)); + + final BufferPoolStats bufferPoolStats = new BufferPoolStats(statisticsTypeFactory); + bufferPoolStats.init(statisticsFactory, 42, platformMXBeans); + bufferPoolStats.close(); + + verify(statistics1).close(); + verify(statistics2).close(); + verifyNoMoreInteractions(statistics1, statistics2); + } + +}