blob: 3bc6c7919e98467c7ca415580693f3c1c7bcce14 [file] [log] [blame]
/*
* 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.statistics;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import org.apache.logging.log4j.Logger;
import org.apache.geode.GemFireException;
import org.apache.geode.StatisticDescriptor;
import org.apache.geode.Statistics;
import org.apache.geode.StatisticsType;
import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.internal.io.RollingFileHandler;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.logging.internal.log4j.api.LogService;
/**
* Captures sample of statistics. The SampleCollector contains maps of StatisticsTypes to
* ResourceTypes and Statistics instances to ResourceInstances, each of which contains the actual
* stat values of the sample. The {@link #sample(long)} operation determines what stats have
* changed, adds in new types and instances, and updates the sampled stat values.
* <p/>
* SampleHandlers are registered with the SampleCollector. The handlers are notified of any changes
* to statistics.
* <p/>
* Extracted from StatArchiveWriter.
*
* @since GemFire 7.0
*/
public class SampleCollector {
private static final Logger logger = LogService.getLogger();
/**
* Singleton instance of SampleCollector set during initialization. This field simply points to
* the latest initialized instance.
*/
@MakeNotStatic
private static SampleCollector instance;
/**
* The stat sampler using this collector to capture samples of stats
*/
private final StatisticsSampler sampler;
/**
* The handlers that are registered for notification of stat samples
*/
private final SampleHandlers sampleHandlers = new SampleHandlers();
/**
* Map of StatisticsType to ResourceType. Contains all currently known statistics types.
*/
private final Map<StatisticsType, ResourceType> resourceTypeMap =
new HashMap<StatisticsType, ResourceType>();
/**
* Map of Statistics to ResourceInstance. Contains all currently known statistics resources.
*/
private final Map<Statistics, ResourceInstance> resourceInstMap =
new HashMap<Statistics, ResourceInstance>();
/**
* Incremented to use as unique identifier to construct new ResourceType
*/
private int resourceTypeId = 0;
/**
* Incremented to use as unique identifier to construct new ResourceInstance
*/
private int resourceInstId = 0;
/**
* The number of statistics resources that existed during the latest sample
*/
private int statResourcesModCount;
/**
* The StatArchiveHandler which is created during initialization
*/
private StatArchiveHandler statArchiveHandler;
/**
* The StatArchiveHandler which is created on demand
*/
private StatMonitorHandler statMonitorHandler;
/**
* Constructs a new instance.
*
* @param sampler the stat sampler using this collector
*/
public SampleCollector(StatisticsSampler sampler) {
this.sampler = sampler;
if (sampler.getStatisticsModCount() == 0) {
this.statResourcesModCount = -1;
} else {
this.statResourcesModCount = 0;
}
}
/**
* Returns the {@link StatMonitorHandler}. If one does not currently exist it will be created.
*
* @return the StatMonitorHandler for adding monitors
* @throws IllegalStateException if no SampleCollector has been created and initialized yet
*/
static StatMonitorHandler getStatMonitorHandler() {
// sync SampleCollector.class and then instance.sampleHandlers
synchronized (SampleCollector.class) {
if (instance == null) {
throw new IllegalStateException("Statistics sampler is not available");
}
synchronized (instance.sampleHandlers) {
StatMonitorHandler handler = instance.statMonitorHandler;
if (handler == null) {
handler = new StatMonitorHandler();
instance.addSampleHandler(handler);
instance.statMonitorHandler = handler;
}
return handler;
}
}
}
/**
* Initializes this collector by creating a {@link StatArchiveHandler} and registering it as a
* handler.
*
* @param config defines the configuration for the StatArchiveHandler
* @param nanosTimeStamp the nanos time stamp to initialize stat archiver with
* @param rollingFileHandler provides file rolling behavior
*/
@SuppressWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD",
justification = "There is never more than one SampleCollector instance.")
public void initialize(final StatArchiveHandlerConfig config, final long nanosTimeStamp,
final RollingFileHandler rollingFileHandler) {
synchronized (SampleCollector.class) {
instance = this;
synchronized (this.sampleHandlers) {
StatArchiveHandler newStatArchiveHandler =
new StatArchiveHandler(config, this, rollingFileHandler);
this.statArchiveHandler = newStatArchiveHandler;
addSampleHandler(newStatArchiveHandler);
newStatArchiveHandler.initialize(nanosTimeStamp);
}
}
}
public boolean isInitialized() {
synchronized (SampleCollector.class) {
synchronized (this.sampleHandlers) {
return instance == this;
}
}
}
/**
* Adds a {@link SampleHandler} to this collector.
*
* @param handler the SampleHandler to add
*/
public void addSampleHandler(SampleHandler handler) {
this.sampleHandlers.addSampleHandler(handler);
}
/**
* Removes a {@link SampleHandler} from this collector.
*
* @param handler the SampleHandler to remove
*/
public void removeSampleHandler(SampleHandler handler) {
this.sampleHandlers.removeSampleHandler(handler);
}
/**
* Returns true if the specified SampleHandler is registered
*/
public boolean containsSampleHandler(SampleHandler handler) {
return this.sampleHandlers.contains(handler);
}
/**
* Collect a sample of all statistics. Adds new statistics resources, removes destroyed statistics
* resources, collects the latest stat values, and notifies SamplerHandlers of the sample.
* <p/>
* The timeStamp is an arbitrary nanoseconds time stamp only used for archiving to stats files.
* The initial time in system milliseconds and the first NanoTimer timeStamp are both written to
* the archive file. Thereafter only the NanoTimer timeStamp is written to the archive file for
* each sample. The delta in nanos can be determined by comparing any nano timeStamp to the first
* nano timeStamp written to the archive file. Adding this delta to the recorded initial time in
* milliseconds provides the actual (non-arbitrary) time for each sample.
*
* @param nanosTimeStamp an arbitrary time stamp in nanoseconds for this sample
*/
public void sample(long nanosTimeStamp) {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "SampleCollector#sample nanosTimeStamp={}",
nanosTimeStamp);
}
List<MarkableSampleHandler> handlers = this.sampleHandlers.currentHandlers();
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "SampleCollector#sample handlers={}", handlers);
}
sampleResources(handlers);
List<ResourceInstance> updatedResources = new ArrayList<ResourceInstance>();
for (ResourceInstance ri : this.resourceInstMap.values()) {
StatisticDescriptor[] stats = ri.getResourceType().getStatisticDescriptors();
if (ri.getStatistics().isClosed()) {
continue;
}
// size updatedStats to same size as stats in case all stats changed
int[] updatedStats = new int[stats.length];
long[] statValues = ri.getPreviousStatValues();
if (statValues == null) {
statValues = new long[stats.length];
for (int i = 0; i < stats.length; i++) {
statValues[i] = ri.getRawStatValue(stats[i]);
updatedStats[i] = i;
}
} else {
statValues = Arrays.copyOf(statValues, statValues.length);
int updatedStatsIdx = 0;
for (int i = 0; i < stats.length; i++) {
long value = ri.getRawStatValue(stats[i]);
if (value != statValues[i]) {
statValues[i] = value;
updatedStats[updatedStatsIdx] = i;
updatedStatsIdx++;
}
}
// resize updatedStats to only contain the the stats which were updated
updatedStats = Arrays.copyOf(updatedStats, updatedStatsIdx);
}
ri.setUpdatedStats(updatedStats);
ri.setLatestStatValues(statValues);
updatedResources.add(ri);
}
try {
notifyAllHandlersOfSample(handlers, updatedResources, nanosTimeStamp);
} catch (IllegalArgumentException e) {
logger.warn(LogMarker.STATISTICS_MARKER,
"Use of java.lang.System.nanoTime() resulted in a non-positive timestamp delta. Skipping notification of statistics sample.",
e);
}
for (ResourceInstance ri : updatedResources) {
ri.setPreviousStatValues(ri.getLatestStatValues());
}
}
public void close() {
// sync SampleCollector.class and then this.sampleHandlers
synchronized (SampleCollector.class) {
synchronized (this.sampleHandlers) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "SampleCollector#close");
}
try {
StatArchiveHandler handler = this.statArchiveHandler;
if (handler != null) {
handler.close();
}
} catch (GemFireException ignore) {
logger.warn(LogMarker.STATISTICS_MARKER, "Statistic archiver shutdown failed because: {}",
ignore.getMessage());
}
StatMonitorHandler handler = this.statMonitorHandler;
if (handler != null) {
handler.close();
}
}
if (instance == this) {
instance = null;
}
}
}
public void changeArchive(File newFile, long nanosTimeStamp) {
synchronized (this.sampleHandlers) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#changeArchive newFile={}, nanosTimeStamp={}", newFile, nanosTimeStamp);
}
StatArchiveHandler handler = this.statArchiveHandler;
if (handler != null) {
handler.changeArchiveFile(newFile, nanosTimeStamp);
}
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(getClass().getName());
sb.append("@").append(System.identityHashCode(this)).append("{");
sb.append("sampler=").append(this.sampler);
sb.append(", statResourcesModCount=").append(this.statResourcesModCount);
sb.append(", resourceTypeId=").append(this.resourceTypeId);
sb.append(", resourceInstId=").append(this.resourceInstId);
sb.append(", resourceTypeMap.size()=").append(this.resourceTypeMap.size());
sb.append(", resourceInstMap.size()=").append(this.resourceInstMap.size());
sb.append("}");
return sb.toString();
}
/**
* For testing only
*/
StatArchiveHandler getStatArchiveHandler() {
synchronized (this.sampleHandlers) {
return this.statArchiveHandler;
}
}
protected List<MarkableSampleHandler> currentHandlersForTesting() {
return this.sampleHandlers.currentHandlers();
}
/**
* Detect and archive any new resource additions or deletions
*/
private void sampleResources(List<MarkableSampleHandler> handlers) {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "SampleCollector#sampleResources handlers={}",
handlers);
}
int newModCount = this.sampler.getStatisticsModCount();
// TODO: what if one is deleted and one is added
if (this.statResourcesModCount != newModCount) {
this.statResourcesModCount = newModCount;
int ignoreCount = 0;
Statistics[] resources = this.sampler.getStatistics();
for (int i = 0; i < resources.length; i++) {
Statistics statistics = resources[i];
// only notify marked/old handlers of new types or resources
if (!this.resourceInstMap.containsKey(statistics)) {
try {
ResourceType type = getResourceType(handlers, statistics);
ResourceInstance resource = allocateResourceInstance(type, statistics);
notifyOldHandlersOfResource(handlers, resource);
} catch (IgnoreResourceException ex) {
ignoreCount++;
}
}
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#sampleResources resources.length={}, ignoreCount={}", resources.length,
ignoreCount);
}
List<ResourceInstance> ri = cleanupResources(resources, ignoreCount);
notifyOldHandlers(handlers, ri);
}
// notify unmarked/new handlers but not marked/old handlers
notifyNewHandlersOfResources(handlers, this.resourceTypeMap.values(),
this.resourceInstMap.values());
}
private ResourceType getResourceType(List<MarkableSampleHandler> handlers, Statistics statistics)
throws IgnoreResourceException {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "SampleCollector#getResourceType statistics={}",
statistics);
}
StatisticsType type = statistics.getType();
if (type == null) {
// bug 30707
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#getResourceType type={}, throwing IgnoreResourceException", type);
}
throw new IgnoreResourceException();
}
ResourceType resourceType = null;
try {
resourceType = (ResourceType) this.resourceTypeMap.get(type);
} catch (NullPointerException ex) {
// bug 30716
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#getResourceType resourceTypeMap.get threw NPE, throwing NullPointerException");
}
throw new IgnoreResourceException();
}
if (resourceType == null) {
resourceType = allocateResourceType(handlers, type);
notifyOldHandlersOfResourceType(handlers, resourceType);
}
return resourceType;
}
private ResourceType allocateResourceType(List<MarkableSampleHandler> handlers,
StatisticsType type) throws IgnoreResourceException {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "SampleCollector#allocateResourceType type={}",
type);
}
ResourceType resourceType = new ResourceType(this.resourceTypeId, type);
this.resourceTypeMap.put(type, resourceType);
this.resourceTypeId++;
return resourceType;
}
private ResourceInstance allocateResourceInstance(ResourceType type, Statistics s) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#allocateResourceInstance type={}, s={}", type, s);
}
ResourceInstance resourceInstance = new ResourceInstance(this.resourceInstId, s, type);
this.resourceInstMap.put(s, resourceInstance);
this.resourceInstId++;
return resourceInstance;
}
private List<ResourceInstance> cleanupResources(Statistics[] resources, int ignoreCount) {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#cleanupResources resources.length={}, ignoreCount={}", resources.length,
ignoreCount);
}
int resourcesToDelete = this.resourceInstMap.size() - (resources.length - ignoreCount);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#cleanupResources resourcesToDelete={}", resourcesToDelete);
}
if (resourcesToDelete == 0) {
return Collections.<ResourceInstance>emptyList();
}
// some resource instances need to be removed
List<ResourceInstance> resourcesRemoved = new ArrayList<ResourceInstance>();
List<Statistics> resourceList = Arrays.asList(resources);
Iterator<Map.Entry<Statistics, ResourceInstance>> it =
this.resourceInstMap.entrySet().iterator();
while (it.hasNext() && resourcesToDelete > 0) {
Map.Entry<Statistics, ResourceInstance> e = it.next();
Statistics key = (Statistics) e.getKey();
if (!resourceList.contains(key)) {
key.close();
ResourceInstance inst = (ResourceInstance) e.getValue();
resourcesRemoved.add(inst);
resourcesToDelete--;
it.remove();
}
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#cleanupResources resourcesRemoved={}", resourcesRemoved);
}
return resourcesRemoved;
}
private void notifyAllHandlersOfSample(List<MarkableSampleHandler> handlers,
List<ResourceInstance> updatedResources, long nanosTimeStamp) {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyAllHandlersOfSample timeStamp={}", nanosTimeStamp);
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyAllHandlersOfSample updatedResources.size()={}, handlers={}",
updatedResources.size(), handlers);
}
for (MarkableSampleHandler handler : handlers) {
handler.sampled(nanosTimeStamp, Collections.unmodifiableList(updatedResources));
}
}
private void notifyNewHandlersOfResources(List<MarkableSampleHandler> handlers,
Collection<ResourceType> types, Collection<ResourceInstance> resources) {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyNewHandlersOfResources ri.size()={}", resources.size());
}
int count = 0;
for (MarkableSampleHandler handler : handlers) {
if (!handler.isMarked()) {
List<ResourceType> allocatedResourceTypes = new ArrayList<ResourceType>();
for (ResourceInstance resourceInstance : resources) {
ResourceType resourceType = resourceInstance.getResourceType();
if (!allocatedResourceTypes.contains(resourceType)) {
// allocatedResourceType...
handler.allocatedResourceType(resourceType);
allocatedResourceTypes.add(resourceType);
}
// allocatedResourceInstance...
handler.allocatedResourceInstance(resourceInstance);
}
for (ResourceType resourceType : types) {
if (!allocatedResourceTypes.contains(resourceType)) {
handler.allocatedResourceType(resourceType);
}
}
handler.mark();
count++;
}
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyNewHandlersOfResources notified {} new handlers", count);
}
}
private void notifyOldHandlersOfResource(List<MarkableSampleHandler> handlers,
ResourceInstance resource) {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyOldHandlersOfResource resource={}", resource);
}
int count = 0;
for (MarkableSampleHandler handler : handlers) {
if (handler.isMarked()) {
handler.allocatedResourceInstance(resource);
count++;
}
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyOldHandlersOfResource notified {} old handlers", count);
}
}
private void notifyOldHandlersOfResourceType(List<MarkableSampleHandler> handlers,
ResourceType type) {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyOldHandlersOfResourceType type={}", type);
}
int count = 0;
for (MarkableSampleHandler handler : handlers) {
if (handler.isMarked()) {
handler.allocatedResourceType(type);
count++;
}
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyOldHandlersOfResourceType notified {} old handlers", count);
}
}
private void notifyOldHandlers(List<MarkableSampleHandler> handlers, List<ResourceInstance> ri) {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "SampleCollector#notifyOldHandlers ri={}", ri);
}
int count = 0;
for (ResourceInstance resource : ri) {
for (MarkableSampleHandler handler : handlers) {
if (handler.isMarked()) {
handler.destroyedResourceInstance(resource);
count++;
}
}
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleCollector#notifyOldHandlers notified {} old handlers", count);
}
}
/**
* For testing only
*/
StatMonitorHandler getStatMonitorHandlerSnapshot() {
synchronized (this.sampleHandlers) {
return this.statMonitorHandler;
}
}
/**
* @since GemFire 7.0
*/
public class MarkableSampleHandler implements SampleHandler {
private final SampleHandler sampleHandler;
private boolean mark = false;
public MarkableSampleHandler(SampleHandler sampleHandler) {
if (sampleHandler == null) {
throw new NullPointerException("SampleHandler is null");
}
this.sampleHandler = sampleHandler;
}
public boolean isMarked() {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"MarkableSampleHandler#isMarked returning {} for {}", this.mark, this);
}
return this.mark;
}
public void mark() {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "MarkableSampleHandler#mark marking {}", this);
}
this.mark = true;
}
public SampleHandler getSampleHandler() {
return this.sampleHandler;
}
@Override
public void sampled(long nanosTimeStamp, List<ResourceInstance> resourceInstances) {
this.sampleHandler.sampled(nanosTimeStamp, resourceInstances);
}
@Override
public void allocatedResourceType(ResourceType resourceType) {
this.sampleHandler.allocatedResourceType(resourceType);
}
@Override
public void allocatedResourceInstance(ResourceInstance resourceInstance) {
this.sampleHandler.allocatedResourceInstance(resourceInstance);
}
@Override
public void destroyedResourceInstance(ResourceInstance resourceInstance) {
this.sampleHandler.destroyedResourceInstance(resourceInstance);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.sampleHandler.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MarkableSampleHandler other = (MarkableSampleHandler) obj;
return this.sampleHandler == other.sampleHandler;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(getClass().getName());
sb.append("@").append(System.identityHashCode(this)).append("{");
sb.append("mark=").append(this.mark);
sb.append(", sampleHandler=").append(this.sampleHandler);
return sb.toString();
}
}
/**
* @since GemFire 7.0
*/
public class SampleHandlers implements Iterable<MarkableSampleHandler> {
private volatile List<MarkableSampleHandler> currentHandlers =
Collections.<MarkableSampleHandler>emptyList();
public SampleHandlers() {}
/**
* For test usage only.
*/
public MarkableSampleHandler getMarkableSampleHandler(SampleHandler handler) {
if (contains(handler)) {
for (MarkableSampleHandler markableSamplerHandler : currentHandlers()) {
if (markableSamplerHandler.sampleHandler == handler) {
return markableSamplerHandler;
}
}
}
return null;
}
public boolean contains(SampleHandler handler) {
MarkableSampleHandler markableHandler = new MarkableSampleHandler(handler);
return this.currentHandlers.contains(markableHandler);
}
public List<MarkableSampleHandler> currentHandlers() {
// optimized for read (no copy and unmodifiable)
return this.currentHandlers;
}
@Override
public Iterator<MarkableSampleHandler> iterator() {
// optimized for read (no copy and unmodifiable)
return this.currentHandlers.iterator();
}
public Iterator<MarkableSampleHandler> markedIterator() {
return new MarkableIterator(true);
}
public Iterator<MarkableSampleHandler> unmarkedIterator() {
return new MarkableIterator(false);
}
public boolean addSampleHandler(SampleHandler handler) {
synchronized (this) {
boolean added = false;
MarkableSampleHandler markableHandler = new MarkableSampleHandler(handler);
List<MarkableSampleHandler> oldHandlers = this.currentHandlers;
if (!oldHandlers.contains(markableHandler)) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleHandlers#addSampleHandler adding markableHandler to {}", this);
}
List<MarkableSampleHandler> newHandlers =
new ArrayList<MarkableSampleHandler>(oldHandlers);
added = newHandlers.add(markableHandler);
this.currentHandlers = Collections.unmodifiableList(newHandlers);
}
return added;
}
}
public boolean removeSampleHandler(SampleHandler handler) {
synchronized (this) {
boolean removed = false;
MarkableSampleHandler markableHandler = new MarkableSampleHandler(handler);
List<MarkableSampleHandler> oldHandlers = this.currentHandlers;
if (oldHandlers.contains(markableHandler)) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"SampleHandlers#removeSampleHandler removing markableHandler from {}", this);
}
List<MarkableSampleHandler> newHandlers =
new ArrayList<MarkableSampleHandler>(oldHandlers);
removed = newHandlers.remove(markableHandler);
this.currentHandlers = Collections.unmodifiableList(newHandlers);
}
return removed;
}
}
protected int countMarkableSampleHandlers(SampleHandler handler) {
int count = 0;
List<MarkableSampleHandler> list = currentHandlers();
for (MarkableSampleHandler wrapper : list) {
if (wrapper.getSampleHandler() == handler) {
count++;
}
}
return count;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(getClass().getName());
sb.append("@").append(System.identityHashCode(this)).append("{");
sb.append("currentHandlers=").append(this.currentHandlers);
return sb.toString();
}
private class MarkableIterator implements Iterator<MarkableSampleHandler> {
private final Iterator<MarkableSampleHandler> iterator;
public MarkableIterator(boolean marked) {
List<MarkableSampleHandler> matchingHandlers = new ArrayList<MarkableSampleHandler>();
List<MarkableSampleHandler> handlers = SampleHandlers.this.currentHandlers();
for (MarkableSampleHandler handler : handlers) {
if (handler.isMarked() == marked) {
matchingHandlers.add(handler);
}
}
this.iterator = matchingHandlers.iterator();
}
@Override
public boolean hasNext() {
return this.iterator.hasNext();
}
@Override
public MarkableSampleHandler next() {
return this.iterator.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove operation is not supported");
}
}
}
}