/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.monitoring.system.internal;
import java.util.Objects;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.monitoring.system.api.OperatingSystemException;
import de.rcenvironment.core.monitoring.system.api.SystemMonitoringConstants;
import de.rcenvironment.core.monitoring.system.api.SystemMonitoringDataService;
import de.rcenvironment.core.monitoring.system.api.model.SystemLoadInformation;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
import de.rcenvironment.toolkit.utils.common.TimeSource;
/**
* Continuously collects low-detail system information (total CPU load, total RAM usage) and provides this data in the required forms. Most
* notably, this includes providing a sliding average of the recent CPU load.
*
* @author Robert Mischke
*/
public class SystemLoadInformationCollector implements Runnable {
/**
* Internal copy of the {@link SystemMonitoringDataService} to avoid race conditions.
*/
private final SystemMonitoringDataService dataSource;
private final TimeSource timeSource;
private final int minimumTimeDelta;
private final int maximumTimeDelta;
private final RingBufferOfDouble cpuLoadRingBuffer;
private double latestCpuLoad = SystemMonitoringConstants.CPU_LOAD_UNKNOWN_DEFAULT;
private long latestAvailableRam = SystemMonitoringConstants.RAM_UNKNOWN_DEFAULT;
private int failureCount = 0;
private long lastUpdateTime = Integer.MIN_VALUE; // neither zero nor Long.MIN_VALUE to prevent test artifacts
private int ignoredUpdateTriggers;
private final Log log = LogFactory.getLog(getClass());
/**
* @param minimumTimeDelta the minimum time in msec that should be accepted between two subsequent update events; if update events
* arrive faster than that, the later ones should be ignored
* @param maximumTimeDelta the maximum time in msec that is allowed to pass between two subsequent update events before this collector
* considers the data sequence "broken" and starts over, ie discards all previous data
*/
public SystemLoadInformationCollector(SystemMonitoringDataService systemDataService, int bufferCapacity, TimeSource timeSource,
int minimumTimeDelta, int maximumTimeDelta) {
Objects.requireNonNull(systemDataService);
Objects.requireNonNull(timeSource);
this.dataSource = systemDataService;
this.cpuLoadRingBuffer = new RingBufferOfDouble(bufferCapacity);
this.timeSource = timeSource;
this.minimumTimeDelta = minimumTimeDelta;
this.maximumTimeDelta = maximumTimeDelta;
}
@Override
@TaskDescription("Collect system load information")
public synchronized void run() {
try {
// update interval checks
long time = timeSource.getCurrentTimeMillis();
if (time < lastUpdateTime + minimumTimeDelta) {
ignoredUpdateTriggers++;
return;
}
lastUpdateTime = time;
// if update triggers have been ignored, log them together as a single message when resuming
if (ignoredUpdateTriggers > 0) {
log.debug(StringUtils.format(
"Ignored %s delayed system monitoring update trigger(s) that arrived faster than "
+ "the configured minimum time of %d msec; this may be caused by the host system "
+ "resuming after being suspended, or a very high number of concurrent tasks",
ignoredUpdateTriggers, minimumTimeDelta));
ignoredUpdateTriggers = 0;
}
// perform the actual update
latestCpuLoad = dataSource.getTotalCPUUsage();
latestAvailableRam = dataSource.getFreeRAM();
cpuLoadRingBuffer.add(latestCpuLoad);
failureCount = 0;
} catch (OperatingSystemException e) {
failureCount++;
log.error("Error retrieving system load information (sequential failure count: " + failureCount + ")", e);
}
}
/**
* Fetches a snapshot of the aggregated system load information.
*
* @param maxSamples the maximum number of samples to include in average values
* @return an immutable holder of the generated data
*/
public synchronized SystemLoadInformation getSystemLoadInformation(int maxSamples) {
return new SystemLoadInformation(cpuLoadRingBuffer.getAverageOfLatest(maxSamples), latestCpuLoad, latestAvailableRam);
}
}