/* * Copyright 2010 NCHOVY * * Licensed 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.krakenapps.rrd.impl; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import org.krakenapps.rrd.DataSourceConfig; import org.krakenapps.rrd.DataSourceType; import org.krakenapps.rrd.io.PersistentLayer; public abstract class DataSource { protected RrdRaw raw; private String name; private DataSourceType type; private long minimalHeartbeat; private double min; private double max; protected double last = Double.NaN; protected double value = Double.NaN; protected long unknownSec = 0; private Map<Archive, ConsolidatedDataPoint> cdps; private Map<Archive, long[]> data; public static DataSource createInstance(RrdRaw rrd, DataSourceConfig dsConfig, long lastUpdate) { switch (dsConfig.getType()) { case COUNTER: return new DataSourceCounter(rrd, dsConfig, lastUpdate); case DERIVE: return new DataSourceDerive(rrd, dsConfig, lastUpdate); case ABSOLUTE: return new DataSourceAbsolute(rrd, dsConfig, lastUpdate); case ABSOLUTE2: return new DataSourceSum(rrd, dsConfig, lastUpdate); default: return new DataSourceGauge(rrd, dsConfig, lastUpdate); } } public static DataSource createFromPersLayer(RrdRaw raw, PersistentLayer persLayer) throws IOException { DataSourceType type = DataSourceType.valueOf(persLayer.readUTF()); String name = persLayer.readUTF(); long minimalHeartbeat = persLayer.readLong(); double min = persLayer.readDouble(); double max = persLayer.readDouble(); double last = persLayer.readDouble(); double value = persLayer.readDouble(); long unknownSec = persLayer.readLong(); DataSourceConfig config = new DataSourceConfig(name, type, minimalHeartbeat, min, max); DataSource newDataSource = DataSource.createInstance(raw, config, raw.getLastUpdateDate().getTime() / 1000); newDataSource.value = value; newDataSource.unknownSec = unknownSec; newDataSource.last = last; newDataSource.cdps = new HashMap<Archive, ConsolidatedDataPoint>(raw.getArchives().size()); for (Archive archive : raw.getArchives()) newDataSource.cdps.put(archive, new ConsolidatedDataPoint(archive, persLayer)); newDataSource.data = new HashMap<Archive, long[]>(raw.getArchives().size()); for (Archive archive : raw.getArchives()) { long[] data = new long[archive.getRowCapacity()]; for (int i = 0; i < data.length; i++) data[i] = persLayer.readLong(); newDataSource.data.put(archive, data); } return newDataSource; } public DataSource(RrdRaw raw, DataSourceConfig dsConfig, long lastUpdate) { this.raw = raw; this.name = dsConfig.getName(); this.type = dsConfig.getType(); this.minimalHeartbeat = dsConfig.getMinimalHeartbeat(); this.min = dsConfig.getMin(); this.max = dsConfig.getMax(); this.cdps = new HashMap<Archive, ConsolidatedDataPoint>(raw.getArchives().size()); for (Archive archive : raw.getArchives()) cdps.put(archive, new ConsolidatedDataPoint(archive)); this.data = new HashMap<Archive, long[]>(raw.getArchives().size()); for (Archive archive : raw.getArchives()) data.put(archive, new long[archive.getRowCapacity()]); } public String getName() { return name; } public DataSourceType getType() { return type; } public long getMinimalHeartbeat() { return minimalHeartbeat; } public double getMin() { return min; } public double getMax() { return max; } public double getLastValue() { return last; } public double getCurrentValue() { return value; } public ConsolidatedDataPoint getCdp(Archive archive) { return cdps.get(archive); } public double getData(Archive archive, int index) { return Double.longBitsToDouble(data.get(archive)[index]); } public void setData(Archive archive, int index, double value) { data.get(archive)[index] = Double.doubleToRawLongBits(value); } protected abstract double getValueDelta(long time, long lastUpdate, double newValue); protected abstract void onNextCheckpointPassed(long time, long lastUpdate, long nextCheckpoint, double newValue); protected double linearInterpolation(double target, double x1, double x2, double v1, double v2) { return v1 + (v2 - v1) / (x2 - x1) * (target - x1); } public void update(long currentTime, long lastUpdate, double newValue) { long step = raw.getStep(); long expectedLastCheckpoint = ((lastUpdate / step) + 1) * step; long previousCheckpoint = ((currentTime / step)) * step; if (Double.isNaN(this.last) && Double.isNaN(this.value)) this.unknownSec = currentTime - previousCheckpoint; if (currentTime != lastUpdate) { if (expectedLastCheckpoint <= currentTime) { onNextCheckpointPassed(currentTime, lastUpdate, previousCheckpoint, newValue); this.unknownSec = 0; } else { double valueDelta = getValueDelta(currentTime, lastUpdate, newValue); this.value = Double.isNaN(this.value) ? valueDelta : this.value + valueDelta; } } else { if (currentTime % step == 0) onNextCheckpointPassed(currentTime, lastUpdate, previousCheckpoint, newValue); else updateLast(newValue); } this.last = newValue; } public int length() { try { int len = type.toString().getBytes("utf-8").length + 2; len += name.getBytes("utf-8").length + 2; len += 48; len += cdps.size() * 40; len += raw.getRowCapacity(); return len; } catch (UnsupportedEncodingException e) { return 0; } } public void writeToPersLayer(PersistentLayer persLayer) throws IOException { persLayer.writeUTF(type.toString()); persLayer.writeUTF(name); persLayer.writeLong(minimalHeartbeat); persLayer.writeDouble(min); persLayer.writeDouble(max); persLayer.writeDouble(last); persLayer.writeDouble(value); persLayer.writeLong(unknownSec); for (Archive key : raw.getArchives()) cdps.get(key).writeToPersLayer(persLayer); for (Archive archive : raw.getArchives()) { for (long d : data.get(archive)) persLayer.writeLong(d); } } public void dump(PrintWriter writer) { writer.printf("== %s ==\ntype: %s\n", name, type.toString()); writer.printf("minimalHeartbaet: %d\n", minimalHeartbeat); writer.printf("min: %f, max: %f\n", min, max); writer.printf("last: %f, value: %f, unknownSec: %s\n", last, value, unknownSec); writer.printf("=== cdp ===\n"); int cdpIndex = 0; for (Archive key : raw.getArchives()) { writer.printf("%d: \n", cdpIndex++); cdps.get(key).dump(writer); } } public abstract void updateLast(Double value); @Override public boolean equals(Object obj) { DataSource rhs = (DataSource) obj; if (!this.name.equals(rhs.name)) return false; if (this.type != rhs.type) return false; if (this.minimalHeartbeat != rhs.minimalHeartbeat) return false; if (!RrdUtil.doubleEqual(this.min, rhs.min) || !RrdUtil.doubleEqual(this.max, rhs.max)) return false; if (!RrdUtil.doubleEqual(this.last, rhs.last)) return false; if (!RrdUtil.doubleEqual(this.value, rhs.value)) return false; if (this.unknownSec != rhs.unknownSec) return false; return true; } @Override public String toString() { return getName(); } } class DataSourceGauge extends DataSource { public DataSourceGauge(RrdRaw rrd, DataSourceConfig dsConfig, long lastUpdate) { super(rrd, dsConfig, lastUpdate); } @Override protected double getValueDelta(long time, long lastUpdate, double newValue) { return newValue * (time - lastUpdate); } @Override protected void onNextCheckpointPassed(long time, long lastUpdate, long nextCheckpoint, double newValue) { double valueDelta = getValueDelta(time, lastUpdate, newValue); double nextValue = Double.isNaN(this.value) ? valueDelta : this.value + valueDelta; double interpolated = linearInterpolation(nextCheckpoint, lastUpdate, time, this.value, nextValue); double checkpointValue = interpolated / raw.getStep(); raw.updateArchives(time, lastUpdate, this, checkpointValue, newValue); this.value = Double.isNaN(interpolated) ? 0 : (nextValue - interpolated); } @Override public void updateLast(Double value) { } } class DataSourceCounter extends DataSource { public DataSourceCounter(RrdRaw rrd, DataSourceConfig dsConfig, long lastUpdate) { super(rrd, dsConfig, lastUpdate); } @Override protected void onNextCheckpointPassed(long time, long lastUpdate, long checkpoint, double newValue) { double valueDelta = getValueDelta(time, lastUpdate, newValue); if (Double.isNaN(this.value)) this.value = 0; double nextValue = this.value + valueDelta; double interpolated = linearInterpolation(checkpoint, lastUpdate, time, this.value, nextValue); double checkpointValue = interpolated / (raw.getStep() - unknownSec); raw.updateArchives(time, lastUpdate, this, checkpointValue, newValue); this.value = nextValue - (Double.isNaN(interpolated) ? 0 : interpolated); } @Override protected double getValueDelta(long time, long lastUpdate, double newValue) { return newValue - this.last; } @Override public void updateLast(Double value) { } } class DataSourceDerive extends DataSource { public DataSourceDerive(RrdRaw rrd, DataSourceConfig dsConfig, long lastUpdate) { super(rrd, dsConfig, lastUpdate); } @Override protected void onNextCheckpointPassed(long time, long lastUpdate, long checkpoint, double newValue) { double valueDelta = getValueDelta(time, lastUpdate, newValue); if (Double.isNaN(this.value)) this.value = 0; double nextValue = Double.isNaN(this.value) ? valueDelta : this.value + valueDelta; double interpolated = linearInterpolation(checkpoint, lastUpdate, time, this.value, nextValue); double checkpointValue = interpolated / (raw.getStep() - unknownSec); raw.updateArchives(time, lastUpdate, this, checkpointValue, newValue); this.value = nextValue - (Double.isNaN(interpolated) ? 0 : interpolated); } @Override protected double getValueDelta(long time, long lastUpdate, double newValue) { return newValue - this.last; } @Override public void updateLast(Double value) { } } class DataSourceAbsolute extends DataSource { public DataSourceAbsolute(RrdRaw rrd, DataSourceConfig dsConfig, long lastUpdate) { super(rrd, dsConfig, lastUpdate); } @Override protected void onNextCheckpointPassed(long time, long lastUpdate, long checkpoint, double newValue) { double valueDelta = getValueDelta(time, lastUpdate, newValue); if (Double.isNaN(this.value)) this.value = 0; double nextValue = this.value + valueDelta; double interpolated = linearInterpolation(checkpoint, lastUpdate, time, this.value, nextValue); double checkpointValue = interpolated / raw.getStep(); raw.updateArchives(time, lastUpdate, this, checkpointValue, newValue); this.value = nextValue - (Double.isNaN(interpolated) ? 0 : interpolated); } @Override protected double getValueDelta(long time, long lastUpdate, double newValue) { return newValue; } @Override public void updateLast(Double value) { } } class DataSourceSum extends DataSource { public DataSourceSum(RrdRaw rrd, DataSourceConfig dsConfig, long lastUpdate) { super(rrd, dsConfig, lastUpdate); } @Override protected void onNextCheckpointPassed(long time, long lastUpdate, long checkpoint, double newValue) { if (Double.isNaN(newValue)) newValue = 0; double valueDelta = getValueDelta(time, lastUpdate, newValue); if (Double.isNaN(this.value)) this.value = 0; raw.updateArchives(time, lastUpdate, this, (time == checkpoint) ? (value + valueDelta) : value, newValue); this.value = time == checkpoint ? 0 : valueDelta; } @Override protected double getValueDelta(long time, long lastUpdate, double newValue) { return newValue; } @Override public void updateLast(Double value) { this.value += value; } }